Socket Programming Practice (5) The problem and solution of--tcp sticky bag

Source: Internet
Author: User
Tags socket error

TCP sticky packet problem

Because the TCP protocol is a byte-stream-based, non-boundary transport protocol, there is a high likelihood of sticky-packet problems, which are described below.


For host A to send the M1 and M2 two each 10K data block, host B receives the data the way is indeterminate, has the following way to receive:

receive M1 First, then receive M2 (the correct way)

receive M2 First, then receive M1 (Error)

receive 20k data at once (error)

received two times, first time 15k, second 5k (Error)

received two times, first time 5k, second 15k (error)

any other possible (error)

The reason for the sticky bag production

1.SQ_SNDBUF socket itself has buffer (send buffer, accept buffer)

2,TCP transmission of the end of the MSS size limit

3, link layer also has the MTU size limit , if the packet is larger than >MTU to be fragmented at the IP layer, resulting in message segmentation.

4, TCP traffic control and congestion control, may also lead to adhesive packets

5, TCP delay transmission mechanism, etc.

The comparison between TCP and UDP on sticky packet problems

Tcp

Udp

BYTE stream

Data Report

No Boundaries

Have borders

One read by the peer does not guarantee that the message will be completely read

The number of packets received by the other party is indeterminate

Sticky Bag solution (essentially maintaining the boundaries of messages and messages at the application layer)

(1) Fixed length package

This method is not practical: if the length of the definition is too long, it will waste network bandwidth, and if the length of the definition is too short, then a message will be split into multiple, only in the application layer of TCP to increase the cost of the merger, not to mention the other layers (so I did not give a fixed-length package in the blog example, Instead, it (a less-than-perfect implementation) is put together with an example of using a custom header, which interested readers can download and view);

(2) package tail plus \ r \ n (FTP use scheme)

If the message itself contains \ r \ n characters, the boundary of the message is also not clear;

(3) Message length + message content

(4) More complex application-layer protocols

Readn/writen implementation

sockets, pipelines, and some devices (especially terminals and networks) have the following two properties:

1) The data returned by a read operation may be less than the requested data, even if it has not reached the end of the file, but this is not an error, should continue to read the device;

2) The return value of a write operation may also be less than the number of bytes specified. This may be caused by a factor, such as: Kernel buffer full ... This is not a mistake, however, and it should continue to write the rest of the data (usually, only non-blocking descriptors, or capture to a signal, only occurs when this write Midway back)

You have never seen this happen when you read and write disk files, unless the file system is running out of space, or if you are approaching the quota limit, you cannot write all the data that is required!

Typically, these features need to be considered when reading/writing a network device, pipe, or terminal. So, we have the following two functions: Readn and Writen, the function is to read/write the specified count byte data, and handle cases where the return value may be less than the required value:


/** return value Description:    = = Count: Description correctly returned, has actually written count bytes    = =-1   : Write error returned **/ssize_t writen (int fd, const void *BUF, size_t Co UNT) {    size_t nleft = count;    ssize_t nwritten = 0;    Char *pbuf = (char *) buf;    while (Nleft > 0)    {        if (Nwritten = Write (fd, PBUF, nleft)) < 0)        {            //If the write operation is interrupted by a signal, the description can also continue writing 
   if (errno = = eintr)                continue;            Otherwise it is other error else                return-1;        }        If ==0 indicates that nothing is written, you can continue to write        else if (Nwritten = = 0)            continue;        Normal Write        nleft-= Nwritten;        PBuf + = Nwritten;    }    return count;}

message length + message content practice

When transmitting text: the first four bytes length + message content is sent one time;

at the time of closing: Read the first four bytes, find out the length of the message, and read the data according to the length.

Send structure:

