`
memorymyann
  • 浏览: 270777 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

9.close和shutdown函数

阅读更多

打开的套接口总是需要调用close来关闭,这样就能够回收资源。虽然大部分发行版本的linux,至少在我的fedora上面,不执行close,当进程死亡的时候打开的套接口仍然会被回收,但依赖于这点回收的话具有很大的不确定因素,因为不能保证所有的内核都会在进程死亡的时候去回收资源(绝大部分嵌入式系统上不会)。

 

但依靠close来关闭套接口不是一个非常有效的方法。原因一:之前曾经说过,close只是将描述字共享计数减一,只有当共享计数为0的时候,资源才会被回收。原因二是:双方通信存在了很大的不确定因素,譬如说,当server觉得无数据可发,可以close,这不代表client没有数据发送。同样的道理,当client觉得没有数据可发,可以执行close时候,server并不一定没数据可发。

 

当然这2个问题,如果小心控制还是可以解决的,但这并不妨碍shutdown函数出现。对于第一个问题,shutdown函数会触发套接口发送fin包,会关掉套接口,而不关此套接口上有多少个进程在使用。close只是将计数减一,只有减成0时候,才会发送fin包。另一方面,是由于关闭全双工引起,如果没有数据可发,则关闭发送即可,不一定要关闭接受功能,shutdown也提供了关闭的套接的发送或者接受(当然也可以2者都关闭)。先看下API

 

 

int shutdown(int sockfd, int howto);

第一个很好理解,第2个可以取值SHUT_RD, SHUT_WR, SHUT_RDWR也不是很难理解。

 

先看看close的缺陷,只看有关代码

for(;;) {
        int connfd = accept(sockfd, (struct sockaddr *)NULL, NULL);
        printf("%d\n", connfd);
        pid_t child_pid;
        if((child_pid = fork()) == 0) {
                char buf[101];
                int n = read(connfd, buf, 100);
                buf[n] = '\0';
                printf("%s", buf);
                close(connfd);
                exit(0);
        }
}

 

服务器端代码。当我们在子进程中调用close(connfd),而父进程没有调用close,我们看看会发生什么情况。

[root@liumengli net]# netstat -a | grep 6584
tcp        0      0 *:6584                      *:*                         LISTEN
tcp        0      0 192.168.1.235:6584          192.168.1.164:51176         CLOSE_WAIT

上面是监听套接口,对于CLOSE_WAIT状态,解释是:client完成通信,发送FIN。server接受并发送ack,进入close_wait状态,虽然子进程执行了close,但父进程没有,所以链接处于CLOSE_WAIT状态,server没有继续发送FIN。(另一个明显的特征是,比如此前描述字如果是3,如果此链接处于close_wait状态,则下一个分配的描述字将会是4。如果一直没有回收,也就是描述字所指向的内存区域没有回收,内存资源就会泄漏)。

 

如果在父进程添加close则出现

[root@liumengli net]# netstat -a | grep 9584
tcp        0      0 *:9584                      *:*                         LISTEN

这时就没有在看见上个链接存在的痕迹,当然也可能会出现,这还得取决于client的代码。当然我们想说明的就是close必须是减到0才会回收链接。

 

当我们把代码缓冲shutdown时候,就会发现即使在不在父进程中添加shutdown,依然可以达到关闭链接的效果,这个自己可以试试,我就不贴了(应该不难)。

 

shutdown的另一方面:

如果当我们执行shutdown(sockfd, SHUT_RD),对底层协议不会有影响, TCP窗口接受数据,但窗口不改变直至窗口满(所谓窗口不改变,也就是说接受到的数据不会传到应用层,应用程序不会接受到数据),也不会确认数据。对于UDP任何情况下都不会产生ICMP错误包。这些话不好理解,从程序来看到底会发生什么事情。

for(;;) {
        char buf[101];
        int n = read(connfd, buf, 100);
        buf[n] = '\0';
        printf("%s", buf);
        printf("n = %d\n", n);
        if(!n) {
                getchar();
                break;
        }
        if(n == 3) {
                shutdown(connfd, SHUT_RD);
        }
}

代码很简单,服务器循环不断的从套接口中读出数据,当数据长度为3时候,就关闭读。从结果来看,当关闭读以后,就再也不会从套接口中读取数据,即使你再次调用read函数,进程会一直被阻塞在read函数中,不会返回。但查看链接来看

tcp        0      0 *:9784                      *:*                         LISTEN
tcp       15      0 192.168.1.235:9784          192.168.1.164:44679         ESTABLISHED

链接仍然存在,只是没有内核缓冲区的数据没有进入应用程序缓冲区(所谓的窗口是起控制作用,滑动窗口协议是每一个网络的基本教材都会讲到的)。

由此可见shutdown(sockfd, SHUT_RD)只是在内核缓冲区到达应用缓冲区产生影响,不会对链接产生影响。

 

当调用shutdown(sockfd, SHUT_WR)时候,TCP将会发送FIN.

此时如果再次想套接口写入数据,会引发SIGPIPE信号并导致进程终止,但如果是接受数据,则不会出现问题。

分享到:
评论

相关推荐

    Linux上TCP的Shutdown和Close

    本文将详细探讨Linux上TCP连接的两个关键操作:Shutdown和Close,以及它们在TCP生命周期中的作用。 首先,让我们了解这两个概念的区别。`close()`函数是用来完全关闭一个socket连接的,而`shutdown()`则是用来部分...

    c语言SOCKET编程指南(20210926020214).pdf

    14. close() 和 shutdown() 函数:本文档中详细介绍了 close() 和 shutdown() 函数的使用方法和参数。 15. getpeername() 函数:本文档中详细介绍了 getpeername() 函数的使用方法和参数。 16. gethostname() 函数...

    c语言SOCKEt编程指南.docx

    14. close()和shutdown()函数: close()函数用于关闭套接字,而shutdown()函数用于关闭套接字的一部分。 15. struct in_addr结构体: struct in_addr结构体用于描述Internet地址,它包含一个无符号长整型的成员...

    c语言SOCKET编程指南.pdf

    14. close()和shutdown()函数:close()和shutdown()函数是SOCKET编程中的两个重要函数,用于关闭套接字。 15. getpeername()函数:getpeername()函数是SOCKET编程中的一个重要函数,用于获取套接字的远程主机名。 ...

    深入理解linux中close与shutdown的区别

    在Linux操作系统中,网络通信是通过套接字(socket)进行的,而`close`和`shutdown`函数是用于管理套接字生命周期的关键操作。理解它们的区别对于编写可靠的网络服务程序至关重要。 首先,我们来看`close`函数。`...

    Linux网络编程

    1.6 Linux 和 Unix 的发展.................................................................................... 15 第二章 UNIX/Linux 模型.....................................................................

    linux 网络编程源代码

    1.6 Linux 和 Unix 的发展 .................................................................................... 15 第二章 UNIX/Linux 模型....................................................................

    Close_Cmp.rar_close

    标题“Close_Cmp.rar_close”和描述中提到的程序是一个用于关闭计算机的工具。这个程序设计的目的是提供一个简单的点击式关机功能,使用户能够快速便捷地关闭他们的电脑。在IT领域,这样的应用程序通常涉及到操作...

    C语言Socket简单编程指南.pdf

    13. close()和shutdown()函数:用于关闭Socket连接,终止数据传输。 14. getpeername()函数:获取已连接套接字的对端socket信息。 15. gethostname()函数:获取本地机器的主机名。 16. 域名服务(DNS):介绍如何...

    Close System程序

    9. **调试和测试**:在开发过程中,利用VC++的调试工具,如断点、变量监视等,对代码进行调试以确保其正确性。同时,应充分测试各种场景,包括不同权限级别、网络环境等,以确保程序在各种情况下的稳定性。 通过...

    windows socket api函数大全

    9. **inet_addr()**和**inet_ntoa()**:IP地址字符串和二进制表示之间的转换。 10. **ioctlsocket()**:控制Socket的特殊操作,如查询硬件状态。 11. **listen()**:使Socket进入监听状态,等待客户端的连接。 12. *...

    win socket教程.

    9. `closesocket`函数:关闭套接字,释放与之相关的资源。 四、Winsock实例 一个简单的TCP服务器示例会创建一个套接字,绑定到特定端口,监听连接,然后对每个新连接调用`accept`。客户端则会创建一个套接字,尝试...

    Linux下socket 编程介绍

    此外,还有shutdown()函数,可以关闭Socket的读或写能力。 六、多线程与多进程 在实际应用中,服务器端往往需要处理多个并发连接。通过多线程或多进程技术,服务器可以同时处理多个客户端请求,提高服务效率。线程...

    C_close.rar_close

    Windows API提供了丰富的函数库,允许程序员执行各种系统级别的操作,包括控制系统的启动、暂停、重启和关闭。下面我们将深入探讨如何在C语言中实现这个功能,并通过一个具体的程序实例来解释其工作原理。 首先,...

    Winsock API函数参考

    9. **`closesocket`**:最后,`closesocket`函数用于关闭套接字,释放系统资源。 在Windows程序设计中,了解和正确使用这些函数至关重要,因为它们构成了网络通信的基础。通过熟练掌握Winsock API,开发者可以构建...

    Socket常用函数大全

    Socket函数集合为开发人员提供了强大的工具箱,用于创建、配置、管理以及在网络间发送和接收数据。以下是对给定文件中提及的Socket常用函数的详细解析,旨在帮助读者深入理解每个函数的功能与应用场景。 ### accept...

Global site tag (gtag.js) - Google Analytics