网络编程Socket之TCP之close/shutdown详解

本文详细探讨了TCP连接关闭时`close`和`shutdown`的区别。`shutdown`可以独立关闭读写通道,而`close`在默认情况下会等待发送缓冲区数据发送完毕后发送FIN。此外,介绍了`SO_LINGER`套接字选项对关闭行为的影响,包括立即关闭(RST)和延迟关闭(等待数据发送和连接终止)。通过实例展示了各种关闭方式对套接字缓冲区数据的影响。
摘要由CSDN通过智能技术生成

close

当套接字的引用计数为0的时候才会引发TCP的四分组连接终止序列;

 

shutdown

不用管套接字的引用计数就激发TCP的正常连接终止序列;


这里由一个SO_LINGER套接字选项

struct linger {

     int l_onoff; /* 0 = off, nozero = on */

     int l_linger; /* linger time,POSIX specifies units as seconds */

};


shutdown:SHUT_RD

关闭连接的读这一半,进程不能再对这样的套接字调用任何读操作;

在套接字上不能再发出接收请求,进程仍可往套接字发送数据,套接字接收缓冲区中所有数据被丢弃,再接收到的任何数据由TCP丢弃,对套接字发送缓冲区没有任何影响;

程序模拟:

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("连接失败\n");
        close(connfd);
        return;
    }
    else
    {
        printf("连接成功\n");
    }
    
    ssize_t writeLen;

    char sendMsg[5000] = {0};
    unsigned long long totalSize = 0;
  
    if (1) {

        writeLen = write(connfd, sendMsg, sizeof(sendMsg));
        if (writeLen < 0) {
            printf("发送失败 errno = %d\n",errno);
            return;
        }
        else
        {
            totalSize += writeLen;
            printf("发送成功 totalSize = %zd\n",totalSize);
        }
        sleep(20);
        
    }

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("创建socket失败\n");
        return -1;
    }
    
    int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
    if (bindResult < 0) {
        printf("绑定端口失败\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("连接失败\n");
        return -1;
    }
    else{
//        这里我们用于测试,只接收一个连接
        close(listenfd);
    }
    
    shutdown(connfd,SHUT_RD);

    while (1) {
        ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
        
        if (readLen < 0) {
            printf("读取失败 errno = %d\n",errno);
            close(connfd);
            return -1;
        }
        else if (readLen == 0) {
            printf("读取完成 totalSize = %llu\n",totalSize);
            close(connfd);
            return -1;
        }
        else
        {
            totalSize += readLen;
            printf("readLen:%ld\n",readLen);
        }

    }
    
    return 0;
}


打印信息:

client:

连接成功

发送成功 totalSize = 5000

server:

读取完成 totalSize = 0

是因为读关闭了,套接字接收缓冲区的数据直接被丢弃掉了,如果将服务端的shutdiwn替换成close会如下

读取失败 errno = 9


#define EBADF 9 /* Bad file descriptor */


说明:如果服务端调用shutdown关闭读之后,客户端再往已经收到RST分节的服务端进行write操作,会引发错误导致程序终止,RST详解里面有讲到

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值