<> <> <> <<>> <> DIRECTORY XNS USING [Address, unknownAddress, unknownSocket, Socket], XNSBuf USING [Buffer, maxBodyBytes, maxBodyFWords, maxBodyHWords], XNSErrorTypes USING [ErrorType]; XNSSocket: CEDAR DEFINITIONS ~ { Address: TYPE ~ XNS.Address; Socket: TYPE ~ XNS.Socket; Buffer: TYPE ~ XNSBuf.Buffer; Handle: TYPE ~ REF Object; Object: TYPE; <> Milliseconds: TYPE = INT; dontWait: Milliseconds = 0; waitForever: Milliseconds = INT.LAST; <> <> Create: PROC [ remote: Address _ XNS.unknownAddress, sendBuffers: NAT _ 1, recvBuffers: NAT _ 5, getTimeout: Milliseconds _ 10000, local: Socket _ XNS.unknownSocket ] -- unknownSocket => XNSPackage will assign a free one RETURNS [handle: Handle]; <> GetLocalAddress: PROC [Handle] RETURNS [Address]; GetRemoteAddress: PROC [Handle] RETURNS [Address]; SetRemoteAddress: PROC [Handle, Address]; <> <<>> SetGetTimeout: PROC [Handle, Milliseconds]; <<>> SetSoftwareChecksumming: PROC [handle: Handle, send, recv: BOOL]; < Faster>> <> <> <<>> SetNoErrors: PROC [handle: Handle, noErrors: BOOL _ TRUE]; <> Kick: PROC [handle: Handle]; <> <<>> Destroy: PROC [handle: Handle]; <> <> AllocBuffer: PROC [handle: Handle] RETURNS [Buffer]; <<>> SetUserBytes: PROC [b: Buffer, bytes: [0 .. XNSBuf.maxBodyBytes]]; SetUserHWords: PROC [b: Buffer, hWords: [0 .. XNSBuf.maxBodyHWords]]; SetUserFWords: PROC [b: Buffer, fWords: [0 .. XNSBuf.maxBodyFWords]]; <> SetUserSize: PROC [b: Buffer, sizeUnits: NAT]; <> <> <> <<>> Put: PROC [handle: Handle, b: Buffer]; <> <> <> <> <<>> Send: PROC [handle: Handle, b: Buffer, dest: Address]; <> Broadcast: PROC [handle: Handle, b: Buffer, socket: Socket]; <> <> Get: PROC [handle: Handle] RETURNS [Buffer]; <> <> <<>> GetUserBytes: PROC [Buffer] RETURNS [bytes: [0 .. XNSBuf.maxBodyBytes]]; GetUserHWords: PROC [Buffer] RETURNS [hWords: [0 .. XNSBuf.maxBodyHWords]]; GetUserFWords: PROC [Buffer] RETURNS [fWords: [0 .. XNSBuf.maxBodyFWords]]; <> GetUserSize: PROC [Buffer] RETURNS [NAT]; <> <> <> FreeBuffer: PROC [handle: Handle, b: Buffer]; <> <> <> <<>> FixupSourceAndDest: PROC [Buffer]; <> <> <> <> <> <<>> ReturnToSender: PROC [handle: Handle, b: Buffer]; <> <> <> <<>> ReturnError: PROC [handle: Handle, b: Buffer, type: XNSErrorTypes.ErrorType]; <> }. Buffers: The hard part of communications is buffer management. Buffers are large and pinned into real memory. It hurts to be sloppy. If you get a buffer from the XNSPackage via AllocBuffer or Get, you should give it back via FreeBuffer, ReturnToSender, ReturnError, Put* or Send. Once you have handed a buffer to the XNSPackage, don't touch it again. ("b _ NIL;" is suggested as a reminder.) The XNSPackage attempts to protect itself against gross client errors by limiting the number of buffers a client can have at any time. Send and receive buffers are counted/limited separately. Buffers are reference counted, so it's OK to drop one on the floor in very weird cases (like getting ABORTed from the debugger). They should be explicitly freed on any reasonably likely error path, especially one that can be induced by a client error or an error on another machine. Sample Server: handle: XNSSocket.Handle _ XNSSocket.Create[local: XNSWKS.xxx, getTimeout: XNSSocket.waitForever]; UNTIL quit DO b: Buffer _ XNSSocket.Get[handle ! ABORTED => { b _ NIL; CONTINUE; } ] IF b = NIL THEN LOOP; ... process the request ... IF reject THEN { XNSSocket.FreeBuffer[b]; LOOP; }; XNSSocket.FixupSourceAndDest[b]; lastClient _ b.source; b.type _ xxx; b.response _ [...]; XNSSocket.SetUserSize[b, SIZE[Response]]; XNSSocket.ReturnToSender[handle, b]; b _ NIL; ENDLOOP; XNSSocket.Destroy[handle]; Sample User: server: ROPE _ xxx. him: XNS.Address _ ... handle: XNSSocket.Handle _ XNSSocket.Create[remote: him, getTimeout: 5000]; hit: BOOL _ FALSE; FOR i: NAT IN [0..10) UNTIL hit DO b: XNSBuffer.Buffer _ XNSSocket.AllocBuffer[handle] b.type _ xxx; b.request _ [xxx]; XNSSocket.SetUserSize[handle, b, SIZE[Request]]; XNSSocket.Put[handle, b]; b _ XNSSocket.Get[handle] IF b = NIL THEN LOOP; ... IF notMyAnswer THEN { XNSSocket.FreeBuffer[handle, b]; LOOP; }; ... process the answer ... XNSSocket.FreeBuffer[handle, b]; b _ NIL; hit _ TRUE; ENDLOOP; XNSSocket.SetNoErrors[handle, TRUE]; XNSSocket.Destroy[handle]; IF ~hit THEN ERROR NoResponse PacketExchange: The XNS Packet Exchange protocol is supposed to automate a certain amount of the above, and might be worth considering. Process structure: The above examples only work if "process the request" is fast relative to the users retransmission rate. If some requests might take a long time, consider passing them to another process so that the main process can continue to take packets from the PupPackage. It's OK to have several processes sending and/or receiving with the same handle at the same time. (That's unlikely for anything reasonably simple.) The client must provide the ML to make sure that Put uses the desired SetRemoteAddress. Sending: There are 3 ways to send a packet. Put is the simplest. It just sends the pup to the address saved from Create or SetRemoteAddress. Send is more convient if you are sending single packets to various machines. ReturnToSender is the normal way for a server to send a single packet response back to a user. The sending process will block until the packet has been sent by the hardware. (In the old days, the packet was queued in the driver, but the sending process didn't have to wait for it.) Receiving: The XNSPackage doesn't do any filtering on the source address. (You might expect it to check since you specified a remote address when you created the handle.) Normally, this works out OK since: 1) if you are a server (using a WKS), you will probably take packets from anybody, or 2) if you are a user (using a default system assigned socket), nobody but the server(s) you are talking to knows your socket number.