Both write() and send() can send and receive data, what's the difference?
  • send can use flags to specify optional information, where 0 means the default send behavior
  • send When flags is 0, it will wait for the send buffer data to be emptied before putting the data into the send buffer and then return
  • write cannot specify optional information and does not block
Both read() and recv() can receive data, what's the difference?
  • recv can use flags to specify optional information, where 0 means the default receive behavior
  • recv When flags is 0, it will wait for the receive buffer to have data before taking the data out of the receive buffer and return
  • read cannot specify optional information and does not block

Data sending and receiving options

 #include <sys/socket.h>

ssize_t send(int socketfd, const void *buf, size_t nbytes, int flags);
ssize_t recv(int socketfd, void *buf, size_t nbytes, int flags);

flags - specify optional information when sending and receiving data, where 0 is the default sending and receiving behavior

flags option information (partial)

optional meaning send recv
MSG_OOB Used to transmit out-of-band data (Out Of Band Data), that is: urgent data (priority transmission)
MSG_PEEK Verify that there is data in the receive buffer (what data is there)
MSG_DONTROUTE The data transmission process does not go through the routing table, but finds the destination in the local area network
MSG_DONTWAIT Non-blocking mode, return immediately when data is sent and received
MSG_WAITALL Do not return early until all requested data has been received
MSG_MORE There is more data to send, instructing the kernel to wait for data
... ...
Note: 不同的操作系统对上述可选项的支持不同,实际工程开发时,需要事先对目标系统中支持的可选项进行调研

MSG_OOB (out-of-band data, urgent data)

native definition
  • Data transmitted independently using a different channel from normal data
  • The priority of out-of-band data is higher than that of ordinary data (prioritized transmission, the peer receives priority)
Out-of-band data in TCP
  • Due to native design limitations, TCP无法提供真正意义上的带外数据
  • In TCP, urgent data can only be transmitted through the flag in the header of the transport protocol, and 长度仅1字节

TCP out-of-band data implementation principle

image.png

The URG pointer points to the next position of the urgent message, that is: the previous byte of the position pointed to by the URG pointer stores the urgent message
 接收端优先接收紧急数据,并将其存储到特殊缓冲区,之后再接收普通数据

紧急数据:0x03
普通数据:0x01,0x02

TCP Out-of-Band Data Handling Policy

  • Since TCP is designed to stream data, true out-of-band data is not possible
  • Marked urgent data can be received early, into a special buffer (only one byte)

    • There is at most one urgent data per TCP packet
    • The special buffer only stores the latest urgent data (if not received in time, it will be lost)

What happens when sending and receiving data in the following way

Send normal data, receive in normal mode: normal, data arrive in sequence Send normal data, receive in emergency mode: return an error to send urgent data, receive in normal mode: block normal data recv to send urgent data, receive in urgent mode: normal, receive urgent data data

Programming experiment: sending and receiving TCP urgent data

client.c
 #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = {0};
    struct sockaddr_in addr = {0};
    int len = 0;
    char *test = "Delpin-Tang";

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    len = send(sock, test, strlen(test), MSG_OOB);

    getchar();

    close(sock);

    return 0;
}
server.c
 #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    while (1) {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if (client == -1) {
            printf("client accept error");
            return -1;
        }

        printf("client: %d\n", client);

        do {
            r = recv(client, buf, sizeof(buf), MSG_OOB);

            if (r > 0) {
                buf[r] = 0;
                printf("OOB: %s\n", buf);
            }

            r = recv(client, buf, sizeof(buf), 0);

            if (r > 0) {
                buf[r] = 0;
                printf("NORMAL: %s\n", buf);
            }
        }while (r > 0);

        close(client);
    }

    close(server);

    return 0;
}
output:
 server start success
client: 4
NORMAL: Delpin-Tan   // 注意,普通数据先输出 (因为当 flags 为 MSG_OOB 时不阻塞,而为 0 时会阻塞,直到接收到数据)
OOB: g               // 注意,仅输出最后一个字符 !!
Small question: In actual development, how to efficiently receive TCP urgent data?

Use select to receive urgent data

When normal data and urgent data are received on the socket, select will return immediately
  • Ordinary data: the socket is in a data readable state (can read ordinary data)
  • Urgent data: the socket handles abnormal status (emergency data can be read)

Example of emergency data reception

 num = select(max + 1, &temp, 0, &except, &timeout);

if (num > 0) {
    for (i=1; i<=max; ++i) {
        if (FD_ISSET(i, &except)) {
            if (i != server) {
                char buf[32] = {0};
                int r = recv(i, buf, sizeof(buf), MSG_OOB);
                if (r > 0) {
                    buf[r] = 0;
                    printf("OOB: %s\n", buf);
                }
            }
        }

        if (FD_ISSET(I, &temp)) {
            // ...
        }
    }
}

Programming Experiment: Using select to Receive Urgent Data

client.c
 #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = {0};
    struct sockaddr_in addr = {0};
    int len = 0;
    char *test = "Delpin-Tang";

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    len = send(sock, test, strlen(test), MSG_OOB);

    getchar();

    close(sock);

    return 0;
}
select-server.c
 #include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int server_handler(int server) 
{
    struct sockaddr_in addr = {0};
    socklen_t asize = sizeof(addr);
    return accept(server, (struct sockaddr*)&addr, &asize);
}

int client_handler(int client) 
{
    char buf[32] = {0};

    int ret = recv(client, buf, sizeof(buf) - 1, 0);

    if (ret > 0) {
        buf[ret] = 0;

        printf("Recv: %s\n", buf);
    }

    return ret;
}

int clint_except_handler(int client) 
{
    char buf[2] = {0};
    int r = recv(client, buf, sizeof(buf), MSG_OOB);

    if (r > 0) {
        buf[r] = 0;
        printf("OOB: %s\n", buf);
    }

    return r;
}

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int max = 0;
    int num = 0;
    fd_set reads = {0};
    fd_set temps = {0};
    fd_set except = {0};
    struct timeval timeout = {0};

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listeb error\n");
        return -1;
    }

    printf("server start success\n");

    FD_ZERO(&reads);
    FD_SET(server, &reads);

    max = server;

    while (1) {
        temps = reads;
        except = reads;

        timeout.tv_sec = 0;
        timeout.tv_usec = 10000;

        num = select(max + 1, &temps, 0, &except, &timeout);

        if (num > 0) {
            int i = 0;

            for (i=0; i<=max; ++i) {
                if (FD_ISSET(i, &except)) {
                    if (i != server) {
                        clint_except_handler(i);
                    }
                }

                if (FD_ISSET(i, &temps)) {
                    if (i == server) {
                        int client = server_handler(server);

                        if (client > -1) {
                            FD_SET(client, &reads);
                            max = (client > max) ? client : max;
                            printf("accept client: %d\n", client);
                        }
                    } else {
                        int r = client_handler(i);

                        if (r == -1) {
                            FD_CLR(i, &reads);
                            close(i);
                        }
                    }
                }
            }

            int client = server_handler(server);
        }
    }
}
output:
 server start success
accept client: 4
OOB: g
Recv: Delpin-Tan

summary

  • read() / write() can be used to send and receive ordinary data (without extended functions)
  • send() / recv() can be extended with option information
  • TCP urgent data identifies 256 urgent events (abnormal events)
  • Through select, urgent data can be processed in time, and ordinary data can be distinguished

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