Java TCP-based socket packet Splitting Method

Source: Internet
Author: User

 

Recently in the socket transmission, encountered packet loss problem, troubled for a long time, saw this article, the original address: http://suwish.com/html/java-tcp-socket-stream-packet-split.html is good, now much easier. In other words, the official "empty track" animation schedule of falcom and the broken people are displayed. I mean it is really unacceptable. Of course, we can't handle this either.

 

Well, let's go to the point where the socket program was recently written. The server is Java and the client is flex. I first thought about the so-called packet splitting problem, because the packet structure was defined by myself, and I simply wrote several lines of data packet verification. The key is that there is nothing happening in the test, but some strange problems occur after the release to the Internet. The typical problem is that after a certain period of communication, the packet verification block will cause an error and then discard the packet. This is the so-called "packet loss", but my is the TCP socket, it is impossible to send or receive packets due to network problems. So I read a few articles and found that this phenomenon is called "stick package". I think it is quite appropriate. After some time of thinking and testing, I have probably understood the Principles. According to the current situation, there should be no problem. So Let's sum up here. If I find some new problems or better methods one day, I will continue to supplement this article. Of course, the predecessors who pass by think that there is an error or something. First, let's talk about TCP communication before you talk about the program. The biggest difference between TCP and UDP is that TCP maintains the connection status, which can be understood as a smooth flow channel, that is, stream. Of course, the transmission content of the stream is still byte. Let's assume that there is a pipe for water diversion. Let's wait for the arrival of water and use a container to receive the flow of water. There are several situations:
  • Assume that the water pipeline will not be broken during operation.
  • First-in-first-out, first-in-pipe water must arrive first.
  • In a certain state, the traffic (when the pipe is not broken) cannot be guaranteed, or even a certain period of time without water.
  • The size of our container (buffer zone) is fixed, that is, the amount of water received each time has a maximum value, so it cannot be received if it is too large.
In the above case, we make it a prerequisite and then return to the code. The premise that the TCP socket can communicate is that the connection is not closed, and the disconnection event can be determined from two conditions. The stream is disconnected. The read () value is-1, socketexception or ioexception. The premise of subcontracting is that the socket can communicate normally, no matter how serious the network delay is, these TCP Protocols will be processed. We only care about communication. In the current optimal condition, that is, in the laboratory environment or intranet, the latency between the server and the client is no more than one millisecond. At this time, as long as the container that receives the water is large enough, the normal communication can basically be completed. However, the Internet situation is very complex, and data packets need to go through numerous network hardware and software devices, and the delay is inevitable. However, the TCP protocol will ensure the sequence and guarantee of data packets will not be lost like the water pipeline. So what we can control at this moment is only the container that connects to the water. Check some simple TCP communication knowledge, and the network data is cached during transmission. Simply put, it means to send n Packets continuously. They may be cached and sent together. This is a sticky packet. Of course, for the receiving end, we cannot guarantee that all data packets can be received at exactly the same time. More often, there are X.5 data packets. Return to the model of water delivery. Our containers are waiting for the arrival of water. Now there is a time-out period, that is, there is a maximum time each time the water is waiting, even if we don't receive a drop of water, we have to deal with this bucket. Therefore, the water in the bucket is theoretically greater than or equal to 0 and smaller than or equal to the capacity of the bucket. I think it should be clear. From the code point of view. Now we have a byte [] buffer = new byte [max_len], that is, the data packet read buffer, int Len = connection. Read (buffer ). The read method uses buffer to read data streams. Len is the actual length of data read. The length is greater than or equal to 0 and less than or equal to max_len. If it is equal to 0, it is not processed. If it is equal to-1, the connection is disconnected. Of course, the read method throws an exception, that is, when the data is read, the connection fails. Now we get a buffer, that is, the buffer. There is available LEN Length data. What we need to do is to convert the buffer into a packet following our own protocol structure. Then it is processed by the business logic code. In this case, we define our communication protocol as a byte packet header for data legality verification. The two byte packets are long (usually 4 bytes, that is, an int ), the remaining content is a variable-length data packet body. Now we get the buffer. At this time, there are two situations: subcontracting (sticking package) and group package (packet is not received completely. It seems to be a headache, but what we need to know about obtaining packet is the actual length of the data packet, that is, the content of 2 bytes, which is converted to short and assumed to be packet_len. Then we only need to split and wait for packet_len bytes in length, which is what our class really needs. Of course, I had a misunderstanding in this process. After being instructed, I found that I followed a lot of useless things and the result increased the complexity of the Code. Now the code is written. My structure is that the server uses NiO, And the NIO framework encapsulates the buffer as Java. NiO. bytebuffer. Its underlying implementation is a fixed-length byte [], which only encapsulates shortcuts for some byte operations. Since it is encapsulated, we need to use it.
  • Bytebuffer. Remaining (), this method is the most powerful, returns the remaining available length, this length is the actual length of the data read, the most natural is the length of the underlying array. So it seems that this bytebuffer is more like a markup stream.
  • Bytebuffer. Get (byte []), read byte [] From bytebuffer.
