socket rst_socket communication programming

Hello everyone, meet again, I am your friend Quanzhanjun.

Three conditions for generating RST:

1. The destination is the SYN arrival of a port, but there is no server listening on the port;

2. TCP wants to cancel an existing connection;

3. TCP receives a segment on a connection that does not exist at all;

Now simulate the three cases above:

client:

struct sockaddr_in serverAdd;
    
    bzero(&serverAdd, sizeof(serverAdd));
    serverAdd.sin_family = AF_INET;
    serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serverAdd.sin_port = htons(SERV_PORT);
    
    int connfd = socket(AF_INET, SOCK_STREAM, 0);
    
    int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
    if (connResult < 0) {
        printf("Connection failed\n");
        close(connfd);
        return;
    }
    
    ssize_t writeLen;
    char sendMsg[5000] = {0};
    unsigned long long totalSize = 0;
    
    while (1) {
        
        writeLen = write(connfd, sendMsg, sizeof(sendMsg));
        if (writeLen < 0) {
            printf("Failed to send errno = \n",errno);
            return;
        }
        else
        {
            totalSize += writeLen;
            printf("Sent successfully totalSize = %zd\n",totalSize);
        }       
        
    }
copy

server:

#define SERV_PORT 8000

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

    struct sockaddr_in serverAdd;
    struct sockaddr_in clientAdd;
    
    bzero(&serverAdd, sizeof(serverAdd));
    serverAdd.sin_family = AF_INET;
    serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAdd.sin_port = htons(SERV_PORT);
    
    socklen_t clientAddrLen;
    
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int yes = 1;
    setsockopt(listenfd,
               SOL_SOCKET, SO_REUSEADDR,
               (void *)&yes, sizeof(yes));
    
    if (listenfd < 0) {
        printf("create socket fail\n");
        return -1;
    }
    
    int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
    if (bindResult < 0) {
        printf("Failed to bind port\n");
        close(listenfd);
        return -1;
    }
    
    listen(listenfd, 20);
    
    int connfd;
    unsigned char recvMsg[246988];
    unsigned long long totalSize = 0;
    
    clientAddrLen = sizeof(clientAdd);
    connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
    if (connfd < 0) {
        printf("Connection failed\n");
        return -1;
    }
    else{
//        Here we use it for testing and only receive one connection
        close(listenfd);
    }
    
    close(connfd);
    
    return 0;
}
copy

Case 1:

Instead of running the server, run the client program directly:

Client print information:

Connection failed

Use the packet capture tool to capture packets:

It can be seen that the client initiates a three-way handshake through the connect method, and after sending the first SYN segment, receives the RST segment from the server;

Case two:

Run the server, and then run the client program:

Client print information:

Send successfully totalSize = 5000

Send successfully totalSize = 10000

Send successfully totalSize = 15000

... ...

Send successfully totalSize = 125000

Send successfully totalSize = 130000

(lldb)

It can be seen that the program crashes at the write method when the client sends the 130001-135000 bytes, because the size of the TCP socket send buffer is 131768 bytes, and the buffer is sent when the first 130000 bytes are sent. It is not full, so the write method returns successfully, and then continues to send

Use the packet capture tool to capture packets:

Assuming that the server and the client have established a connection, the server calls close and sends the FIN segment to the client. At this time, the server can no longer send and receive data through the socket. At this time, the client calls read, and if it receives the FIN segment, it will return 0, but the client does this It is still possible to write to the server, the write call is only responsible for handing the data to the TCP send buffer and it can be returned successfully, so there will be no error, and the server will respond with an RST segment after receiving the data, indicating that the server can no longer receive the data, connect Reset, the client cannot notify the application layer immediately after receiving the RST segment, and only saves this state in the TCP protocol layer. If the client calls write again to send data to the server, since the TCP protocol layer is already in the RST state, the data will not be sent, but a SIGPIPE signal will be sent to the application layer. The default processing action of the SIGPIPE signal is to terminate the program.

When a process performs a write operation to a socket that has received an RST, (the write operation returns an EPIPE error at this time) the kernel sends a SIGPIPE signal to the process. The default behavior of this signal is to terminate the process, so the process must catch lest it be terminated unwillingly;

