Write C + + server by hand (18): method of TCP emergency transmission -- out of band data (principle and code example)

TCP's connection oriented transmission of three handshakes and four waves is specific, which essentially ensures the reliability of transmission. In addition, there are byte numbering mechanism, sliding window mechanism, timeout retransmission mechanism, selective confirmation mechanism and so on, which ensure its reliable transmission to the greatest extent. However, the advantages and disadvantages of everything depend on each other. While ensuring reliability, other characteristics must be sacrificed. Emergency data transmission is one of them. What steps need to be taken if emergency data needs to be transmitted? If it is carried out under the framework of three handshakes and four waves, byte numbering mechanism, sliding window mechanism, timeout retransmission mechanism, selective confirmation mechanism and so on, will it delay the emergency real-time and priority?

catalogue

Start with TCP fixed header structure

Necessity of emergency data

Out of band data concept

TCP out of band data transmission process

Sender

receiving end

exception handling

socket recognizes out of band data

Code example

Sender

receiving end

reference resources

Start with TCP fixed header structure

The TCP fixed header structure is shown in the following figure:

This paper focuses on the 16 bit emergency pointer, and the details of other parts can be seen: https://xduwq.blog.csdn.net/article/details/105891603

The emergency pointer, 16 bits in total, is a positive offset. It is added with the value of the sequence number field to represent the sequence number of the next byte of the last emergency data. It is used to send emergency data from the sender to the receiver.

So the question is, when should the emergency pointer be used? The following is the focus of this article!

Necessity of emergency data

TCP's connection oriented transmission of three handshakes and four waves is specific, which essentially ensures the reliability of transmission. In addition, there are byte numbering mechanism, sliding window mechanism, timeout retransmission mechanism, selective confirmation mechanism and so on, which ensure its reliable transmission to the greatest extent. However, the advantages and disadvantages of everything depend on each other. While ensuring reliability, other characteristics must be sacrificed. Emergency data transmission is one of them. What steps need to be taken if emergency data needs to be transmitted? If it is carried out under the framework of three handshakes and four waves, byte numbering mechanism, sliding window mechanism, timeout retransmission mechanism, selective confirmation mechanism and so on, will it delay the emergency real-time and priority? So, out of band data comes out!

Out of band data concept

Out Of Band is a concept corresponding to in band data (ordinary data), which is used to quickly notify each other of important events. Out Of Band data has higher priority than in band data. It will be sent immediately, regardless of whether there is ordinary data queued for transmission in the transmission buffer. The transmission of Out Of Band data can use an independent transport layer connection or can be mapped to the connection for transmitting ordinary data. Only Telnet, FTP and other inactive programs can use it.

Note: neither TCP nor UDP has out of band data, but as mentioned above, the emergency pointer and emergency pointer identification bit in the TCP fixed header structure provide an emergency mode for the application layer. Therefore, TCP emergency data is generally called out of band data.

TCP out of band data transmission process

Sender

Set the URG flag on the header of the TCP message to be sent, and the emergency pointer is set to point to the next byte of the last out of band data (the emergency offset value obtained by further subtracting the current TCP message segment value), so only the last byte of the multi byte out of band data sent by the sender at one time is regarded as out of band data, for example, If you want to send out of band data "abcdef", only the last byte of data "f" will be regarded as out of band data, and other data will be regarded as ordinary data.

If more than one TCP message segment sends the contents in the buffer, each TCP message segment will be set to the URG flag, and the emergency pointer will point to the same location. However, only one TCP segment pointer carries the "out of band data of the last byte".

Even if the sending end TCP suspends sending data due to flow control (the socket receiving buffer of the receiving buffer is full, resulting in its TCP announcing a window with a value of 0 to the sending end), the emergency notification is still not accompanied by the sending of any data. In other words, even if the data flow will stop due to TCP flow control, the emergency notification is always sent to the opposite TCP without obstacles.

receiving end

Only when the emergency pointer flag is received, the receiving end will check the emergency pointer, determine the position of out of band data according to the position indicated by the emergency pointer, and read it into a special cache. This cache has only 1 byte, which is called out of band cache.