First, use bytebuffer as a stream, that is, bytebuffer. Flip () after Read (bytebuffer (). Reset to the front-end of the stream. This Java code is written according to the original idea. It is ugly but clear. If you have time to optimize the algorithm, you should be able to write more beautifully. Public list <byte []> getpacket (bytebuffer buffer) throws exception {
Plink. Clear ();
Try {
While (buffer. Remaining ()> 0 ){
If (packetlen = 0) {// There are two situations at this time and the data packet length may have been obtained once
If (buffer. Remaining () + _ PACKET. Length <3 ){
Byte [] temp = new byte [buffer. Remaining ()];
Buffer. Get (temp );
_ PACKET = packetutil. joinbytes (_ PACKET, temp );
Break; // Save the header
} Else {If (_ PACKET. Length = 0 ){
Buffer. Get ();
Packetlen = packetutil. parserbuffer2toint (buffer );
} Else if (_ PACKET. Length = 1 ){
Packetlen = packetutil. parserbuffer2toint (buffer );
} Else if (_ PACKET. Length = 2 ){
Byte [] lenbyte = new byte [2];
Lenbyte [0] = _ PACKET [1];
Lenbyte [1] = buffer. Get ();
Packetlen = packetutil. parserbytes2toint (lenbyte );
} Else {
Packetlen = packetutil. parserbytes2toint (_ PACKET, 1 );
}

}
}

If (_ PACKET. Length <= 3) {// at this time, _ PACKET does not have useful data and all required data is in the buffer zone.
If (buffer. Remaining () <packetlen ){
_ PACKET = new byte [buffer. Remaining ()];
Buffer. Get (_ PACKET );
} Else {
Byte [] P = new byte [packetlen];
Buffer. Get (P );
Plink. Add (P );
Packetlen = 0;
_ PACKET = new byte [0];
}
} Else {
If (buffer. Remaining () + _ PACKET. Length-3 <packetlen) {// The remaining package contains less than one complete package. After saving the package, wait for a write
Byte [] temp = new byte [buffer. Remaining ()];
Buffer. Get (temp );
_ PACKET = packetutil. joinbytes (_ PACKET, temp); break;
} Else {// complete or excessive data packets
Byte [] temp = new byte [packetlen-(_ PACKET. Length-3)];
Buffer. Get (temp );
Plink. Add (packetutil. subpacket (packetutil. joinbytes (_ PACKET, temp )));
_ PACKET = new byte [0];
Packetlen = 0;
}
}
}
} Catch (exception e ){
System. Out. println (".. getpacket packetlen =" + packetlen + "_ PACKET. Length =" + _ PACKET. Length );
Throw E;
}
Return plink;
} If you do not think it is easy, you can first look at the following flex method. The idea is the same, but it looks very simple. Private function socketdatahandler (Event: progressevent): void {

Try {
While (true ){
If (packet_len = 0 ){
If (socket. bytesavailable <3) return;
VaR temp: bytearray = new bytearray ();
Socket. readbytes (temp, 0, 3 );
Packet_len = packetutil. parserbytestoint2 (temp, 1 );
}
If (socket. bytesavailable <packet_len) return;
VaR Buffer: bytearray = new bytearray ();
Socket. readbytes (buffer, 0, packet_len );
Packet_len = 0;
Buffer. Position = 0;
Packetarrive (buffer );
}

} Catch (E: Error ){
Trace (E. Message );
}
} It takes too much space to post code. First, let's talk about flex. The flex library and Flash are actually the same. The socket in flex has its own buffer, so you only need to read data on time. So we will wait for the packet length and wait for the length to wait for the bytes to be concise. However, Java is different. We cannot control the underlying buffer of Java, so we need to write something ourselves, and the buffer does not receive complete data. It is the _ PACKET in the code, which is a byte [] with a Initialization length of 0. The idea is to wait for what we need, wait until we read it, and save the incomplete ones and merge them for another judgment. Of course, such things are regular. I don't think I have found this rule yet. If I find it, the code length should be as concise as flex. The rule is really wonderful. After summing up the rule, we jumped out of the complicated and error-prone steps to focus on more important things. Just like after I got packet, I started to calculate the array index. Because it is a variable length, the content in it is also the defined variable data, so it is very painful to calculate the data index. Then I found out the rule. In short, index + = packet [Index] + N. Then we can get rid of the data structure completely. Well, it's almost like this.

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.