Continue to modify the client program as follows, the server remains unchanged:

    struct sockaddr_in serverAdd;
    
    bzero(&serverAdd, sizeof(serverAdd));
    serverAdd.sin_family = AF_INET;
    serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serverAdd.sin_port = htons(SERV_PORT);
    
    int connfd = socket(AF_INET, SOCK_STREAM, 0);
    
    int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
    if (connResult < 0) {
        printf("Connection failed\n");
        close(connfd);
        return;
    }
    
    ssize_t writeLen;
    ssize_t readLen;
    char recvMsg[65535];
    char sendMsg[5000] = {0};
    unsigned long long totalSize = 0;
    
    while (1) {
        
        writeLen = write(connfd, sendMsg, sizeof(sendMsg));
        if (writeLen < 0) {
            printf("Failed to send errno = %d\n",errno);
            return;
        }
        else
        {
            totalSize += writeLen;
            printf("Sent successfully totalSize = %zd\n",totalSize);
        }
        sleep(1);
        readLen = read(connfd, recvMsg, sizeof(recvMsg));
        if (readLen < 0) {
            printf("read failed errno = %d\n",errno);
            return;
        }
        else
        {
            printf("readLen:%ld\n",readLen);
        }
        
    }
copy

After the client writes 5000 bytes to the server, it sleeps for one second in order to send the data and confirm that the TCP protocol layer has received the RST segment responded by the server, and then performs the read operation. At this time, read returns -1. no longer 0;

Run the server first, then run the client, the client prints the information as follows:

Send successfully totalSize = 5000

read failed errno = 54

#defineECONNRESET 54/* Connection reset by peer */

When a process performs a read operation to a socket that has received an RST, (the read operation returns an ECONNRESET error)

The packet capture information is as follows:

The above situation will cause a problem: the server host process is terminated or restarted after a crash, the client will not know without writing, and read will return an ECONNRESET error or timeout;

The solution is to use select:

1. If the peer TCP sends a FIN (the peer process terminates), the socket becomes readable, and read returns 0;

2. If the peer TCP sends an RST (the peer host crashes and restarts), then the socket becomes readable, and read returns -1, and errno contains the exact error code;

This problem is described in the detailed explanation of select

Case three:

Modify the client program as follows, the server remains unchanged;

Run the server, then run the client program, the client prints the connection successfully, the if statement will sleep for 20 seconds at the beginning, (in the server program, after receiving a connection, close the socket and immediately exit the program) During this period, again Open the server, wait for the client's read data section to arrive, and then return an RST section to the client because TCP receives a section on a connection that does not exist at all; the server host crashes and restarts: its TCP All connection information before the crash is lost, so the server TCP responds with an RST for all data segments received from the client;

struct sockaddr_in serverAdd;
    
    bzero(&serverAdd, sizeof(serverAdd));
    serverAdd.sin_family = AF_INET;
    serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serverAdd.sin_port = htons(SERV_PORT);
    
    int connfd = socket(AF_INET, SOCK_STREAM, 0);
    
    int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
    if (connResult < 0) {
        printf("Connection failed\n");
        close(connfd);
        return;
    }
    else
    {
        printf("connection succeeded\n");
    }
    
    ssize_t writeLen;
    char sendMsg[5000] = {0};
    unsigned long long totalSize = 0;
   
    if (1) {
        sleep(20);

        writeLen = write(connfd, sendMsg, sizeof(sendMsg));
        if (writeLen < 0) {
            printf("Failed to send errno = %d\n",errno);
            return;
        }
        else
        {
            totalSize += writeLen;
            printf("Sent successfully totalSize = %zd\n",totalSize);
        }
        
    }
copy

The packet capture information is as follows:

refer to:

<UNIX Network ProgrammingVolume 1, Third Edition: TheSockets Networking API>

Copyright statement: The content of this article is contributed by Internet users, and the opinions of this article only represent the author himself. This site only provides information storage space services, does not own ownership, and does not assume relevant legal responsibilities. If you find any content suspected of infringing/violating laws and regulations on this site, please send an email to report. Once verified, this site will be deleted immediately.

Publisher: Full stack programmer, please indicate the source: https://javaforall.cn/187267.html Original link: https://javaforall.cn

Tags: security

Posted by Jassal on Mon, 14 Nov 2022 20:42:00 +1030