When the actual data byte pointed by the emergency pointer reaches the receiving end TCP, the data byte will have two storage areas: one is the same online retention as ordinary data, and the other is an independent single byte out of band buffer. The only way for the receiving process to read data from this single byte out of band buffer is to specify MSG_OOB calls recv, recvfrom, recvmsg. If it is placed in the in band area with normal data, the receiving process has to check the out of band flag OOB of the connection to know when to access the data byte. The use of the two regions is through the socket option SO_OOBLINE. By default, out of band data bytes are placed in a separate single byte out of band buffer.

exception handling

  1. If the process requests to read data (through MSG_OOB flag), but the opposite end does not send any out of band data, the read operation will return EINVAL.

  2. On the premise that the receiving process has been informed that the opposite end has sent an out of band byte (SIGURG and select), if the receiving process attempts to read the byte, but the byte has not yet arrived, the read operation returns EWOULDBLOCK. What the receiving process does at this time is to read data from the buffer and make room to allow the peer TCP to send the out of band byte.

  3. If the accepting process attempts to read the same out of band byte multiple times, the read operation returns EINVAL.

  4. If so is turned on_ Oobinline socket option, if the process is still accepted through MSG_OOB reads out of band data, and the read operation will return EINVAL.

socket recognizes out of band data

When the Linux kernel detects the TCP emergency flag, it will notify the application to receive out of band data. The common way of kernel notification is that I/O multiplexing generates abnormal events and SIGRG signals. However, even if the application gets the notification that out of band data needs to be received, it also needs to know the specific location of out of band data in the data stream.

See notes for specific methods:

#include <sys/stocket.h>
// Judge whether the sockfd is in the out of band flag, that is, whether the next read data is out of band data
// sockatmark returns 1 if yes, and 0 if No
int sockatmark(int sockfd);

Code example

In the API of TCP data sending and receiving, flags provides flag bit MSG_OOB is used to send and receive emergency data. Next, use the above knowledge points to write a demo with my data sending and receiving.

Sender

#include <sys/socket.h>
#include <assert.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[]) {

    if (argc != 3) {
        cout << "input parameter error!" << endl;
        return 1;
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in server_address;
    bzero(&server_address, sizeof(server_address));
    server_address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &server_address.sin_addr);
    server_address.sin_port = htons(port);

    int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(sockfd >=0);
    if (connect(sockfd, (struct sockaddr*)&
                server_address, sizeof(server_address)) < 0) {
        cout << "connect fail!" << endl;
    } else {
        const char* oob_data = "this is oob data";
        const char* normal_data = "this is normal data";
        send(sockfd, normal_data, strlen(normal_data), 0);
        send(sockfd, oob_data, strlen(oob_data), MSG_OOB);
    }

    close(sockfd);
    return 0;
}

receiving end

#include <sys/socket.h>
#include <assert.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

using namespace std;

const int BUF_SIZE = 1024;

int main (int argc, char* argv[]) {

    if (argc <= 2) {
        cout << "input parameter error!" << endl;
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >=0);

    int ret = bind(sock, (struct sockaddr*)& address, sizeof(address));
    assert(ret >= 0);

    struct sockaddr_in client;
    socklen_t client_addrlen = sizeof(client);
    int confd = accept(sock, (struct sockaddr*)& client, &client_addrlen);
    if (confd < 0) {
        cout << "error! " << errno << endl;
    } else {
        char buffer[BUF_SIZE];

        memset(buffer, '\0', BUF_SIZE);
        ret = recv(confd, buffer, BUF_SIZE - 1, 0);
        cout << "normal data byte is " << ret << "buffer is " << buffer <<endl;

        memset(buffer, '\0', BUF_SIZE);
        ret = recv(confd, buffer, BUF_SIZE - 1, MSG_OOB);
        cout << "oob data byte is " << ret << "buffer is " << buffer <<endl;
    }

    close(sock);
    return 0;
}

reference resources

Added by erwt on Mon, 10 Jan 2022 20:46:56 +0200