Socket one-way disconnect function shutdown

table of Contents

1. Function declaration

2. The difference between shutdown() and close()/closesocket()

3. Applicable scenarios

4. Code


Because the socket is two-way, both the client and the server can perform input and output, so sometimes we need to realize one-way transmission of data on the socket, that is, the data is transmitted in one direction. One-way sockets are semi-open sockets.

To achieve semi-open, you need to use the shutdown() function.

1. Function declaration

/* Shut down all or part of the connection open on socket FD.
   HOW determines what to shut down:
     SHUT_RD   = No more receptions;
     SHUT_WR   = No more transmissions;
     SHUT_RDWR = No more receptions or transmissions.
   Returns 0 on success, -1 for errors.  */
extern int shutdown (int __fd, int __how) __THROW;

SHUT_RD: disconnect the input stream. The socket cannot receive data (even if the buffer receives data, it will be cleared), and cannot call input-related functions.

SHUT_WD: Disconnect the output stream. The socket cannot send data, but if there is untransmitted data in the output buffer, it will be delivered to the target host.

SHUT_RDWR: Disconnect the I/O stream at the same time. It is equivalent to calling shutdown() twice, one of which takes SHUT_RD as a parameter, and the other takes SHUT_WR as a parameter.

2. The difference between shutdown() and close()/closesocket()

In a multi-process environment:

close()/closesocket() destroys the socket identifier referenced by the current process, but the socket still exists intact, and other processes can still be used (just like shared_ptr). When the number of references drops to 0, the socket Was released and destroyed.

shutdown() is used to close the connection, not the socket. No matter how many times shutdown() is called, the socket will still exist until close() / closesocket() is called to clear the socket from memory. The kill area of ​​shutdown() is relatively large. Assuming that a pair of parent and child processes share a socket, if the parent process performs the shutdown (fd, SHUT_RD) operation on the identifier corresponding to this socket, all reference operators of this socket are Cannot receive data, can only send data.

When calling close()/closesocket() to close the socket, or calling shutdown() to close the output stream, a FIN packet will be sent to the other party. The FIN packet indicates that the data transmission is complete, and the computer knows that no more data will be transmitted after receiving the FIN packet.

By default, close()/closesocket() will immediately send a FIN packet to the network, regardless of whether there is data in the output buffer, and shutdown() will wait for the data in the output buffer to be transmitted before sending the FIN packet. This means that calling close()/closesocket() will lose the data in the output buffer, while calling shutdown() will not.

3. Applicable scenarios

Because shutdown allows sockets to achieve one-way transmission, and semi-open sockets are used in the following situations:

  • When you want to ensure that all written data has been sent successfully. If the network is disconnected or abnormal during the process of sending data, the system will not necessarily return an abnormality. At this time, how do you determine that the peer has received the data? You can use the shutdown() function to determine whether the data is sent successfully, because when the shutdown() function is called, it will only return after all the data in the buffer has been sent successfully.
  • When you cannot write data to some sockets, or you cannot read data from some sockets, you can use shutdown to disconnect them, and subsequent write or read operations will return an exception.
  • When multi-threaded, if you want to prevent other threads or processes from accessing the resource, if you have or you want to completely close the socket so that other threads or processes cannot use it, you can call shutdown().

4. Code

/*================================================================
 *   Copyright (C) 2021 baichao All rights reserved.
 *
 *   文件名称:shutdownService.cpp
 *   创 建 者:baichao
 *   创建日期:2021年02月08日
 *   描    述:
 *
 ================================================================*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(){
    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    //将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(11230);  //端口
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    //进入监听状态,等待用户发起请求
    listen(serv_sock, SOMAXCONN);


    //接收客户端请求
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);

    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    int count = 0;
    int size = 3;
    while(size--)
    {

        //sleep(5);
        char str[100];
        int readNum = read(clnt_sock,str,sizeof(str));
        if(readNum <= 0)
            continue;
        std::cout<<"第"<<++count<<"次读取数据长度为:"<<readNum<<std::endl;
        // write(clnt_sock, , sizeof(str));

    }
    shutdown(clnt_sock,SHUT_RD);

    size = 3;
    while(size--)
    {

        //sleep(5);
        char str[100];
        int readNum = read(clnt_sock,str,sizeof(str));
        if(readNum <= 0)
            continue;
        std::cout<<"第"<<++count<<"次读取数据长度为:"<<readNum<<std::endl;
        // write(clnt_sock, , sizeof(str));

    }
    close(clnt_sock);
    close(serv_sock);
    return 0;
}
/*================================================================
 *   Copyright (C) 2021 baichao All rights reserved.
 *
 *   文件名称:shutdownClient.cpp
 *   创 建 者:baichao
 *   创建日期:2021年02月08日
 *   描    述:
 *
 ================================================================*/

#include<iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(){

    int serv_sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(11230);

    connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    char context[100] = {0};
    for(int i = 0; i < sizeof(context)/sizeof(context[0]); ++i)
        context[i] = '1';
    int size = 3;
    while(size--)
    {
        write(serv_sock,context,sizeof(context)-1);
    }
    sleep(5);
    size = 3;
    while(size--)
    {
        write(serv_sock,context,sizeof(context)-1);
    }
    close(serv_sock);

    return 0;
}

operation result:

 

 

 

 

 

Guess you like

Origin blog.csdn.net/weixin_40179091/article/details/113669146