Discussion on protocol design of Application layer (based on TCP/UDP) in network programming

Source: Internet
Author: User
Tags json message queue
For the first network programming developers, the communication protocol design is generally confused. This aspect is also less relevant in general web-programming books. Guess it's too easy. This piece is really not difficult, but if you don't know it, it's easy to get out of a basket or detour. Let's talk about protocol design based on TCP/UDP.
1, TCP-based protocol design
TCP is a stream-based protocol. But most Web applications typically have a smaller processing unit, which we call frame.
Whether the frame is divided as described above, most network applications need to be framed. For example, the user login is a frame, the user sends the text message is a frame. A few applications can not need to sub-frame, such as: Echo Server, receive what direct reply can be, the forwarding server, also receives the data directly to the target machine, the more common situation is a TCP connection only send/process a request after the direct shutdown, this also no need to sub-frame.
Considering that in addition to learning Network programming, no one does echo server. So as long as the server is not a connection processing only one request, or pure forwarding, you should adopt the design of a frame.
How to Frame: frame is the unit of business processing, is the specific application of care, but this is not a matter of TCP. Beginners often think TCP this side write once, TCP that end will read once, and then exclaimed "Sticky Packet", "Lost Packet", in fact, this is the process of improper processing. Recommend a book here "TCP/IP protocol detailed volume 1", very thin, read can reduce a lot of TCP error understanding. In fact, the sender sends a frame, the receiver may have to n times to read the completion, and may read the next frame of data at the same time. How to get a frame of data in the receiver to read a little more than a lot of it.
There are two common practices: length-based and Terminator-based (Delimiter). Based on the length, the length of the frame is sent before the frame, usually with a fixed length of bytes to send this length, such as 2 bytes (maximum frame length cannot be greater than 65535), 4 bytes. (PS: I have also seen the use of variable-length bytes to send this length, such as Netty in the Protobufvarint32framedecoder, see the code that is quite the egg pain, I feel completely tossing themselves, strongly not recommended. Using length-based framing, the receiver process typically reads: "Read fixed-length bytes, parse out frame length, read frame long bytes, and process frames."
Based on Terminator (Delimiter), the most typical application is the HTTP protocol, using/r/n/r/n as the Terminator. Using Terminator-based framing, the receiver's processing flow is generally the same: "read data, positioning in the read data terminator, not found, the data cache, continue reading data, locate Terminator, locate Terminator, processing the data before the Terminator as a frame ”。
It is important to consider escaping when using terminator, otherwise there will be terminator in the frame's data, the fun is big.
Note that regardless of the approach, you need to consider the problem of maximum frame length when developing. Otherwise, if the other person says to send a 4G length frame (malicious or program error), really go to the new 4G byte cache, or the other side has been sending data, no terminator. can cause the program to run out of memory.
In general, the length-based framing method. Development is simpler, program execution is more efficient and more widely used. Based on Terminator is also not useless: readability is better, easy to simulate and test (such as with Telnet). The following is a discussion of the length-based framing approach. Length-based frame design (length based frame designs) in general, we will divide frames into frames (frame headers, usually fixed lengths) and frame bodies (frame body, usually variable-length, fixed-length). As mentioned above, the simplest frame header is a single field-long frame. In practical applications, however, a typical frame header might have the following fields:
A) message type: In a network application, there are often multiple types of frames. For example IM, have login/logout/Send Message/.... The receiver needs to decode the different kinds of messages according to the Message Type field of the frame header and give it to the corresponding processing module for processing. That is, the structure of the frame is length-type-message,length-type can be regarded as the frame head, Message is the frame body. The message type is usually fixed length, such as length 4 bytes, type 4 bytes, then the length of the frame header is 8 bytes. Receiver processing flow: "Read frame header length byte data-decode frame header get length and message type-read frame body length byte data-decode message based on message type-process message". The frame design of the length-type-message structure is the most widely used, best and most streamlined design.
b) Request serial number (serials): This is not a required option, but I think for the non-ECHO service (Echo service: Always the client sends the request-the server responds to the request, the response is guaranteed strictly in order of request), plus this field certainly does not regret. This allows for the execution of a disorderly order (if there is a thread pool in the message queue, which is normal) to be able to and request the number, thus making the correct processing. In general, high-performance service-side to ensure strict and orderly response, is more cumbersome and impact performance.
c) Version number: A lot of people use it, but I think it's not a good idea in most cases. The frame header should place the fields required for most/all frames. and the version number may be only a few packages such as login will be used, so it may be more appropriate to put in the login package body. Maintenance of the version of each protocol will be relatively large workload, the development will be more cumbersome and error-prone. The better way to worry about decoding failures is to use a backwards-compatible codec scheme like PROTOBUF.
Note: The frame header should be designed to be as thin and versatile as possible, because the frame header length is the extra overhead required for each frame. If a field (such as a serial number) is used only by a few frames, it can be placed entirely in the frame body. Conversely, if a field most of the package has, but not defined in the Baotou, it will be difficult to unify processing, increase the development effort. These need to be weighed against specific business needs, without a unified answer. For example, the length-type-message structure is suitable for most situations, but if the business requires each frame to indicate the operator, adding the UID field to the Length-type-uid-message in the frame header will make it easier to develop the program.
Design of frame body
The frame body is a collection of fields, for example, the login frame body contains the user name, password two fields (just for example, the actual login package is often more complex). In the frame body design, we are often also eight immortals crossing recount. For example, based on the XML, JSON, based on the field POS (for example, the login package, the first write/read the user name, and then write/read the password. This is not very good, it is difficult to backward-compatible: for example, the login package needs to add a user state between the user name and password, if the server/client does not sync upgrade, will be Sparta). I've even seen the wild outrageous direct use of C-struct, this brain-like explosion: Compatibility slag does not say, class alignment (can be used pragma pack to avoid inconsistencies), byte order, machine word length will cause trouble.
More recommended Practice: Sao years, with Google Protobuf Bar. JSON is more bandwidth-saving than XML if it is to be readable.
2. UDP-based protocol design
In general, UDP servers are much simpler than TCP (but if you want to implement a reliable UDP-based message transfer, I'm not saying it). And UDP is a packet-based protocol. Write/read can be one-to-one (regardless of packet loss), so there is no need for a length field/terminator.
But note: In order to avoid high packet loss rate, the UDP packet length should not be greater than 1500 bytes (presumably, for security reasons, I generally guarantee less than 1K hey), if the volume of data is large, you need to subcontract, which is more than the TCP trouble place.
The typical UDP protocol design is: Type-message. The type length is fixed, which is used to describe the message type, the message is the body, and the frame body design of TCP is the same.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.