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.
NoCopyReceives.c
/* |
File: NoCopyReceives.c |
Contains: Minimal sample to demo no-copy receives under OT. |
Written by: Quinn "The Eskimo!" |
Copyright: Copyright © 1997-2001 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source 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 source |
code, but that you've made changes. |
Change History (most recent first): |
2/18/2001 Chad Jones Updated for Codewarrior IDE 4.1 and Carbonized for OSX |
7/22/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
///////////////////////////////////////////////////////////////////// |
// Need to include to be carbonized |
#if defined(__MWERKS__) |
# include <carbon.h> |
///////////////////////////////////////////////////////////////////// |
// Pick up all the standard OT stuff. |
# include <Events.h> |
# include <Files.h> |
# include <OpenTransport.h> |
# include <OpenTptInternet.h> |
# include <OpenTptClient.h> |
# include <SIOUX.h> |
#else |
# include <CoreServices/CoreServices.h> |
#endif |
# include <stdio.h> |
///////////////////////////////////////////////////////////////////// |
// OTDebugStr is not defined in any OT header files, but it is |
// exported by the libraries, so we define the prototype here. |
extern pascal void OTDebugStr(const char* str); |
///////////////////////////////////////////////////////////////////// |
// More Assert replaces OTAssert in carbonized code because OTAssert |
// does not exist |
// |
// We usually want asserions and other debugging code |
// turned on, but you can turn it all off if you like |
// by setting MORE_DEBUG to 0. |
// |
#ifndef MORE_DEBUG |
# define MORE_DEBUG 1 |
#endif |
// |
// Our assertion macros compile down to nothing if |
// MORE_DEBUG is not true. MoreAssert produces a |
// value indicating whether the assertion succeeded |
// or failed. MoreAssertQ is Quinn's flavor of |
// MoreAssert which does not produce a value. |
// |
#if MORE_DEBUG |
# define MoreAssert(x) \ |
((x) ? true : (DebugStr ("\pMoreAssert failure: " #x), false)) |
# define MoreAssertQ(x) \ |
do { if (!(x)) DebugStr ("\pMoreAssertQ failure: " #x); } while (false) |
#else |
# define MoreAssert(x) (true) |
# define MoreAssertQ(x) |
#endif |
///////////////////////////////////////////////////////////////////// |
static UInt32 gLastPrinted = 0; |
static pascal void YieldingNotifier(void* contextPtr, OTEventCode code, |
OTResult result, void* cookie) |
{ |
#pragma unused(contextPtr) |
#pragma unused(result) |
#pragma unused(cookie) |
switch (code) { |
case kOTSyncIdleEvent: |
if ( TickCount() > gLastPrinted + 60 ) { |
printf("."); |
fflush(stdout); |
gLastPrinted = TickCount(); |
} |
break; |
default: |
// do nothing |
break; |
} |
} |
///////////////////////////////////////////////////////////////////// |
enum { |
kTransferBufferSize = 1024 |
}; |
static char gTransferBuffer[kTransferBufferSize]; |
static OSStatus NoCopyReceiveUsingOTReadBuffer(EndpointRef ep, SInt16 destFileRefNum) |
// Reads data from the endpoint using no-copy receive. The data |
// is then copied out of the OTBuffer chain using the OTReadBuffer utility |
// function. This method is useful if you need to look at a small chunk of |
// data (which you can copy out using OTReadBuffer) to decide what to do with |
// the rest. |
{ |
OSStatus err; |
OTResult result; |
OTBuffer *receivedBuffer; |
OTBufferInfo bufferInfo; |
OTFlags junkFlags; |
UInt32 bytesRemaining; |
OTByteCount bytesThisTime; //This is a UInt32 |
SInt32 count; |
// Prepare for failure. |
err = noErr; |
receivedBuffer = nil; |
// Read the data. Use the constant kOTNetbufDataIsOTBufferStar to |
// indicate that you want to do a no-copy receive. |
result = OTRcv(ep, &receivedBuffer, kOTNetbufDataIsOTBufferStar, &junkFlags); |
if (result >= 0) { |
// Use the OT utility function OTBufferDataSize to calculate |
// how much data OT returned. |
bytesRemaining = OTBufferDataSize(receivedBuffer); |
// Initialise the bufferInfo data structure. |
bufferInfo.fOffset = 0; |
bufferInfo.fBuffer = receivedBuffer; |
// Write that data to the file. We do this in chunks, |
// copying each chunk of data out of the OTBuffer chain |
// and into our transfer buffer using the OTReadBuffer function, |
// then writing each chunk of data, until there is no |
// more data left in the buffer chain. This is not a |
// particularly efficient method (see below for something |
// better) but it does demonstrate the use of OTReadBuffer. |
while (err == noErr && bytesRemaining > 0) { |
if (bytesRemaining > kTransferBufferSize) { |
bytesThisTime = kTransferBufferSize; |
} else { |
bytesThisTime = bytesRemaining; |
} |
(void) OTReadBuffer(&bufferInfo, gTransferBuffer, &bytesThisTime); |
count = bytesThisTime; |
err = FSWrite(destFileRefNum, &count, gTransferBuffer); |
bytesRemaining -= bytesThisTime; |
} |
err = noErr; |
} else { |
err = result; |
} |
// Clean up. We *must* release the OTBuffer chain back to OT |
// so that it can reuse it. Also, OTReleaseBuffer is not tolerant of |
// the parameter being nil, so we check for that case first. |
if (receivedBuffer != nil) { |
OTReleaseBuffer(receivedBuffer); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////////// |
static OSStatus NoCopyReceiveWalkingBufferChain(EndpointRef ep, SInt16 destFileRefNum) |
// Reads data from the endpoint using no-copy receive. We walk |
// the resulting buffer chain, writing out chunks of data |
// directly to the file from the buffers returned to us by OT. |
{ |
OSStatus err; |
OTResult result; |
OTBufferInfo bufferInfo; |
OTBuffer *thisBuffer; |
OTFlags junkFlags; |
SInt32 count; |
err = noErr; |
// Initialise the bufferInfo data structure. |
bufferInfo.fOffset = 0; |
bufferInfo.fBuffer = nil; |
// Read the data. Use the constant kOTNetbufDataIsOTBufferStar to |
// indicate that you want to do a no-copy receive. |
result = OTRcv(ep, &bufferInfo.fBuffer, kOTNetbufDataIsOTBufferStar, &junkFlags); |
if (result >= 0) { |
// Now walk the returned buffer chain, writing out each |
// chunk of data to the file. |
thisBuffer = bufferInfo.fBuffer; |
while (err == noErr && thisBuffer != nil) { |
count = thisBuffer->fLen; |
err = FSWrite(destFileRefNum, &count, thisBuffer->fData); |
thisBuffer = thisBuffer->fNext; |
} |
} else { |
err = result; |
} |
// Clean up. We *must* release the OTBuffer chain back to OT |
// so that it can reuse it. Also, OTReleaseBuffer is not tolerant of |
// the parameter being nil, so we check for that case first. |
if (bufferInfo.fBuffer != nil) { |
OTReleaseBuffer(bufferInfo.fBuffer); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////////// |
enum { |
kUsingOTReadBuffer = 0, |
kUseWalkingBufferChain |
}; |
static OSStatus TestNoCopyReceive(UInt8 method, SInt16 destFileRefNum) |
// Test the above two no-copy receive functions by connecting |
// to "www.apple.com" and downloading the root HTTP object. |
{ |
OSStatus err; |
OSStatus junk; |
EndpointRef ep; |
DNSAddress hostDNSAddress; |
TCall sndCall; |
OTResult bytesSent; |
char httpGetCommand[256]; |
// Create a TCP endpoint. |
ep = OTOpenEndpointInContext(OTCreateConfiguration(kTCPName), 0, nil, &err,nil); |
// Set up the endpoint. |
if (err == noErr) { |
// 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); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTSetSynchronous failed |
junk = OTSetBlocking(ep); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTSetBlocking failed |
junk = OTInstallNotifier(ep, NewOTNotifyUPP(YieldingNotifier), nil); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTInstallNotifier failed |
junk = OTUseSyncIdleEvents(ep, true); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTUseSyncIdleEvents failed |
// Bind the endpoint. Because we're an outgoing connection, |
// we don't have to bind it to a specific address. |
err = OTBind(ep, nil, nil); |
} |
// Initialise the sndCall structure and call OTConnect. We nil |
// out most of the fields in the sndCall structure because |
// we don't want any special options or connection data. |
// The important field of the sndCall is the addr TNetBuf, |
// which we initialise to the address "www.apple.com:80" |
// (port 80 is the HTTP port). |
if (err == noErr) { |
OTMemzero(&sndCall, sizeof(TCall)); |
sndCall.addr.buf = (UInt8 *) &hostDNSAddress; |
sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, "www.apple.com:80"); |
err = OTConnect(ep, &sndCall, nil); |
} |
// Send the HTTP command to the web server. |
if (err == noErr) { |
(void) sprintf(httpGetCommand, "GET / HTTP/1.0%c%c%c%c", 13, 10, 13, 10); |
bytesSent = OTSnd(ep, (void *) httpGetCommand, OTStrLength(httpGetCommand), 0); |
// OTSnd returns the number of bytes sent. Because we're in |
// synchronous mode, it won't return until it's sent all the |
// bytes, or it gets an error. |
if (bytesSent > 0) { |
err = noErr; |
} else { |
err = bytesSent; |
} |
} |
// Now receive the response from the server and write it to the |
// destination file. |
if (err == noErr) { |
// No-copy receive does not really make sense in sync/blocking |
// mode, so we switch the endpoint to sync/non-blocking before. |
// proceeding. This is reasonable because you should not be using |
// no-copy receives unless you're looking for maximum speed, |
// and if you're looking for maximum speed you should be using |
// async/blocking mode, and doing everything in your notifier. |
// In this case, I'm just trying to demonstrate it's use, so |
// I make do with sync/non-blocking mode. |
junk = OTSetNonBlocking(ep); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTSetNonBlocking failed |
do { |
// Depending on which method we were asked to use, |
// call the relevant receive function. |
switch (method) { |
case kUsingOTReadBuffer: |
err = NoCopyReceiveUsingOTReadBuffer(ep, destFileRefNum); |
break; |
case kUseWalkingBufferChain: |
err = NoCopyReceiveWalkingBufferChain(ep, destFileRefNum); |
break; |
default: |
//OTDebugBreak("TestNoCopyReceive: What method?"); |
MoreAssert(false);//Triggers if: Get to this error path |
err = -1; |
break; |
} |
// If we get kOTNoDataErr, that means we're still waiting for data, |
// so we just clear the error and continue looping. |
if (err == kOTNoDataErr) { |
YieldingNotifier(nil, kOTSyncIdleEvent, noErr, nil); |
err = noErr; |
} |
} while (err == noErr); |
// We will eventually leave the above loop with a kOTLookErr |
// because the endpoint received either a T_ORDREL or T_DISCONNECT |
// event. Either way, the data transfer is finished, so |
// we can just clear the error code and continue. |
if (err == kOTLookErr) { |
err = noErr; |
} |
} |
// I'm not going to bother cleaning up the endpoint cleanly. |
// This is the subject of another sample. For this sample, force |
// closing the endpoint is good enough. |
// lean up. |
if (ep != kOTInvalidEndpointRef) { |
junk = OTCloseProvider(ep); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTCloseProvider failed |
} |
return err; |
} |
///////////////////////////////////////////////////////////////////// |
int main(void) |
// The main line of the sample program. It creates |
// a file in the same directory as the sample called |
// "NoCopyReceive Test Output" and puts two copies |
// of the root HTTP object from "www.apple.com" into |
// that file. |
{ |
OSStatus err; |
OSStatus junk; |
FSSpec fss; |
SInt16 destFileRefNum; |
#if defined(__MWERKS__) |
SIOUXSettings.autocloseonquit = FALSE; // don't close the SIOUX window on program termination |
SIOUXSettings.asktosaveonclose = FALSE; // don't offer to save on a close |
#endif |
printf("NoCopyReceives\n"); |
printf("-- Download a URL using no-copy receives\n"); |
printf("\n"); |
err = InitOpenTransportInContext(kInitOTForApplicationMask, nil); |
if (err == noErr) { |
// Create and open the output file. |
(void) FSMakeFSSpec(0, 0, "\pNoCopyReceives_Output.txt", &fss); |
(void) FSpCreate(&fss, 'R*ch', 'TEXT', 0); |
err = FSpOpenDF(&fss, fsRdWrPerm, &destFileRefNum); |
// Download two copies of the URL, one using the OTReadBuffer |
// method, the other by the buffer walking method. |
if (err == noErr) { |
printf("Downloading using OTReadBuffer method"); |
err = TestNoCopyReceive(kUsingOTReadBuffer, destFileRefNum); |
printf("\n\n"); |
if (err == noErr) { |
printf("Downloading using 'walk the buffer chain' method"); |
err = TestNoCopyReceive(kUseWalkingBufferChain, destFileRefNum); |
printf("\n\n"); |
} |
// Close the file. |
junk = FSClose(destFileRefNum); |
MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: FSClose failed |
} |
CloseOpenTransportInContext(nil); |
} |
if (err == noErr) { |
printf("Success.\n"); |
} else { |
printf("Failed with error %ld.\n", err); |
} |
printf("Done.\n"); |
return(0); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14