SOCKET Packet and unpacking

Source: Internet
Author: User
Tags error handling memory usage pack socket
origin:http://blog.csdn.net/e_wsq/article/details/12835457
For the TCP-based development of the communication program, there is a very important problem to be solved, that is, the packet and unpacking. Since I've been working on network communication programming (about three years), I've been thinking about and improving the way that packets and unpacking are addressed. Here's what I think about this question. If it's wrong, The wrong place, begged everyone to correct. Thank you first.


I. Why TCP-based communication programs require packets and unpacking.

TCP is a "flow" protocol, the so-called flow, is a string of data without bounds. You can think of the river water, is connected into a piece, in the meantime there is no dividing line. However, the general communication program development needs to define a separate packet, such as a data packet for logging in, for logging off the packet. Because the TCP "stream "and the network condition, there are several situations in which data transfer occurs.
Suppose we call two times in a row send two pieces of data data1 and data2, at the receiving end there are the following kinds of reception (of course, not only these cases, here are only a representative case).
A. Receive the DATA1 first, then receive the DATA2.
B. Receive some of the DATA1 data first, then receive the remainder of data1 and all of the data2.
C. Received all the data of data1 and some data of data1, then received the remaining data of data2.
D. All data of data1 and Data2 are received at once.

For a This situation is exactly what we need, no more discussion. For b,c,d the situation is that we often say "sticky bag", we need to take the received data to be dismantled, split into a separate packet. In order to split the package, the packet must be marshaled on the sending side.

Another: For UDP, there is no problem of unpacking, because UDP is a "packet" protocol, that is, two of the data is bounded, at the receiving end of either receive data or receive a complete piece of data, no less receive or receive more.

Two. Why there is a b.c.d situation. The
Sticky pack can occur on the sending side and can occur at the receiving end.
1. Sticky packets from the sending end caused by the Nagle algorithm: The Nagle algorithm is a kind of algorithm to improve the network transmission efficiency. Simply put, when we submit a piece of data to TCP for sending, TCP does not send this data immediately, but instead waits for a short period of time to see if there is any data to be sent during the wait. If any, these two pieces of data will be sent out at once. This is a simple explanation of the Nagle algorithm, please read the relevant books. The case of C and D is probably caused by the Nagle algorithm.
        2. Receive-side sticky packets received by the receiving side are not timely: TCP will present the received data in its own buffer, Then notify the application tier to fetch the data. When the application layer is unable to get the TCP data out in time for some reason, it will cause a few pieces of data to be stored in the TCP buffer.

Three. How to package and unpacking.
    When I first encountered a "sticky pack" problem, I slept for a short period of time by calling sleep between two calls. The disadvantage of this solution is obvious, so that the transmission efficiency is greatly reduced, And it's unreliable. Later, it is solved by means of answer, although it is feasible most of the time, but it does not solve the situation like B, and it increases the traffic volume by means of response, which aggravates the network load. Later, packets are marshaled and the packets are split.
     Packet:
Packet is to give a piece of data with Baotou, so that the packet is divided into Baotou and the package two parts of the content (after the packet filter illegal packets will be added "packet tail" content). Baotou is actually a fixed-size structure, There is a struct member variable that represents the length of the package body, which is a very important variable, other members of the struct can be defined according to their own needs. According to the length of Baotou and the variable containing the length of the packet in the Baotou, a complete packet can be split correctly.
     for unpacking I am most commonly used in the following two ways.
    1. Dynamic buffer staging mode. The reason the buffer is dynamic is because the buffer length is increased when the length of the data that needs to be buffered exceeds the length of the buffer. The
     approximate process is described as follows:
    a, dynamically allocating a buffer for each connection, associating this buffer with the socket, The common use is through struct-body correlation.
    b, when data is received, the data is first stored in the buffer.
    c to determine whether the length of the data in the buffer is sufficient for the length of a header, if not enough, to remove the packet.
    d, according to the Baotou data to resolve the inside represents the length of the package variable.
    e, to determine whether the buffer in addition to the length of the packet outside the Baotou is enough to the length of a package, if not enough, do not do the unpacking operation.
    f the entire packet. The "fetch" here means not only copying the packets from the buffer, but also removing the packet from the cache. The way to delete this packet is to move the data behind the package to the start address of the buffer.

There are two drawbacks to this approach. 1. Dynamically allocating a buffer for each connection increases memory usage. 2. Three places need to copy data, one place is to store the data in the buffer, one place is to take the complete packet out of the buffer, One place is to remove the packet from the buffer. The second method of unpacking solves and perfects these shortcomings.

The relevant code is given below.

The disadvantages of this approach are mentioned earlier. The following is an improved method, that is, the use of ring buffer. But this improvement does not solve the first disadvantage and the first copy of the data, only the third place to solve the copy of the data (this place is the most copied data place). The 2nd way of unpacking will solve both problems.
The loop buffer implementation is defined by defining two pointers, pointing to the header and tail of the valid data, respectively. When you store data and delete data, only the head and tail pointers are moved.
Code to illustrate. Note: The following code is the code that takes an open source game server, and I have modified this code.

2. Using the underlying buffer to split the package
Because TCP also maintains a buffer, we can use the TCP buffer to cache our data, so we do not need to allocate a buffer for each connection. On the other hand we know that recv or WSARecv have a parameter, Used to indicate how long we are going to receive data. With these two conditions, we can optimize the first method.
For blocking sockets, we can use a loop to receive packet length data, then parse out the variable representing the length of the package, and then use a loop to receive the packet length data.
The relevant code is as follows:

