3.3 frame formation and resolution
The framing technology solves the problem of how the receiving end locates the beginning and end of a message.
There are two main technologies that can find the end position of a message.
· Delimiter-based): The end of a message is indicated by a unique identifier, that is, the sender adds a special byte sequence after the data is transmitted. This identifier does not appear in the transmitted data.
· Display Length: Add a fixed size field before the variable length field or message to indicate the number of bytes contained in the field or message.
A Special Condition Based on the Delimiter is that it can be used for the last message transmitted over a TCP connection: After the message is sent, the sender simply closes (shutdownoutput () or close () method). After reading the last byte of the message, the receiver will receive a stream end mark (-1 returned by the read method ), this flag indicates that the message has reached the end.
The delimiter-based method is usually used in text-encoded messages: defines a special character or string to identify the end of a message. The receiver simply scans the input information to find the demarcation sequence and returns the string of the front edge of the separator. The disadvantage of this method is that the message itself cannot contain delimiters.
The length-based method needs to know the maximum message length. The sender must first determine the message length and store the length information into an integer as the message prefix.If the message length is no more than 255 bytes, one byte is required. If the message length is less than 65535 bytes, two bytes are required.
The following is a framer interface. It has two methods. The framemsg () method is used to add frame information and output the specified message to the specified stream. nextmsg () then scans the specified stream and extracts a message from it.
Package COM. suifeng. tcpip. chapter3.framer; import Java. io. ioexception; import Java. io. outputstream; public interface framer {/*** assemble data before sending, add frame information and output the specified message to the specified stream * @ Param message * @ Param out * @ throws ioexception */void framemsg (byte [] Message, outputstream out) throws ioexception; /*** parse the data, scan the specified stream, and extract a message * @ return * @ throws ioexception */byte [] nextmsg () throws ioexception ;}
The implementation of the framer interface using delimiters is as follows:
Package COM. suifeng. tcpip. chapter3.framer; import Java. io. bytearrayoutputstream; import Java. io. eofexception; import Java. io. ioexception; import Java. io. inputstream; import Java. io. outputstream;/*** use a boundary to process and parse data * @ author suifeng **/public class delimframer implements framer {private inputstream in; private Static final int delimter = '\ n'; Public delimframer (inputstream in) {This. in = in ;}@ overridepublic Void framemsg (byte [] Message, outputstream out) throws ioexception {for (byte B: Message) {// check whether the message contains a delimiter. If yes, throw an exception if (delimter = B) {Throw new ioexception ("message contains delimiter") ;}// write the message out. write (Message); // output the frame information to the out of the stream. write (delimter); out. flush () ;}@ overridepublic byte [] nextmsg () throws ioexception {bytearrayoutputstream messagebuffer = new bytearrayoutputstream (); int nextbyte; // read from the stream Byte. It indicates that the while (nextbyte = in. Read () is encountered ())! = Delimter) {// If (nextbyte =-1) {If (messagebuffer. size () = 0) {// return NULL has been received for the message;} else {// The message has no delimiters, throw throw new eofexception ("non-empty message without delimiter") ;}// write messages without deleters to messagebuffer. write (nextbyte);} // convert to a byte array and return messagebuffer. tobytearray ();}}
The following is a Frame Generation Method Based on the length. It is applicable to messages smaller than 65535.
The sender first gives the message length, and writes the length information to two byte integers in the big-Endian order. Then, the two bytes are placed before the complete message content, write Data to the output stream together with the message.
The following is the specific code implementation:
Package COM. suifeng. tcpip. chapter3.framer; import Java. io. datainputstream; import Java. io. eofexception; import Java. io. ioexception; import Java. io. inputstream; import Java. io. outputstream; public class implements framer {public static final int max_message_length = 65535; public static final int byte_mask = 0xff; public static final int short_mask = 0xff; public static final int byte_shift = 8; private datainputstream in; Public lengthframer (inputstream in) {This. in = new datainputstream (in) ;}@ overridepublic void framemsg (byte [] Message, outputstream out) throws ioexception {If (message. length> max_message_length) {Throw new ioexception ("message too long");} // 8-bit out in height. write (message. length> byte_shift) & byte_mask); // 8-bit out lower. write (message. length & byte_mask); out. write (Message); out. flush () ;}@ overridepublic byte [] nextmsg () throws ioexception {int length = 0; try {length = in. readunsignedshort (); // two bytes} catch (eofexception e) {return NULL;} byte [] MSG = new byte [length]; In. readfully (MSG); // block wait until enough bytes return MSG are received ;}}
3.4 Java-specific Encoding
When using sockets, if both parties know that (I) communication is implemented in Java, (ii) has full control over the protocol, you can use a built-in JAVA tool such as serializable or remote Method Invocation (RMI ). RMI can call methods of different virtual machines and hide all tedious encoding and decoding details. Serialization processes the actual conversion of Java objects into byte sequences, so Java objects can be transferred in different virtual machines.