Recently I have been doing middleware related things, so I have come into contact with a variety of protocols, generally SPEAKING, TCP, UDP, HTTP and other network transmission protocols, so I want to start from the most basic PROTOCOL TCP sticky packet problem, the computer network this part of the foundation of a solid.
This section describes TCP
TCP is a connection-oriented transport layer protocol
In simple terms, a TCP connection must be established before TCP can be used, which is often referred to as the three-way handshake. After data transfer is complete, the established TCP connection must be released. Otherwise, unexpected problems may occur, causing service unavailability.
Every TCP connection is a reliable connection and has only two endpoints
The TCP connection is point-to-point from the Server to the Client. Data is transmitted through TCP without error, repetition, and loss.
TCP communication is full-duplex
TCP allows applications on both sides of the communication to send data at any time. Both ends of the TCP connection are equipped with send buffer and receive buffer to temporarily store bidirectional communication data. When sending data, the application sends the data to TCP’s buffer and then does its own thing, while TCP sends the data out when appropriate. Upon receiving, TCP puts the received data into the receive buffer, and the upper layer application reads the data when appropriate.
TCP is byte stream oriented
A stream in TCP is a sequence of bytes flowing into or out of a process. Therefore, TCP communication to Java, Golang and other high-level languages requires the corresponding entity serialization before transmission. Also, when we use Redis to cache, we need to serialize the data into Redis, because the bottom layer of Redis is the TCP protocol.
TCP does not know the meaning of the byte stream being transmitted, and TCP does not guarantee that the block of data sent by the receiver application and the sender application will have a corresponding size relationship (this is the sticky packet problem during TCP transmission). But the application receiver ends up receiving the same byte stream that the sender sends. Therefore, when using TCP, we should formulate a reasonable sticky packet unpacking policy.
The following figure shows the whole process of TCP protocol transmission:
The graph below is taken fromOld moneyIt’s very vivid
TCP transport GIF
TCP sticky packets reappear
The theory of scrutiny
As shown in the figure below, there are three types of sticky bag problems
TCP sticky packet problem
The first kind of circumstance: as shown in the bar above the first, the service side totally read two packets, each packet is complete, and stuck package problem does not occur, this situation is better, the server simply buffer to read from the network, each time the server read the news is completed, incorrect data will not happen.
The second case: the server only receive a packet, the packet contains two messages from the client the complete information, this time the logic implementation based on the first case received at the service side, because the server cannot handle the packet, can’t even handle, this kind of situation is TCP package problem.
The third case: Server received two packets, the first packet contains only part of the first message, the first of the rest of the message and the second message is in the second packet, or is the first packet contains the first message of complete information and the second part of the message information, the second packet contains the remaining part of the second message, This is a TCP unpacking problem, because a message is split between two packets, and again the server logic above does not handle this situation well.
Why does TCP packet sticking and unpacking occur
-
Unpacking occurs when the application writes data larger than the socket buffer size.
-
Applications write data that is smaller than the socket buffer size. The network adapter sends data that has been written more than once to the network. Sticky packets occur.
-
TCP fragmentation is performed for MSS (Maximum packet Length). When the TCP packet length -TCP header length is greater than MSS, packet disassembly occurs.
-
The receiving method does not read socket buffer data in a timely manner, and sticky packets occur.
How to deal with sticking and unpacking
There are some common methods:
-
Using a protocol with a message header, the message header stores the start identifier and the length of the message. When the server gets the message header, it resolves the length of the message and then reads the content of the length backwards.
-
Set a fixed length message, the server each time read a fixed length of content as a complete message, when the message is not long enough to fill the fixed character space.
-
To set message boundaries, the server separates the message content from the network flow by message editing, typically using ‘\n’.
-
More complex protocols, such as the Internet of vehicles (iot) protocol 808,809.
TCP sticky packet unpacking code practice
The following code mainly demonstrates the use of a specified message header, message body to solve the TCP sticky packet, unpack the problem.
Server-side code: The main logic of server-side code is to receive the message sent from the client, reassemble the message, and print it out.
import java.io.*; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; /** * @author wuhf * @Date 2018/7/16 15:50 **/ public class TestSocketServer { public static void main(String args[]) { ServerSocket serverSocket; try { serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress(8089)); while (true) { Socket socket = serverSocket.accept(); new ReceiveThread(socket).start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static class ReceiveThread extends Thread { public static final int PACKET_HEAD_LENGTH = 2; // Header length private Socket Socket; private volatile byte[] bytes = new byte[0]; public ReceiveThread(Socket socket) { this.socket = socket; } public byte[] mergebyte(byte[] a, byte[] b, int begin, int end) { byte[] add = new byte[a.length + end - begin]; int i = 0; for (i = 0; i < a.length; i++) { add[i] = a[i]; } for (int k = begin; k < end; k++, i++) { add[i] = b[k]; } return add; } @Override public void run() { int count = 0; while (true) { try { InputStream reader = socket.getInputStream(); if (bytes.length < PACKET_HEAD_LENGTH) { byte[] head = new byte[PACKET_HEAD_LENGTH - bytes.length]; int couter = reader.read(head); if (couter < 0) { continue; } bytes = mergebyte(bytes, head, 0, couter); if (couter < PACKET_HEAD_LENGTH) { continue; Byte [] temp = new byte[0]; temp = mergebyte(temp, bytes, 0, PACKET_HEAD_LENGTH); String templength = new String(temp); int bodylength = Integer.parseInt(templength); If (bytes.length - PACKET_HEAD_LENGTH < bodylength) {byte[] body = new byte[bodyLength + PACKET_HEAD_LENGTH - bytes.length]; Int couter = reader.read(body); if (couter < 0) { continue; } bytes = mergebyte(bytes, body, 0, couter); if (couter < body.length) { continue; } } byte[] body = new byte[0]; body = mergebyte(body, bytes, PACKET_HEAD_LENGTH, bytes.length); count++; System.out.println("server receive body: " + count + new String(body)); bytes = new byte[0]; } catch (Exception e) { e.printStackTrace(); } } } } }Copy the code
Client-side code: The main logic of the client-side code is to assemble the message to be sent, determine the message header, the message body, and then send to the server.
import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; /** * @author wuhf * @Date 2018/7/16 15:45 **/ public class TestSocketClient { public static void main(String args[]) throws IOException { Socket clientSocket = new Socket(); clientSocket.connect(new InetSocketAddress(8089)); new SendThread(clientSocket).start(); } static class SendThread extends Thread { Socket socket; PrintWriter printWriter = null; public SendThread(Socket socket) { this.socket = socket; try { printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }} @override public void run() {String message = "HelloWorld! from clientsocket this is test half packages!" ; for (int i = 0; i < 100; i++) { sendPacket(reqMessage); } if (socket ! = null) { try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void sendPacket(String message) { try { OutputStream writer = socket.getOutputStream(); writer.write(message.getBytes()); writer.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }}}}Copy the code
summary
Recently has been writing some of the framework of the blog, specifically for some problems of the original rational technology discussion of the blog is still relatively few, so the building Lord thinking about how to learn things in their own at the same time can also give together in the technology of this wild road on the small partners some inspiration, is the building Lord has been trying to direction.
Refer to the article
- Netty: TCP sticky packet unpacking problem
- Computer Networks – Xie Xiren – 7th edition
Author: the original haifeiWu link: www.hchstudio.cn/article/201… Copyright notice: all non-special notices are original works of this website, please indicate the author and original link when reprinting.