Char packagehead[1024];
Char packagecontext[1024*20];

int Len;
Package_head *ppackagehead;
while (M_bclose = = False)
{
memset (packagehead,0,sizeof (Package_head));
Len = M_tcpsock.receivesize ((char*) packagehead,sizeof (Package_head));
if (len = = socket_error)
{
Break
}
if (len = = 0)
{
Break
}
Ppackagehead = (Package_head *) Packagehead;
memset (packagecontext,0,sizeof (Packagecontext));
if (ppackagehead->ndatalen>0)
{
Len = M_tcpsock.receivesize ((char*) packagecontext,ppackagehead->ndatalen);
}
}

M_tcpsock is a variable of the class that encapsulates the socket, where the receivesize is used to receive a certain length of data until a certain length of data is received or a network error is

Return.

int Winsocket::receivesize (char* strdata, int ilen)
{
if (strdata = = NULL)
return err_badparam;
char *p = strdata;
int len = Ilen;
int ret = 0;
int returnlen = 0;
while (Len > 0)
{
ret = recv (M_hsocket, p+ (Ilen-len), Ilen-returnlen, 0);
if (ret = = Socket_error | | ret = = 0)
{

return ret;
}

Len-= ret;
Returnlen + = ret;
}

return Returnlen;
}
For a non-blocking socket, such as the completion port, we can submit a request to receive packet length data, when the GetQueuedCompletionStatus returns, we determine whether the length of the received data is equal to the length of the header, if equal to, then submit the packet length of the request to receive data, If not equal, the request to receive the remaining data is submitted. A similar approach is used when receiving the package body.
The relevant code is given below

Enum Iotype
{
Ioinitialize,
Ioread,
Iowrite,
Ioidle
};

Class Overlappedplus
{
Public
Overlappedm_ol;
Iotypem_iotype;
BOOL m_bispackagehead;//Whether the data currently received is header data.

int m_count;
Wsabuf M_wsabuffer;
int m_recvpos;
Char m_buffer[1024*8];//this buffer to be as large as possible

Overlappedplus (Iotype iotype) {
ZeroMemory (this, sizeof (Overlappedplus));
M_iotype = Iotype;
}
};
Receives the first request issued after the connection, requests to receive the packet header size data.
Overlappedplus*poverlappedplus = new Overlappedplus;
Poverlappedplus->m_wsabuffer.buf = poverlappedplus->m_buffer;
Poverlappedplus->m_wsabuffer.len = length of package_head_len;///header
Poverlappedplus->m_bispackagehead = true;
Poverlappedplus->m_recvpos = 0;
Poverlappedplus->m_iotype = Ioread;


DWORD recvbytes;
DWORD Flags;
Flags = 0;
if (WSARecv (Clientsocket, & (Poverlappedplus->m_wsabuffer), 1, &recvbytes, &flags,
&poverlappedplus->m_ol, NULL) = = Socket_error)
{
if (WSAGetLastError ()! = error_io_pending)
{
Delete Poverlappedplus;
}
Else
{
Related error handling

}
}
Else
{
Related error handling

}


In the function where GetQueuedCompletionStatus is located.
if (poverlapplus->m_iotype== ioread)
{
if (Poverlapplus->m_wsabuffer.len = = dwiosize)
{
if (Poverlapplus->m_bispackagehead = = true)///Received is Baotou.
{
Package_head *ppackagehead = (Package_head *) (Poverlapplus->m_buffer);

if (Pthis->islegalitypackagehead (ppackagehead) ==false)///Determine if the package is legal
{
Closesocket (Lpclientcontext->m_socket);
Continue
}

Poverlapplus->m_bispackagehead = false;
Poverlapplus->m_wsabuffer.len = ppackagehead->ndatalen;
Poverlapplus->m_recvpos + = Dwiosize;
Poverlapplus->m_wsabuffer.buf = poverlapplus->m_buffer+poverlapplus->m_recvpos;

}
Else///received is the package body
{

Poverlapplus->m_recvpos + = Dwiosize;
In this case, a complete packet is stored in the Poverlapplus->m_buffer, with a length of poverlapplus->m_recvpos


Continue request packet header for next packet
Poverlapplus->m_wsabuffer.buf = poverlapplus->m_buffer;
memset (poverlapplus->m_buffer,0,sizeof (Poverlapplus->m_buffer));
Poverlapplus->m_wsabuffer.len = Package_head_len;
Poverlapplus->m_bispackagehead = true;
Poverlapplus->m_recvpos = 0;

}
}
The data received by else///is not complete yet
{
Poverlapplus->m_wsabuffer.len-= dwiosize;
Poverlapplus->m_recvpos + = Dwiosize;
Poverlapplus->m_wsabuffer.buf = poverlapplus->m_buffer+poverlapplus->m_recvpos;
}
Poverlapplus->m_iotype = Ioread;
State = WSARecv (Lpclientcontext->m_socket, & (Poverlapplus->m_wsabuffer), 1, &recvbytes, &Flags,
&poverlapplus->m_ol, NULL);
if (state = = Socket_error)
{
if (WSAGetLastError ()! = error_io_pending)
{

Close the socket to release the appropriate resource
Continue
}
}

}

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.