<> <> <> <<>> <> DIRECTORY Endian USING [FWORD], Pup USING [Address, Socket], PupBuffer USING [Buffer, ByteIndex, FWordIndex, HWordIndex], PupType USING [ErrorCode], Rope USING [ROPE]; PupSocket: CEDAR DEFINITIONS = { Buffer: TYPE = PupBuffer.Buffer; ROPE: TYPE = Rope.ROPE; Socket: TYPE = REF Object; Object: TYPE; <> Milliseconds: TYPE = INT; dontWait: Milliseconds = 0; waitForever: Milliseconds = INT.LAST; <> <> CreateServer: PROC [ local: Pup.Socket, -- Must be WellKnown sendBuffers: NAT _ 1, recvBuffers: NAT _ 5, getTimeout: Milliseconds _ 10000 ] RETURNS [socket: Socket]; <> <> <> <> CreateEphemeral: PROC [ remote: Pup.Address, sendBuffers: NAT _ 1, recvBuffers: NAT _ 5, getTimeout: Milliseconds _ 10000 ] RETURNS [socket: Socket]; <> <> SetRemoteAddress: PROC [Socket, Pup.Address]; GetRemoteAddress: PROC [Socket] RETURNS [Pup.Address]; <> GetLocalAddress: PROC [Socket] RETURNS [Pup.Address]; <> <> SetGetTimeout: PROC [Socket, Milliseconds]; <> <<>> SetNoErrors: PROC [socket: Socket]; <> <> Kick: PROC [socket: Socket]; <> <> <<>> Destroy: PROC [socket: Socket]; <> <> <> AllocBuffer: PROC [socket: Socket] RETURNS [Buffer]; <> <> <<>> SetUserBytes: PROC [b: Buffer, bytes: PupBuffer.ByteIndex]; SetUserHWords: PROC [b: Buffer, hWords: PupBuffer.HWordIndex]; SetUserFWords: PROC [b: Buffer, fWords: PupBuffer.FWordIndex]; SetUserSize: PROC [b: Buffer, sizeUnits: NAT]; <> <> <> <> <<>> Broadcast: PROC [socket: Socket, b: Buffer]; <> <> <<>> Put: PROC [socket: Socket, b: Buffer]; <> Send: PROC [socket: Socket, b: Buffer, dest: Pup.Address]; <> <> <> <<>> <> Get: PROC [socket: Socket] RETURNS [Buffer]; <> <> <> <<>> GetUserBytes: PROC [Buffer] RETURNS [bytes: PupBuffer.ByteIndex]; GetUserHWords: PROC [Buffer] RETURNS [hWords: PupBuffer.HWordIndex]; GetUserFWords: PROC [Buffer] RETURNS [fWords: PupBuffer.FWordIndex]; GetUserSize: PROC [Buffer] RETURNS [sizeUnits: NAT]; <> <> <> <> FreeBuffer: PROC [b: Buffer]; <> <> <> <> <<>> FixupSourceAndDest: PROC [Buffer]; <> <> <> <> <> <> <<>> ReturnToSender: PROC [b: Buffer]; <> <> <> <> <<>> ReturnError: PROC [b: Buffer, code: PupType.ErrorCode, rope: ROPE]; <> <> <> <<>> <> CopyRope: PROC [b: PupBuffer.Buffer, rope: Rope.ROPE]; AppendRope: PROC [b: PupBuffer.Buffer, rope: Rope.ROPE]; <> <> <<>> ExtractRope: PROC [b: PupBuffer.Buffer] RETURNS [rope: Rope.ROPE]; <> ExtractErrorRope: PROC [b: PupBuffer.Buffer] RETURNS [rope: Rope.ROPE]; ExtractAbortRope: PROC [b: PupBuffer.Buffer] RETURNS [rope: Rope.ROPE]; <> <> <> GetUniqueID: PROC RETURNS [Endian.FWORD]; <> <> IsThisMe: PROC [Pup.Address] RETURNS [yes: BOOL]; <> <> GetMyAddress: PROC RETURNS [Pup.Address]; <> <> <> SocketNotWellKnown: ERROR; }. Buffers: The hard part of communications is buffer management. Buffers are large and pined into real memory. If you get a buffer from the PupPackage via AllocBuffer or Get, you should give it back via FreeBuffer, ReturnToSender, ReturnError, Put* or Send. Once you have handed a buffer to the PupPackage, don't touch it again. ("b _ NIL;" is suggested as a reminder.) The PupPackage 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. Sockets and Buffers are reference counted, so it's OK to drop them on the floor in funny cases. Sample Server: socket: PupSocket.Socket _ PupSocket.CreateServer[local: PupWKS.xxx, getTimeout: INT.LAST]; UNTIL quit DO b: Buffer _ PupSocket.Get[socket ! ABORTED => { b _ NIL; CONTINUE; } ] IF b = NIL THEN LOOP; ... process the request ... IF reject THEN { PupSocket.FreeBuffer[b]; LOOP; }; PupSocket.FixupSourceAndDest[b]; lastClient _ b.source; b.type _ xxx; b.response _ [...]; PupSocket.SetUserSize[b, SIZE[Response]]; PupSocket.ReturnToSender[socket, b]; b _ NIL; ENDLOOP; PupSocket.Destroy[socket]; Sample User: server: ROPE _ xxx. him: Pup.Address _ PupName.NameLookup[server, PupWKS.xxx ! ...]; socket: PupSocket.Socket _ PupSocket.CreateEphemeral[remote: him, getTimeout: 5000]; hit: BOOL _ FALSE; id: Endian.FWORD _ ... FOR i: NAT IN [0..10) UNTIL hit DO b: PupBuffer.Buffer _ PupSocket.AllocBuffer[socket] b.type _ xxx; b.id _ id; b.request _ [xxx]; PupSocket.SetUserSize[socket, b, SIZE[Request]]; PupSocket.Put[socket, b]; b _ PupSocket.Get[socket] IF b = NIL THEN LOOP; IF id # b.id THEN { PupSocket.FreeBuffer[socket, b]; LOOP; }; ... process the answer ... PupSocket.FreeBuffer[socket, b]; b _ NIL; hit _ TRUE; ENDLOOP; PupSocket.SetNoErrors[socket, TRUE]; PupSocket.Destroy[socket]; IF ~hit THEN ERROR NoResponse 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 socket at the same time. (That's unlikely for anything reasonably simple.) Sending: There are 3 ways to send a packet. Put is the simplest. It just sends the pup to the address saved from Create. 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 PupPackage 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 socket.) 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. If it matters, you should use and verify the id field.