TCP/IP using Ada Sockets: How to correctly finish a packet?

994 views Asked by At

I'm attempting to implement the Remote Frame Buffer protocol using Ada's Sockets library and I'm having trouble controlling the length of the packets that I'm sending.

I'm following the RFC 6143 specification (https://tools.ietf.org/pdf/rfc6143.pdf), see comments in the code for section numbers...

          --  Section 7.1.1
          String'Write (Comms, Protocol_Version);
          Put_Line ("Server version: '"
            & Protocol_Version (1 .. 11) & "'");

          String'Read (Comms, Client_Version);
          Put_Line ("Client version: '"
            & Client_Version (1 .. 11) & "'");

          --  Section 7.1.2
          --  Server sends security types
          U8'Write (Comms, Number_Of_Security_Types);
          U8'Write (Comms, Security_Type_None);


          --  client replies by selecting a security type
          U8'Read (Comms, Client_Requested_Security_Type);
          Put_Line ("Client requested security type: "
            & Client_Requested_Security_Type'Image);

          --  Section 7.1.3
          U32'Write (Comms, Byte_Reverse (Security_Result));

          --  Section 7.3.1
          U8'Read (Comms, Client_Requested_Shared_Flag);
          Put_Line ("Client requested shared flag: "
            & Client_Requested_Shared_Flag'Image);


          Server_Init'Write (Comms, Server_Init_Rec);

The problem seems to be (according to wireshark) that my calls to the various 'Write procedures are causing bytes to queue up on the socket without getting sent.

Consequently two or more packet's worth of data are being sent as one and causing malformed packets. Sections 7.1.2 and 7.1.3 are being sent consecutively in one packet instead of being broken into two.

I had wrongly assumed that 'Reading from the socket would cause the outgoing data to be flushed out, but that does not appear to be the case.

How do I tell Ada's Sockets library "this packet is finished, send it right now"?

1

There are 1 answers

4
LoneWanderer On

To enphasize https://stackoverflow.com/users/207421/user207421 comment:

I'm not a protocols guru, but from my own experience, the usage of TCP (see RFC793) is often misunderstood.

The problem seems to be (according to wireshark) that my calls to the various 'Write procedures are causing bytes to queue up on the socket without getting sent.

Consequently two or more packet's worth of data are being sent as one and causing malformed packets. Sections 7.1.2 and 7.1.3 are being sent consecutively in one packet instead of being broken into two.

In short, TCP is not message-oriented.

Using TCP, sending/writing to socket results only append data to the TCP stream. The socket is free to send it in one exchange or several, and if you have lengthy data to send and message oriented protocol to implement on top of TCP, you may need to handle message reconstruction. Usually, an end of message special sequence of characters is added at the end of the message.

Processes transmit data by calling on the TCP and passing buffers of data as arguments. The TCP packages the data from these buffers into segments and calls on the internet module to transmit each segment to the destination TCP. The receiving TCP places the data from a segment into the receiving user's buffer and notifies the receiving user. The TCPs include control information in the segments which they use to ensure reliable ordered data transmission.

See also https://stackoverflow.com/a/11237634/7237062, quoting:

TCP is a stream-oriented connection, not message-oriented. It has no concept of a message. When you write out your serialized string, it only sees a meaningless sequence of bytes. TCP is free to break up that stream up into multiple fragments and they will be received at the client in those fragment-sized chunks. It is up to you to reconstruct the entire message on the other end.

In your scenario, one would typically send a message length prefix. This way, the client first reads the length prefix so it can then know how large the incoming message is supposed to be.

or TCP Connection Seems to Receive Incomplete Data, quoting:

The recv function can receive as little as 1 byte, you may have to call it multiple times to get your entire payload. Because of this, you need to know how much data you're expecting. Although you can signal completion by closing the connection, that's not really a good idea.

Update:

I should also mention that the send function has the same conventions as recv: you have to call it in a loop because you cannot assume that it will send all your data. While it might always work in your development environment, that's the kind of assumption that will bite you later.