|
|
Reliable Message PassingThis project involves the design and development of code to implement half-duplex reliable message passing on top of UDP/IP. Recall that half-duplex divides communication into one-way channels, so in this project you will (again) be dealing with a single sender and a single receiver. We have discussed all the tools that you need, including the UDP sockets interface, and a basic discussion of signals, signal handlers, and the SIGALRM that you will use to implement timers. For system calls, you can also refer to section 2 of the Unix man pages; for library routines, you can refer to section 3 of the Unix man pages. (E.g. man 2 sendto, or man 3 htonl). Also Donahoo covers all the necessary material. This project will again be implemented in phases. In the first phase, you will implement reliable message passing using the Stop-And-Wait protocol. In the second phase, you will implement reliable message passing using the Sliding-Window protocol. Note in the grading criteria below that I expect your submissions to be documented, well-structured, and well-commented. In the discussion following, I will use the term channel to denote an application level communication ability between two communication endpoints. Simplifying Assumptions
Peer-to-peer protocolUnlike earlier projects, in which you were able to design the peer to peer protocol yourself, in this project I will prescribe the peer to peer format and rules of interaction. This means that I should be able to use your sender with my receiver or my sender with your receiver and have everything work just fine. If you wish, you can select a "testing partner" to do exactly that -- make sure your sender and receiver can interoperate with the testing partner's sender and receiver. Note that you are responsible for your own code, so if problems arise, I expect you both to debug your own pieces. Since you have a prescribed peer-to-peer protocol, I instead give you the design freedom on the service interface. I will make a few suggestions below, but you should decide on your own design, perhaps using the suggestions as a starting point. All packets should have the following format:
The C structure for such a packet format would look something like the following: struct { Note, however, that this is only representative. In packet exchanges, we will always send the first 8 bytes, but we only send as many bytes of the data portion as are required. The control field allows us to specify what type of packet this is. There are three choices:
All other values are undefined. The ack field is used by the receiver to acknowledge a packet from the sender. A value of zero indicates that this packet is not an acknowledgement, and a value of one indicates that this is an acknowledgement. All other values are undefined. Since we only have half duplex communication, this means that all packets from sender to receiver will have an ack value of 0, and all packets from receiver to sender will have an ack value of 1. The seqnum field is a 4 byte field encoding the sequence number. Be careful when putting a particular value into this field or reading a value back out. When the above structure is submitted to UDP, all fields must be in network byte order, so a value in host byte order must be converted to network byte order before the send. After a receive from UDP, the field will be in network byte order, and must be converted to host byte order before interpretation. See Chapter 3 of Donohoo and the specification of the utility routines htonl(),ntohl(), htons(), and ntohs(). The length field is a 2 byte field encoding the byte length of the subsequent data portion of the packet. Valid values are 0 through 1024. The same comments on encoding within the structure made above apply to the length field as well. Notice that there are no address fields and no port fields in the above packet format. Since we will be encapsulating this packet inside a UDP message, the address and port from UDP will serve our purpose adequately. As noted above, the data field must allow for a variable length, with just the appropriate header and data bytes going out "over the wire." You should be able to use memcpy() to copy bytes from a user specified buffer on a send operation into the appropriate place in the packet structure. Channel SetupBefore transfer of data may commence, a receiver and a sender must agree that a channel has been established. This is accomplished through an initial handshake. When a receiver is ready to accept a communication channel from a sender, it sets up a UDP receive on a particular port. The sender may then transmit a packet to that server and port with the control field set to 0x01, the ack field set to 0x00. The length field is set to 0 and (as a consequence) the data field is nonexistent. The seqnum field is set to a random sequence number generated by the sender. This is so the sender and receiver can agree on an initial sequence number used for the data transport. The sender starts a timer just before issuing the channel setup request. At the receiving side, when a channel setup request arrives, the receiver may accept by sending back an acknowledgement. This is a UDP send to the sender with a packet format in which the control field is set to 0x01, the ack is field is set to 0x01, the seqnum field is set to the seqnum proposed by the sender, the length field is 0, and the data field is nonexistent. Back at the sending side, if the timer expires prior to receipt of the acknowledgement of the channel setup request, the sender retransmits (including using the same proposed initial sequence number). This can happen up to some configurable maximum number of times before the sender gives up. If the connection setup request acknowledgement arrives, the sender may proceed into the data transfer phase. Channel TeardownWhen the sender is through sending data, the channel must be town down. This allows the sender to close things gracefully and also allows the receiver to do a proper close of its standard output so that none of the transferred data gets "lost." Channel teardown proceeds identically to the out-and-back handshake in channel setup. It can only be initiated by the sender. The sender transmits a packet to the established server with the control field set to 0x02, the ack field set to 0x00. The length field is set to 0 and (as a consequence) the data field is nonexistent. The seqnum field is set to the final sequence number attained and transmitted in the data transfer phase. The sender starts a timer just before issuing the channel teardown request. At the receiving side, when a channel teardown request arrives, the receiver must have acknowledged all data packets prior to acknowledging the channel teardown. The acknowledgement is a UDP send to the sender with a packet format in which the control field is set to 0x02, the ack is field is set to 0x01, the seqnum field is set to the final seqnum as given by the sender, the length field is 0, and the data field is nonexistent. Back at the sending side, if the timer expires prior to receipt of the acknowledgement of the channel teardown request, the sender retransmits (including using the same final sequence number). This can happen up to some configurable maximum number of times before the sender gives up. If the connection teardown request acknowledgement arrives, the sender may terminate execution. Data TransferPhase 1: Stop and Wait
Data transfer proceeds in Stop and Wait as described in section 2.5.1 of our textbook. Data carrying packets from the sender to the receiver will have a control field set to 0x03, and the ack field set to 0x00. The seqnum field on the first data-carrying packet is set to the initial sequence number, and is incremented by one for each new data transfer packet. The length field is set to the length of the payload, and the data field is populated with the user's data buffer. The sender starts a timer just before issuing the data transfer. At the receiving side, when a data transfer packet arrives, the receiver sends an acknowledgement. The acknowledgement is a UDP send to the sender with a packet format in which the control field is set to 0x03, the ack is field is set to 0x01, the seqnum field is set to the seqnum from the packet received from the sender, the length field is 0, and the data field is nonexistent. To cover the case of a lost acknowledgement resulting in a duplicate packet being received, the receiver must keep track and discard such a duplicate packet and retransmit the acknowledgement. Phase 1: Sliding WindowData transfer proceeds in the Sliding Window as described in section 2.5.2 of our textbook. Data carrying packets from the sender to the receiver will have a control field set to 0x03, and the ack field set to 0x00. The seqnum field on the first data-carrying packet is set to the initial sequence number, and is incremented by one for each new data transfer packet. The length field is set to the length of the payload, and the data field is populated with the user's data buffer. The sender starts a timer just before issuing each data transfer. On entry to the SndData interface (see service interface below) to the sliding window SndChannel, the code must decide whether it can accept the current request to send the data. This can be determined by looking at the current sender's window. If there is room in the sender's window, the SndData request should be accepted, and appropriate action taken to copy the data into a newly formed packet to be managed by the SndChannel. After accepting the message, copying it locally, and intiating a timer and UDP send of the resultant packet, the SndData should allow a return to the calling program, so that more data may be pipelined down through the SndData call. If there is not sufficient room in the sender's window for the incoming request, the SndData interface may block until further channel actions open up the sender's window.
Service InterfaceYou are free to design the service interface as you see fit. You may use C++ or C in its implementation. An object-oriented way of presenting the interface might be to have a class for each of the receive message channel (call int RcvChannel) and the send message channel (call it SndChannel). The constructor for a RcvChannel in Stop-and-Wait could include a parameter for a port at the server (receiver) side. The supported methods could include a RcvSetup(), and the RcvData(). Given simplifying assumption number 3, the latter could implement a loop receiving all data and writing it to standard output without the need for a user level loop and passing buffers, although you are welcome to do the latter if you wish. RcvData would terminate after the channel teardown handshake had completed. The constructor for a SndChannel in Stop-and-Wait could include parameters for a host (preferably a logical name, not a dotted-quad) and a port. The supported methods could include a SndSetup(), a SndData(), and a SndTeardown(). The SndData should include a pointer to a message buffer and a length. The constructor for a SndChannel in Sliding Window could also include parameters that define the size of the sender's sliding window. Likewise, the RcvChannel constructor can include parameters that define the receiver's window size. |
|
|