Re: Receive UDP broadcast packets with OTRcvUData
Re: Receive UDP broadcast packets with OTRcvUData
- Subject: Re: Receive UDP broadcast packets with OTRcvUData
- From: Quinn <email@hidden>
- Date: Wed, 3 Nov 2004 23:09:04 +0000
Title: Re: Receive UDP broadcast packets with
OTRcvUData
At 14:50 +0100 3/11/04, Matthieu Beghin wrote:
I create a Synchrone
socket and set it non blockling. It receives broadcast on
2.255.255.255.
Sometimes I have to
answer by broadcast and I get the error
kEACCESErr -3212
Permission denied
when calling
OTSndUData.
I just tried sending UDP broadcasts under OT on Mac OS X, and it
worked as easily as I remember. I'm not sure what's going on
with your code, but you might want to compare it with mine. I've
included my test program at the end of this email. Try it out on
your machine and see what you get.
You should just be able to paste it into an Xcode CoreServices
command line tool project.
S+E
--
Quinn "The
Eskimo!"
<http://www.apple.com/developer/>
Apple Developer Technical Support * Networking, Communications,
Hardware
----------------------------------------------------------------------------
/*
File:
UDPBroadcastTester.c
Contains: Framework for testing UDP
broadcast code.
Written by: Quinn "The Eskimo!"
Copyright: © 1998 by Apple Computer, Inc.,
all rights reserved.
Change History (most recent first):
You may incorporate this sample code into your
applications without
restriction, though the sample code has been
provided "AS IS" and the
responsibility for its operation is 100% yours.
However, what you are
not permitted to do is to redistribute the source
as "DSC Sample Code"
after having made changes. If you're going to
re-distribute the source,
we require that you make it clear in the source
that the code was
descended from Apple Sample Code, but that you've
made changes.
*/
/////////////////////////////////////////////////////////////////////
// Pick up all the standard OT stuff.
#include <OpenTransport.h>
/////////////////////////////////////////////////////////////////////
// Pick up all the OT TCP/IP stuff.
#include <OpenTptInternet.h>
/////////////////////////////////////////////////////////////////////
// Pick up OT low-level stuff.
#include <stropts.h>
/////////////////////////////////////////////////////////////////////
// Pick up the OTDebugBreak and OTAssert macros.
#include <OTDebug.h>
/////////////////////////////////////////////////////////////////////
// Pick up various Mac OS stuff.
#include <Threads.h>
#include <Traps.h>
#include <MixedMode.h>
#include <Gestalt.h>
#include <Events.h>
/////////////////////////////////////////////////////////////////////
// Standard C stuff
#include <stdio.h>
/////////////////////////////////////////////////////////////////////
// The following equates are actually exported by <miioccom.h>,
but
// they commented out for some reason )-:
// #include <miioccom.h>
#define MIOC_SIOC
'j' /* sockio.h socket ioctl's */
/////////////////////////////////////////////////////////////////////
enum {
kTestPort = 12345
};
static Boolean gQuitNow = false;
static Boolean gEchoThreadBound = false;
/////////////////////////////////////////////////////////////////////
static OSStatus MyYieldToAnyThread(void)
{
OSStatus err;
OSStatus junk;
err = noErr;
junk = YieldToAnyThread();
OTAssert("MyYieldToAnyThread: YieldToAnyThread
failed", junk == noErr);
if ( gQuitNow ) {
err = userCanceledErr;
}
return err;
}
static pascal void YieldingNotifier(EndpointRef ep, OTEventCode
code,
OTResult result, void*
cookie)
// This simple notifier checks for kOTSyncIdleEvent
and
// when it gets one calls the Thread Manager
routine
// YieldToAnyThread. Open Transport sends
kOTSyncIdleEvent
// whenever it's waiting for something, eg data to
arrive
// inside a sync/blocking OTRcv call. In such
cases, we
// yield the processor to some other thread that
might
// be doing useful work.
{
#pragma unused(result)
#pragma unused(cookie)
OSStatus junk;
switch (code) {
case
kOTSyncIdleEvent:
junk = YieldToAnyThread();
OTAssert("YieldingNotifier: YieldToAnyThread failed", junk
== noErr);
if
( gQuitNow ) {
junk = OTCancelSynchronousCalls(ep,
userCanceledErr);
OTAssert("YieldingNotifier:
OTCancelSynchronousCalls failed", junk == noErr);
}
break;
default:
//
do nothing
break;
}
}
/////////////////////////////////////////////////////////////////////
static void SetupEndpointForThreads(EndpointRef ep)
{
OSStatus junk;
// Establish the modes of operation. This
sample uses
// sync/blocking mode, with sync idle events that
yield
// time using the Thread Manager.
junk = OTSetSynchronous(ep);
OTAssert("SetupEndpointForThreads:
OTSetSynchronous failed", junk == noErr);
junk = OTSetBlocking(ep);
OTAssert("SetupEndpointForThreads:
OTSetBlocking failed", junk == noErr);
junk = OTInstallNotifier(ep, YieldingNotifier,
ep);
OTAssert("SetupEndpointForThreads:
OTInstallNotifier failed", junk == noErr);
junk = OTUseSyncIdleEvents(ep, true);
OTAssert("SetupEndpointForThreads:
OTUseSyncIdleEvents failed", junk == noErr);
}
/////////////////////////////////////////////////////////////////////
static OSStatus BindEndpoint(EndpointRef ep, InetHost host, InetPort
port)
{
OSStatus err;
TBind bindRequest;
InetAddress requestAddr;
TBind bindResponse;
InetAddress responseAddr;
// Set up to bind the endpoint. Because we're
listening for broadcasts,
// we have to request kOTAnyInetAddress.
OTInitInetAddress(&requestAddr, port,
host);
OTMemzero(&bindRequest, sizeof(TBind));
bindRequest.addr.buf = (UInt8 *)
&requestAddr;
bindRequest.addr.len = sizeof(InetAddress);
// Because the port might be in use, we have to
check that XTI
// has not given us another port (XTI is kinda lame
in this respect).
// So we set up bindResponse to collect the bound
address.
OTMemzero(&bindResponse, sizeof(TBind));
bindResponse.addr.buf = (UInt8 *)
&responseAddr;
bindResponse.addr.maxlen = sizeof(InetAddress);
// Do the bind and check that we got what we
wanted.
err = OTBind(ep, &bindRequest,
&bindResponse);
if (err == noErr) {
if ( requestAddr.fPort !=
responseAddr.fPort ) {
err
= kEADDRINUSEErr;
}
}
return err;
}
/////////////////////////////////////////////////////////////////////
static OTResult SetFourByteOption(EndpointRef ep,
OTXTILevel level,
OTXTIName name,
UInt32 value)
// level and name must denote a four byte option
that is
// appropriate for the endpoint ep. This
routine sets the
// option to value. ep is assumed to be in
synchronous
// mode.
//
// If all goes well, the result is noErr. If
an error
// occurs, the result is negative. If the
option could not
// be negotiated, a positive result being one of
(T_FAILURE,
// T_PARTSUCCESS, T_READONLY, T_NOTSUPPORT) is
returned
{
OTResult err;
TOption option;
TOptMgmt request;
TOptMgmt result;
// Set up the option buffer to reflect the specific
option
// and value we want to set. We use a TOption
structure
// to represent the option buffer. TOption is
specifically
// defined to allow easy construction of 4 byte
options.
// If you want to negotiate different size options,
or
// multiple options in a single call, then
constructing
// the option buffer is a little trickier
option.len =
kOTFourByteOptionSize;
option.level = level;
option.name = name;
option.status = 0;
option.value[0] = value;
// Set up the request for OTOptionManagement to
point
// to the option buffer we just filled out, and
tell
// it that we want to negotiate (ie set) the
option.
request.opt.buf = (UInt8 *) &option;
request.opt.len = sizeof(option);
request.flags = T_NEGOTIATE;
// Set up the reply for OTOptionManagement.
This is where
// OTOptionManagement puts the result of the
negotiation.
result.opt.buf = (UInt8 *) &option;
result.opt.maxlen = sizeof(option);
// Call OTOptionManagement and then check that the
value
// was negotiated successfully. Any value
other than
// T_SUCCESS is reported via the error result.
err = OTOptionManagement(ep, &request,
&result);
if (err == noErr) {
if (option.status !=
T_SUCCESS) {
err
= option.status;
}
}
return err;
}
/////////////////////////////////////////////////////////////////////
static OSStatus GetSourceAndDestHosts(InetHost *sourceHost, InetHost
*destHost)
{
OSStatus err;
InetInterfaceInfo info;
err = OTInetGetInterfaceInfo(&info,
kDefaultInetInterface);
if (err == noErr) {
*sourceHost =
info.fAddress;
*destHost =
info.fBroadcastAddr;
if ( *destHost ==
kOTAnyInetAddress ) {
//
*destHost = (0xFFFFFFFF & ~info.fNetmask) | (info.fAddress &
info.fNetmask);
*destHost = 0xFFFFFFFF;
}
}
return err;
}
/////////////////////////////////////////////////////////////////////
static pascal OSStatus EchoThread(void *junkParam)
{
#pragma unused(junkParam)
OSStatus err;
OSStatus junk;
OTResult result;
EndpointRef ep;
TEndpointInfo epInfo;
UInt8 *dataBuffer;
UInt8 *addrBuffer;
TUnitData rcvRequest;
OTFlags junkFlags;
InetAddress *sourceAddr;
dataBuffer = nil;
addrBuffer = nil;
ep =
OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0, nil, &err);
// If the endpoint opens successfully...
if (err == noErr) {
SetupEndpointForThreads(ep);
junk =
OTGetEndpointInfo(ep, &epInfo);
OTAssert("EchoThread:
OTGetEndpointInfo failed", junk == noErr);
dataBuffer =
OTAllocMem(epInfo.tsdu);
if (dataBuffer == nil)
{
err
= kENOMEMErr;
}
addrBuffer =
OTAllocMem(epInfo.addr);
if (dataBuffer == nil)
{
err
= kENOMEMErr;
}
}
// Now bind the endpoint.
if (err == noErr) {
result =
SetFourByteOption(ep, INET_IP, IP_REUSEADDR, 1);
if (result > 0) {
err
= kENXIOErr;
} else {
err
= result;
}
}
if (err == noErr) {
err = BindEndpoint(ep,
kOTAnyInetAddress, kTestPort);
}
if (err == noErr) {
// Tell the sending thread
that we're bound and waiting for
// packets to echo.
We do this because we want to make sure
// that we bind before it
does.
gEchoThreadBound =
true;
// Repeatedly get data from
the endpoint and echo it to
// stdout.
do {
OTMemzero(&rcvRequest, sizeof(TUnitData));
rcvRequest.addr.buf = addrBuffer;
rcvRequest.addr.maxlen = epInfo.addr;
rcvRequest.udata.buf = dataBuffer;
rcvRequest.udata.maxlen = epInfo.tsdu;
err
= OTRcvUData(ep, &rcvRequest, &junkFlags);
if
(err == noErr) {
sourceAddr = (InetAddress *)
addrBuffer;
printf("EchoThread: Got a
packet!\n");
printf(" size =
%ld.\n", rcvRequest.addr.len);
if ( sourceAddr->fAddressType ==
AF_INET ) {
printf(" source addr = %d.%d.%d.%d, source port =
%d\n",
(int)
((sourceAddr->fHost >> 24) & 0x00FF),
(int)
((sourceAddr->fHost >> 16) & 0x00FF),
(int)
((sourceAddr->fHost >> 8) & 0x00FF),
(int)
((sourceAddr->fHost >> 0) & 0x00FF),
sourceAddr->fPort);
} else {
printf(" unknown (fAddressType = %d)\n", ((InetAddress
*) addrBuffer)->fAddressType);
}
} else if (err == kOTLookErr) {
OTResult look;
look = OTLook(ep);
switch ( look ) {
case
T_UDERR:
err = OTRcvUDErr(ep, nil);
break;
default:
printf("EchoThread: Got a kOTLookErr, look = %ld\n",
look);
// leave err set to kOTLookErr
break;
}
}
//
fflush(stdout);
} while (err == noErr);
}
// Clean up.
if (addrBuffer != nil) {
OTFreeMem(dataBuffer);
}
if (dataBuffer != nil) {
OTFreeMem(addrBuffer);
}
if (ep != kOTInvalidEndpointRef) {
junk =
OTCloseProvider(ep);
OTAssert("EchoThread:
OTCloseProvider failed", junk == noErr);
}
if (err == noErr || err == userCanceledErr) {
printf("EchoThread:
Successful termination.\n");
} else {
printf("EchoThread:
Terminated with error %d.\n", err);
}
return noErr;
}
/////////////////////////////////////////////////////////////////////
#define HackyTest 0
#if !HackyTest
struct sockaddr {
unsigned short
sa_family; /* address family */
char
sa_data[14]; /* up to 14 bytes of direct address */
};
struct ifreq {
#define IFNAMSIZ
36 /* Must be same as kMaxProviderNameSize %%%
*/
char
ifr_name[IFNAMSIZ]; /* if name, e.g.
"en0" */
union {
struct sockaddr
ifru_addr;
struct sockaddr
ifru_dstaddr;
struct sockaddr
ifru_broadaddr;
short
ifru_flags;
int ifru_metric;
char
*ifru_data;
} ifr_ifru;
#define ifr_addr ifr_ifru.ifru_addr /* address
*/
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of
p-to-p link */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast
address */
#define ifr_flags ifr_ifru.ifru_flags /* flags */
#define ifr_metric ifr_ifru.ifru_metric /*
metric */
#define ifr_data ifr_ifru.ifru_data /* for use
by interface */
};
#define SIOCGIFCONF MIOC_CMD(MIOC_SIOC,20)
static struct ifreq gInterfaceArray[10];
static OSStatus FindInterfaceNameByHost(EndpointRef ep, InetHost host,
char *interfaceName)
{
OSStatus err;
struct strioctl interfaceIoctl;
SInt32 interfaceIndex;
OTMemzero(&gInterfaceArray[0],
sizeof(gInterfaceArray));
interfaceIoctl.ic_cmd = SIOCGIFCONF;
interfaceIoctl.ic_timout = 0;
interfaceIoctl.ic_len =
sizeof(gInterfaceArray);
interfaceIoctl.ic_dp = (char *)
&gInterfaceArray[0];
err = OTIoctl(ep, I_STR, &interfaceIoctl);
if (err == noErr) {
interfaceName[0] = 0;
for (interfaceIndex = 0;
interfaceIndex < 10; interfaceIndex++) {
if
( ((InetAddress *)
&(gInterfaceArray[interfaceIndex].ifr_addr))->fHost == host )
{
OTStrCopy(interfaceName,
gInterfaceArray[interfaceIndex].ifr_name);
}
}
if ( interfaceName[0] == 0
) {
err
= kENXIOErr;
}
}
return err;
}
#endif
#define OldWay 0
#if OldWay
static OSStatus NegotiateBroadcastInterface(EndpointRef ep, InetHost
host)
{
OTResult err;
TOptMgmt request;
TOptMgmt result;
struct {
TOptionHeader
optHeader;
char
interfaceName[kMaxProviderNameSize];
} option;
#if HackyTest
#pragma unused(host)
err = noErr;
OTStrCopy(option.interfaceName, "pci1011,140");
#else
err =
FindInterfaceNameByHost(ep, host, option.interfaceName);
#endif
if (err == noErr) {
option.optHeader.len =
sizeof(option); // kOTOptionHeaderSize +
OTStrLength(option.interfaceName) + 1;
option.optHeader.level
= INET_IP;
option.optHeader.name =
IP_BROADCAST_IF;
option.optHeader.status =
0;
// Set up the request for
OTOptionManagement to point
// to the option buffer we
just filled out, and tell
// it that we want to
negotiate (ie set) the option.
request.opt.buf = (UInt8 *)
&option;
request.opt.len =
option.optHeader.len;
request.flags =
T_NEGOTIATE;
// Set up the reply for
OTOptionManagement. This is where
// OTOptionManagement puts
the result of the negotiation.
result.opt.buf = (UInt8 *)
&option;
result.opt.maxlen =
sizeof(option);
// Call OTOptionManagement
and then check that the value
// was negotiated
successfully. Any value other than
// T_SUCCESS is reported
via the error result.
err =
OTOptionManagement(ep, &request, &result);
if (err == noErr) {
if
(option.optHeader.status != T_SUCCESS) {
err = option.optHeader.status;
}
}
}
return err;
}
#else
#define IP_BROADCAST_IFADDR 0x1018 /*
Set interface addr for broadcasts */
static OSStatus NegotiateBroadcastInterface(EndpointRef ep, InetHost
host)
{
OSStatus err;
// host = 0x11cb15f8; //
17.203.21.248 *** temp hack to test that IP_BROADCAST_IFADDR
works
err = SetFourByteOption(ep, INET_IP,
IP_BROADCAST_IFADDR, host);
return err;
}
#endif
/////////////////////////////////////////////////////////////////////
static OSStatus PrintSecondaryAddresses(InetInterfaceInfo*
interfaceInfo, SInt32 interfaceIndex)
{
OSStatus err;
InetHost *secondaryAddressBuffer;
UInt32 numberOfSecondaryAddresses;
UInt32 addressIndex;
secondaryAddressBuffer = nil;
numberOfSecondaryAddresses =
interfaceInfo->fIPSecondaryCount;
if ( numberOfSecondaryAddresses > 0 ) {
// Allocate a buffer for
the secondary address info.
err = noErr;
secondaryAddressBuffer =
(InetHost *) OTAllocMem( numberOfSecondaryAddresses * sizeof(InetHost)
);
if (secondaryAddressBuffer
== nil) {
err
= kENOMEMErr;
}
// Ask OT for the list of
secondary addresses on this interface.
if (err == noErr) {
err
= OTInetGetSecondaryAddresses(secondaryAddressBuffer,
&numberOfSecondaryAddresses, interfaceIndex);
}
// Start a server for each
secondary address.
for (addressIndex = 0;
addressIndex < numberOfSecondaryAddresses; addressIndex++) {
printf("secondaryAddressBuffer[%ld] = lx\n",
addressIndex, secondaryAddressBuffer[addressIndex]);
}
}
// Clean up.
if (secondaryAddressBuffer != nil) {
OTFreeMem(secondaryAddressBuffer);
}
return err;
}
enum
{
kOTIPSingleLinkMultihomingVersion = 0x01300000
};
static OSStatus PrintIPAddresses(void)
{
OSStatus err;
Boolean haveIPSingleLinkMultihoming;
NumVersionVariant otVersion;
SInt32 interfaceIndex;
InetInterfaceInfo info;
Boolean done;
haveIPSingleLinkMultihoming =
(
Gestalt(gestaltOpenTptVersions, (long *) &otVersion) == noErr
&& (otVersion.whole >=
kOTIPSingleLinkMultihomingVersion )
&& (
OTInetGetSecondaryAddresses != (void *)
kUnresolvedCFragSymbolAddress));
err = noErr;
done = false;
interfaceIndex = 0;
do {
done = (
OTInetGetInterfaceInfo(&info, interfaceIndex) != noErr );
if ( ! done ) {
printf("fAddress = lx\n", info.fAddress);
if
( haveIPSingleLinkMultihoming ) {
err =
PrintSecondaryAddresses(&info, interfaceIndex);
}
interfaceIndex += 1;
}
} while (err == noErr && !done);
return err;
}
static void PrintIPAddressesTest(void)
{
OSStatus err;
err = PrintIPAddresses();
if (err == noErr) {
printf("Success!\n");
} else {
printf("Failed with
error %ld.\n", err);
}
}
/////////////////////////////////////////////////////////////////////
static UInt32 gPacketsToSend = 0;
static UInt32 gCurrentPacketNumber = 0;
static pascal OSStatus SendThread(void *junkParam)
{
#pragma unused(junkParam)
OSStatus err;
OSStatus junk;
EndpointRef ep;
OTResult result;
InetHost sourceHost;
InetHost destHost;
TUnitData sndRequest;
InetAddress destAddr;
OTResult look;
char dataString[256];
ep = kOTInvalidEndpointRef;
// Start by waiting for the echo thread to start up
successfully.
err = noErr;
while ( err == noErr && ! gEchoThreadBound
) {
err =
MyYieldToAnyThread();
}
// Create the endpoint, set it up for threaded
execution, set
// up the IP_REUSEADDR option, and then bind it to
one of the
// interfaces.
if (err == noErr) {
ep =
OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0, nil, &err);
}
if (err == noErr) {
SetupEndpointForThreads(ep);
result =
SetFourByteOption(ep, INET_IP, IP_REUSEADDR, 1);
if (result > 0) {
err
= kENXIOErr;
} else {
err
= result;
}
}
if (err == noErr) {
err =
GetSourceAndDestHosts(&sourceHost, &destHost);
}
if (err == noErr) {
err = BindEndpoint(ep,
sourceHost, kTestPort);
}
if (err == noErr) {
err =
NegotiateBroadcastInterface(ep, sourceHost);
}
if (err == noErr) {
do {
if
(gPacketsToSend == 0) {
err = MyYieldToAnyThread();
}
else {
OTInitInetAddress(&destAddr,
kTestPort, destHost);
gCurrentPacketNumber += 1;
sprintf(dataString, "Broadcast
Message %ld", gCurrentPacketNumber);
OTMemzero(&sndRequest,
sizeof(TUnitData));
sndRequest.addr.buf = (UInt8 *)
&destAddr;
sndRequest.addr.len =
sizeof(InetAddress);
sndRequest.udata.buf = (UInt8 *)
dataString;
sndRequest.udata.len =
OTStrLength(dataString);
err = OTSndUData(ep,
&sndRequest);
if (err == kOTLookErr) {
look =
OTLook(ep);
switch ( look
) {
case T_UDERR:
err = OTRcvUDErr(ep, nil);
break;
case T_DATA:
printf("SendThread: Got a
T_DATA\n");
// leave err set to
kOTLookErr
break;
default:
printf("SendThread: Got a
kOTLookErr, look = %ld\n", look);
// leave err set to
kOTLookErr
break;
}
}
// fflush(stdout);
gPacketsToSend -= 1;
}
} while (err == noErr);
}
// Clean up.
if (ep != kOTInvalidEndpointRef) {
junk =
OTCloseProvider(ep);
OTAssert("SendThread:
OTCloseProvider failed", junk == noErr);
}
if (err == noErr || err == userCanceledErr) {
printf("SendThread:
Successful termination.\n");
} else {
printf("SendThread:
Terminated with error %d.\n", err);
}
return noErr;
}
/////////////////////////////////////////////////////////////////////
// Experimental hack to get SIOUX to execute threads while
// it's waiting for user input. It seems to cause SIOUX
// to suffer horribly re-entrancy problems, so it's disabled
// at the moment.
#define WildlyDangerousHack 0
#if WildlyDangerousHack
typedef pascal void (*TEIdleProc)(TEHandle
hTE);
enum {
uppTEIdleProcInfo =
kPascalStackBased
| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(TEHandle)))
};
static UniversalProcPtr gOldTEIdle;
static UniversalProcPtr gMyTEIdle;
static pascal void MyTEIdle(TEHandle hTE)
{
long oldA5;
OSStatus junk;
oldA5 = SetCurrentA5();
junk =
YieldToAnyThread();
OTAssert("MyTEIdle:
YieldToAnyThread failed", junk == noErr);
(void) SetA5(oldA5);
#if GENERATINGCFM
CallUniversalProc(gOldTEIdle, uppTEIdleProcInfo, hTE);
#else
((TEIdleProc) gOldTEIdle)(hTE);
#endif
}
#endif
/////////////////////////////////////////////////////////////////////
static void PrintHelp(void)
{
printf("?) Help\n");
printf("s) Send a packet\n");
printf("S) Send 10 packets\n");
printf("p) Print IP addresses\n");
printf("r) Run threads for a
while\n");
printf("q) Quit\n");
}
void main(void)
{
OSStatus err;
ThreadID echoThread;
ThreadID sendThread;
ThreadState junkState;
char command[256];
UInt32 startTime;
printf("Hello Cruel World!\n");
printf("QStandard.c\n");
err = InitOpenTransport();
if (err == noErr) {
err =
NewThread(kCooperativeThread,
(ThreadEntryProcPtr)
EchoThread, nil,
0, kCreateIfNeeded,
nil,
&echoThread);
if (err == noErr) {
err
= NewThread(kCooperativeThread,
(ThreadEntryProcPtr) SendThread, nil,
0,
kCreateIfNeeded,
nil,
&sendThread);
}
if (err == noErr) {
#if
WildlyDangerousHack
// Oh this is ugly. Patch
TEIdle to call YieldToAnyThread,
// so that our threads run while the
SIOUX window is waiting
// for input.
gMyTEIdle = NewRoutineDescriptor(
(ProcPtr) MyTEIdle, uppTEIdleProcInfo, GetCurrentArchitecture());
OTAssert("main: Could not create
routine descriptor for patch", gMyTEIdle != nil);
gOldTEIdle =
GetToolboxTrapAddress(_TEIdle);
SetToolboxTrapAddress(gMyTEIdle,
_TEIdle);
#endif
}
if (err == noErr) {
PrintHelp();
do
{
printf("Enter a
command:\n");
gets(command);
switch ( command[0] ) {
case 's':
gPacketsToSend += 1;
break;
case 'S':
gPacketsToSend += 10;
break;
case 'r':
startTime = TickCount();
while ( TickCount() < (startTime + (3 * 60)) ) {
(void)
MyYieldToAnyThread();
}
break;
case 'p':
PrintIPAddressesTest();
break;
case '?':
PrintHelp();
break;
case 'q':
gQuitNow = true;
break;
default:
printf("Huh?\n");
break;
}
fflush(stdout);
}
while ( ! gQuitNow );
printf("Waiting for threads to terminate...\n");
while ( GetThreadState(echoThread, &junkState) == noErr) {
(void) MyYieldToAnyThread();
}
while ( GetThreadState(sendThread, &junkState) == noErr) {
(void) MyYieldToAnyThread();
}
printf("\n");
}
CloseOpenTransport();
}
if (err == noErr) {
printf("Success.\n");
} else {
printf("Failed with
error %d.\n", err);
}
printf("Done. Press command-Q to
Quit.\n");
}
----------------------------------------------------------------------------
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Macnetworkprog mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden