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.
closer.c
/******************************************************************************** |
TCPClose |
this snippet shows how to close a connection gracefully without causing errors or forcing |
aborts on either the local or remote end. |
note: don't run this from the think debugger, as it doesn't like getting invoked at |
interrupt time via a DebugStr()... |
Steve Falkenburg |
MacDTS |
11/16/92 |
********************************************************************************/ |
#include <MacTCPCommonTypes.h> |
#include <TCPPB.h> |
// constants |
#define kTimeout 30 |
#define kFTPServerIP 0x5a85342c // change this to your ftp host !! |
#define kFTPPort 21 |
// globals |
short gDrvrRef; |
Boolean gTerminated; |
// prototypes |
void main(void); |
OSErr MakeStream(StreamPtr *stream); |
OSErr DoFTPConnStuff(StreamPtr stream); |
OSErr OpenConnection(StreamPtr stream,long address,short port); |
OSErr CloseConnection(StreamPtr stream); |
OSErr ReceiveAndJunkData(StreamPtr stream); |
pascal void ASR( |
StreamPtr tcpStream, |
unsigned short eventCode, |
Ptr userDataPtr, |
unsigned short terminReason, |
struct ICMPReport *icmpMsg); |
// main program... |
// |
void main(void) |
{ |
OSErr err; |
StreamPtr conn; |
err = OpenDriver("\p.IPP",&gDrvrRef); // open TCP driver |
if (err!=noErr) { |
DebugStr("\pMacTCP not installed"); |
return; |
} |
err = MakeStream(&conn); // create our connection stream |
if (err!=noErr) { |
DebugStr("\pcouldn't make stream"); |
return; |
} |
err = OpenConnection(conn,kFTPServerIP,21); // open a connection (change IP #) |
if (err!=noErr) { |
CloseConnection(conn); |
DebugStr("\pConnection not opened"); |
return; |
} |
err = DoFTPConnStuff(conn); // do a short FTP session |
if (err!=noErr) |
DebugStr("\pFTP server not responding"); |
err = CloseConnection(conn); // close the connection and stream |
} |
// opens a TCP stream and returns the stream ptr. we use an 8k stream buffer |
// |
OSErr MakeStream(StreamPtr *stream) |
{ |
OSErr err; |
TCPiopb pBlock; |
Ptr connBuffer; |
long connBufferLen; |
connBufferLen = 8192; |
connBuffer = NewPtr(connBufferLen); |
if (MemError()!=noErr) |
return MemError(); |
pBlock.ioCRefNum = gDrvrRef; |
pBlock.csCode = TCPCreate; |
pBlock.csParam.create.rcvBuff = connBuffer; |
pBlock.csParam.create.rcvBuffLen = connBufferLen; |
pBlock.csParam.create.notifyProc = ASR; |
err = PBControl(&pBlock,false); |
*stream = pBlock.tcpStream; |
if (err!=noErr) |
DisposPtr(connBuffer); |
return err; |
} |
// opens the connection to the ip # and tcp port passed in |
// |
OSErr OpenConnection(StreamPtr stream,long address,short port) |
{ |
OSErr err; |
TCPiopb pBlock; |
gTerminated = false; |
pBlock.ioCRefNum = gDrvrRef; |
pBlock.csCode = TCPActiveOpen; |
pBlock.tcpStream = stream; |
pBlock.csParam.open.ulpTimeoutValue = kTimeout; |
pBlock.csParam.open.ulpTimeoutAction = 1; |
pBlock.csParam.open.validityFlags = 0xC0; |
pBlock.csParam.open.commandTimeoutValue = kTimeout; |
pBlock.csParam.open.remoteHost = address; |
pBlock.csParam.open.remotePort = port; |
pBlock.csParam.open.localPort = 0; |
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; |
err = PBControl((ParmBlkPtr)&pBlock,false); |
return err; |
} |
// does a short FTP session, consisting of receiving the welcome message, |
// issuing a "QUIT" command, and receiving the response |
// |
OSErr DoFTPConnStuff(StreamPtr stream) |
{ |
OSErr err; |
TCPiopb pBlock; |
wdsEntry wds[2]; |
wds[0].length = 6; |
wds[0].ptr = "QUIT\r\n"; |
wds[1].length = 0; |
err = ReceiveAndJunkData(stream); |
if (err!=noErr) |
DebugStr("\precverror"); |
pBlock.ioCRefNum = gDrvrRef; |
pBlock.csCode = TCPSend; |
pBlock.tcpStream = stream; |
pBlock.csParam.send.ulpTimeoutValue = kTimeout; |
pBlock.csParam.send.ulpTimeoutAction = 1; |
pBlock.csParam.send.validityFlags = 0xC0; |
pBlock.csParam.send.pushFlag = false; |
pBlock.csParam.send.urgentFlag = true; |
pBlock.csParam.send.wdsPtr = (Ptr)wds; |
err = PBControl((ParmBlkPtr)&pBlock,false); |
if (err!=noErr) |
DebugStr("\perrsend"); |
err = ReceiveAndJunkData(stream); |
if (err!=noErr) |
DebugStr("\precverror"); |
return err; |
} |
// close the connection gracefully. this involves issuing the TCPClose, receiving data |
// until we get an error (the remote end is closing) and issue a TCPRelease to get rid of |
// the stream. |
// |
OSErr CloseConnection(StreamPtr stream) |
{ |
OSErr err; |
TCPiopb pBlock; |
pBlock.ioCRefNum = gDrvrRef; |
pBlock.csCode = TCPClose; |
pBlock.tcpStream = stream; |
pBlock.csParam.close.ulpTimeoutValue = kTimeout; |
pBlock.csParam.close.validityFlags = 0xC0; |
pBlock.csParam.close.ulpTimeoutAction = 1; |
err = PBControl((ParmBlkPtr)&pBlock,false); |
// receive data until the connection closes |
do { |
err = ReceiveAndJunkData(stream); |
} while (!gTerminated); |
pBlock.ioCRefNum = gDrvrRef; |
pBlock.csCode = TCPRelease; |
pBlock.tcpStream = stream; |
err = PBControl((ParmBlkPtr)&pBlock,false); |
if (err!=noErr) |
return err; |
DisposPtr(pBlock.csParam.create.rcvBuff); |
return err; |
} |
// receive a block of data from the remote connection, but don't even return what the data |
// is. we're using the no-copy form of the receive command for simplicity. |
// |
OSErr ReceiveAndJunkData(StreamPtr stream) |
{ |
OSErr err; |
TCPiopb pBlock; |
rdsEntry rds[3]; |
// set up our 2-part rds |
rds[0].length = 0; |
rds[0].ptr = nil; |
rds[1].length = 0; |
rds[1].ptr = nil; |
rds[2].length = 0; |
rds[2].ptr = nil; |
// issue the receive |
pBlock.ioCRefNum = gDrvrRef; |
pBlock.csCode = TCPNoCopyRcv; |
pBlock.tcpStream = stream; |
pBlock.csParam.receive.commandTimeoutValue = kTimeout; |
pBlock.csParam.receive.rdsPtr = (Ptr)rds; |
pBlock.csParam.receive.rdsLength = 2; |
err = PBControl((ParmBlkPtr)&pBlock,false); |
// return the buffer |
if (err==noErr) { |
pBlock.csCode = TCPRcvBfrReturn; |
err = PBControl((ParmBlkPtr)&pBlock,false); |
} |
return err; |
} |
// our asynchronous notification routine. this gets called several times, but we only |
// look at the connection termination messages. we do this to determine if the close |
// was graceful, or if either we or the remote end had to abort. |
// |
pascal void ASR( |
StreamPtr tcpStream, |
unsigned short eventCode, |
Ptr userDataPtr, |
unsigned short terminReason, |
struct ICMPReport *icmpMsg) |
{ |
if (eventCode==TCPTerminate) { |
gTerminated = true; |
switch (terminReason) { |
case TCPULPClose: |
DebugStr("\pgraceful close on both ends"); // this is the one you want |
break; |
case TCPRemoteAbort: |
DebugStr("\premote connection forced abort"); |
break; |
case TCPULPTimeoutTerminate: |
DebugStr("\pULP timeout reached so connection dropped"); |
break; |
case TCPULPAbort: |
DebugStr("\pTCPRelease issued before remote end closed"); |
break; |
default: |
DebugStr("\pconnection terminated non-gracefully"); |
break; |
} |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14