struct packet{    unsigned int    msglen;     The length of the data portion (network byte order)    char            text[1024];//data portion of the message};
Improved code void echo (int clientfd) {struct Packet buf for the server side Echo section;    int readbytes; First read header while ((Readbytes = Readn (clientfd, &buf.msglen, sizeof (Buf.msglen))) > 0) {//network byte sequence-host        byte order int lenhost = Ntohl (Buf.msglen);        Then read the data section readbytes = Readn (Clientfd, Buf.text, lenhost);        if (readbytes = =-1) err_exit ("READN socket Error");            else if (readbytes! = lenhost) {cerr << "client connect closed ..." << Endl;        return;        } cout << Buf.text;  Then write it back to the socket if (writen (CLIENTFD, &buf, sizeof (Buf.msglen) +lenhost) = =-1) err_exit ("Write socket        Error ");    memset (&buf, 0, sizeof (BUF));    } if (readbytes = =-1) err_exit ("Read socket error"); else if (readbytes! = sizeof (Buf.msglen)) Cerr << "Client connect closed ..." << Endl;}
Client side send and receive code ... struct Packet buf;    memset (&buf, 0, sizeof (BUF)); while (Fgets (Buf.text, sizeof (Buf.text), stdin)! = NULL) {/** write part **/unsigned int lenhost = strlen (buf.t        EXT);        Buf.msglen = htonl (lenhost);        if (writen (SOCKFD, &buf, sizeof (Buf.msglen) +lenhost) = =-1) err_exit ("writen socket Error");        /** Read part **/memset (&buf, 0, sizeof (BUF));        First read the header ssize_t readbytes = Readn (sockfd, &buf.msglen, sizeof (Buf.msglen));        if (readbytes = =-1) err_exit ("Read socket error"); else if (readbytes! = sizeof (Buf.msglen)) {cerr << "server connect closed ... \nexiting ..." <&l T            Endl        Break        }//Then read the data section lenhost = Ntohl (Buf.msglen);        Readbytes = Readn (SOCKFD, Buf.text, lenhost);        if (readbytes = =-1) err_exit ("Read socket error"); else if (readbytes! = lenhost) {cerr << "Server Connect closed \nexiting ... "<< Endl;        Break        }//Data part printout cout << Buf.text;    memset (&buf, 0, sizeof (BUF)); }...

Full implementation code:

http://download.csdn.net/detail/hanqing280441589/8460557

read by row practice

Recv/send function

ssize_t recv (int sockfd, void *buf, size_t len, int flags); ssize_t Send (int sockfd, const void *buf, size_t len, int flags );

Compared to read, recv can only be used for socket file descriptors, and one more flags

The flags parameter of the recv is commonly used to value:

Msg_oob (Out-of-band data: Data sent through the emergency pointer [required to set the TCP head emergency pointer bit valid])

This flag requests receipt of Out-of-band data , would not being received

In the normal data stream. Some Protocols Place expedited data at the head of

The normal data queue, and thus this flag cannot is used with such protocols.

Msg_peek ( can read the data, but not read from the cache [just a glimpse], the use of this feature can be easily implemented by rows to read data; a single character read, multiple calls to the system call the Read method, not high efficiency)

This flag causes the receive operation to return data from the beginning of

The receive queue without removing that data from the queue. Thus, a subsequent

receive call would return the same data.

/** Example: Encapsulates a Recv_peek function through Msg_peek (view data only, but not take away) **/ssize_t recv_peek (int sockfd, void *buf, size_t len) {while (true) {        int ret = recv (SOCKFD, buf, Len, Msg_peek);        If the recv is interrupted by a signal, you need to continue (continue) to see if (ret = =-1 && errno = = eintr) continue;    return ret;  }}/** use Recv_peek to implement read-by-row readline (socket only) **//** return value Description: = = 0: Off = =-1: Read error Other: Number of bytes in a row (contains ' \ n ') **/ssize_t    ReadLine (int sockfd, void *buf, size_t maxline) {int ret;    int nread = 0;    int returncount = 0;    Char *pbuf = (char *) buf;    int nleft = Maxline;        while (true) {ret = Recv_peek (SOCKFD, PBuf, nleft);        If the view fails or the peer shuts down, return the IF (Ret <= 0) return ret directly;        Nread = ret;            for (int i = 0; i < nread; ++i)//In the current view of the buffer containing ' \ n ', it is possible to read a line of if (pbuf[i] = = ' \ n ')           {//The buffer contents are read out//note is i+1: Will ' \ n ' also read out ret = READN (SOCKFD, PBuf, i+1);     if (ret! = i+1) exit (exit_failure);            return ret + returncount;         }//if ' \ n ' is not found in this message, then a message is not satisfied////After the message is read from the buffer, continue to view ret = READN (SOCKFD, PBuf, nread);;        if (ret! = nread) exit (exit_failure);        PBuf + = Nread;        Nleft-= nread;    Returncount + = Nread; }//If the program is able to get here, it means that there is an error return-1;}

ReadLine Realization Idea:

in the ReadLine function, we first use Recv_peek to "peep" at the number of characters in the buffer now and read to Pbuf, and then see if there is a line break ' \ n '. If present, the READN is read with a newline character (the function is equivalent to emptying the socket buffer); If it does not exist, also clear the buffer, and move the position of the pbuf, go back to the start of the while loop and see again. Note that when we call READN to read the data, that part of the buffer is emptied, because READN calls the Read function. It is also important to note that if you read ' \ n ' The second time, you first use Returncount to save the first read of the number of characters, and then return the RET need to add the original data size.

Read the Echo code by line:

void echo (int clientfd) {    char buf[512] = {0};    int readbytes;    while (readbytes = ReadLine (clientfd, buf, sizeof (BUF))) > 0)    {        cout << buf;        if (writen (CLIENTFD, buf, readbytes) = =-1)            err_exit ("writen error");        memset (buf, 0, sizeof (BUF));    }    if (readbytes = =-1)        err_exit ("ReadLine error");    else if (readbytes = = 0)        cerr << "Client connect closed ..." << Endl;}

Client-side read and send code

...    Char buf[512] = {0};    memset (buf, 0, sizeof (BUF));    while (Fgets (buf, sizeof (BUF), stdin)! = NULL)    {        if (writen (SOCKFD, buf, strlen (buf)) = =-1)            Err_exit (" writen error ");        memset (buf, 0, sizeof (BUF));        int readbytes = ReadLine (sockfd, buf, sizeof (BUF));        if (readbytes = =-1)            err_exit ("ReadLine error");        else if (readbytes = = 0)        {            cerr << "Server connect closed ..." << Endl;            break;        }        cout << buf;        memset (buf, 0, sizeof (BUF));    } ...

Complete code implementation:

http://download.csdn.net/detail/hanqing280441589/8460883

Socket Programming Practice (5) The problem and solution of--tcp sticky bag

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.