Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
network.c
/* |
TCP Client/Server Queuing Example |
Steve Falkenburg, MacDTS, Apple Computer |
3/11/92 |
this client/server sample uses MacTCP to implement a simple "greeting" server. the server |
opens up several listeners on kGreetingPort (1235). when a client connects, the data entered |
in the greeting dialog is sent to the remote connection, and the connection is closed. |
connection management is done through the use of Operating System queues to simplify tracking |
and usage. |
*/ |
#include <Processes.h> |
#include <MacTCPCommonTypes.h> |
#include <TCPPB.h> |
#include "const.h" |
#include "globals.h" |
#include "utils.h" |
#include "queues.h" |
#include "network.h" |
void StartListener(TCPiopb *pBlock); |
void ListenerCompletion(MyQElemPtr iopb); |
void SendData(TCPiopb *pBlock,StringPtr data); |
/* initialize TCP/IP driver and create our queues, putting them on the unused list |
*/ |
OSErr InitNetwork(void) |
{ |
OSErr err; |
TCPiopb *pBlock; |
short i; |
err = OpenDriver("\p.ipp",&gTcpDrvrRef); |
if (err!=noErr) |
return err; |
for (i=0; i<kNumConnections; i++) { |
pBlock = (TCPiopb *)GetUnusedPBlock(); |
if (pBlock) |
StartListener(pBlock); |
} |
return noErr; |
} |
/* creates a connection end and assigns it to listen for an incoming connection on our |
network port. |
*/ |
void StartListener(TCPiopb *pBlock) |
{ |
Ptr rcvBuff; |
OSErr err; |
rcvBuff = NewPtr(kRcvBuffSize); |
if (MemError()!=noErr) |
DoError(MemError()); |
/* create a network stream */ |
pBlock->csCode = TCPCreate; |
pBlock->ioCRefNum = gTcpDrvrRef; |
pBlock->csParam.create.rcvBuff = rcvBuff; |
pBlock->csParam.create.rcvBuffLen = kRcvBuffSize; |
pBlock->csParam.create.notifyProc = nil; |
err = PBControl(pBlock,false); |
if (err!=noErr) |
DoError(err); |
/* create a listener */ |
pBlock->csCode = TCPPassiveOpen; |
#ifdef __SYSEQU__ |
((MyQElemPtr)pBlock)->savedA5 = *(long *)CurrentA5; |
#else |
((MyQElemPtr)pBlock)->savedA5 = (long)CurrentA5; |
#endif |
pBlock->ioCompletion = (ProcPtr)ListenerCompletion; |
pBlock->csParam.open.ulpTimeoutValue = 0; |
pBlock->csParam.open.ulpTimeoutAction = 1; |
pBlock->csParam.open.validityFlags = 0xC0; |
pBlock->csParam.open.commandTimeoutValue = 0; |
pBlock->csParam.open.remoteHost = 0; |
pBlock->csParam.open.remotePort = 0; |
pBlock->csParam.open.localHost = 0; |
pBlock->csParam.open.localPort = kGreetingPort; |
pBlock->csParam.open.tosFlags = 0; |
pBlock->csParam.open.precedence = 0; |
pBlock->csParam.open.dontFrag = 0; |
pBlock->csParam.open.timeToLive = 0; |
pBlock->csParam.open.security = 0; |
pBlock->csParam.open.optionCnt = 0; |
PBControl(pBlock,true); |
gRunning++; // increment count of running parameter blocks |
} |
/* called to release all network resources in response to a quit |
*/ |
void CloseNetwork(void) |
{ |
THz theZone; |
short drvrRefNum; |
OSErr err; |
TCPiopb tcpBlock; |
StreamPtr *curStream; |
long theStream; |
theZone = ApplicZone(); |
tcpBlock.ioCRefNum = gTcpDrvrRef; |
tcpBlock.csCode = TCPGlobalInfo; |
err = PBControl((ParmBlkPtr)&tcpBlock,false); |
if (err!=noErr) |
return; |
curStream = *tcpBlock.csParam.globalInfo.tcpCDBTable; |
while (*curStream) { |
theStream = *curStream; |
if (PtrZone((Ptr)theStream)==theZone) { // only release streams in our heap |
tcpBlock.csCode = TCPStatus; |
tcpBlock.tcpStream = theStream; |
err = PBControl((ParmBlkPtr)&tcpBlock,false); |
// abort connection |
tcpBlock.csCode = TCPAbort; |
tcpBlock.tcpStream = theStream; |
err = PBControl((ParmBlkPtr)&tcpBlock,false); |
// release stream |
tcpBlock.csCode = TCPRelease; |
tcpBlock.tcpStream = theStream; |
err = PBControl((ParmBlkPtr)&tcpBlock,false); |
} |
curStream++; |
} |
} |
/* this completion routine will be called when one of the listeners gets a connection from a |
remote machine. since we're using OS queues dispatched from the main event loop, we just |
take the parameter block and put it on the completed queue. if running seven, we call |
WakeUpProcess() to return control to the application |
*/ |
void ListenerCompletion(MyQElemPtr iopb) |
{ |
long saveA5; |
ProcessSerialNumber currentProc; |
saveA5 = SetA5(iopb->savedA5); |
StoreCompletedPBlock(iopb); |
gRunning--; |
if (gRunningSeven) { |
WakeUpProcess(&gOurPSN); |
} |
SetA5(saveA5); |
} |
/* this routine is called from the event dispatcher of the event loop to continue processing |
of completed listens received at interrupt time from ListenerCompletion. We send them |
some data, close the connection, and restart the listener, putting it back on MacTCP's queue |
*/ |
void ProcessConnection(MyQElemPtr iopb) |
{ |
SendData(&iopb->tcpBlock,gGreetingData); |
RecycleFreePBlock(iopb); |
iopb = GetUnusedPBlock(); |
if (iopb) |
StartListener(&iopb->tcpBlock); |
} |
/* here, we send some data to the remote host over the opened connection and close the connection |
when done. if we didn't use os queues, this code would be much more complicated, since it |
would have to be interrupt safe, and we would have to perform all driver calls async with |
linked completion routines (yuck) |
*/ |
void SendData(TCPiopb *pBlock,StringPtr data) |
{ |
OSErr err; |
wdsEntry wdsPtr[4]; |
char crlf[] = "\015\012"; |
wdsPtr[0].length = wdsPtr[2].length = 2; |
wdsPtr[0].ptr = wdsPtr[2].ptr = crlf; |
wdsPtr[1].length = data[0]; |
wdsPtr[1].ptr = (void *)&data[1]; |
wdsPtr[3].length = 0; |
wdsPtr[3].ptr = nil; |
pBlock->csCode = TCPSend; |
pBlock->ioCRefNum = gTcpDrvrRef; |
pBlock->csParam.send.ulpTimeoutValue = 0; |
pBlock->csParam.send.ulpTimeoutAction = 1; |
pBlock->csParam.send.validityFlags = 0xc0; |
pBlock->csParam.send.pushFlag = false; |
pBlock->csParam.send.urgentFlag = false; |
pBlock->csParam.send.wdsPtr = (Ptr)wdsPtr; |
err = PBControl(pBlock,false); |
if (err!=noErr) |
DoError(err); |
pBlock->csCode = TCPClose; |
pBlock->ioCRefNum = gTcpDrvrRef; |
pBlock->csParam.close.validityFlags = 0xC0; |
pBlock->csParam.close.ulpTimeoutValue = 20; |
pBlock->csParam.close.ulpTimeoutAction = 1; |
err = PBControl(pBlock,false); |
if (err!=noErr) |
DoError(err); |
pBlock->csCode = TCPRelease; |
pBlock->ioCRefNum = gTcpDrvrRef; |
err = PBControl(pBlock,false); |
if (err!=noErr) |
DoError(err); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14