打开的套接口总是需要调用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,以及它们在TCP生命周期中的作用。 首先,让我们了解这两个概念的区别。`close()`函数是用来完全关闭一个socket连接的,而`shutdown()`则是用来部分...
14. close() 和 shutdown() 函数:本文档中详细介绍了 close() 和 shutdown() 函数的使用方法和参数。 15. getpeername() 函数:本文档中详细介绍了 getpeername() 函数的使用方法和参数。 16. gethostname() 函数...
14. close()和shutdown()函数: close()函数用于关闭套接字,而shutdown()函数用于关闭套接字的一部分。 15. struct in_addr结构体: struct in_addr结构体用于描述Internet地址,它包含一个无符号长整型的成员...
close()和shutdown()函数用于关闭套接字。 15. getpeername()函数 getpeername()函数用于获取对方的主机名和端口号。 16. gethostname()函数 gethostname()函数用于获取本机的主机名。 17. 域名服务(DNS) ...
14. close()和shutdown()函数:close()和shutdown()函数是SOCKET编程中的两个重要函数,用于关闭套接字。 15. getpeername()函数:getpeername()函数是SOCKET编程中的一个重要函数,用于获取套接字的远程主机名。 ...
在Linux操作系统中,网络通信是通过套接字(socket)进行的,而`close`和`shutdown`函数是用于管理套接字生命周期的关键操作。理解它们的区别对于编写可靠的网络服务程序至关重要。 首先,我们来看`close`函数。`...
1.6 Linux 和 Unix 的发展.................................................................................... 15 第二章 UNIX/Linux 模型.....................................................................
1.6 Linux 和 Unix 的发展 .................................................................................... 15 第二章 UNIX/Linux 模型....................................................................
标题“Close_Cmp.rar_close”和描述中提到的程序是一个用于关闭计算机的工具。这个程序设计的目的是提供一个简单的点击式关机功能,使用户能够快速便捷地关闭他们的电脑。在IT领域,这样的应用程序通常涉及到操作...
13. close()和shutdown()函数:用于关闭Socket连接,终止数据传输。 14. getpeername()函数:获取已连接套接字的对端socket信息。 15. gethostname()函数:获取本地机器的主机名。 16. 域名服务(DNS):介绍如何...
9. **调试和测试**:在开发过程中,利用VC++的调试工具,如断点、变量监视等,对代码进行调试以确保其正确性。同时,应充分测试各种场景,包括不同权限级别、网络环境等,以确保程序在各种情况下的稳定性。 通过...
9. **inet_addr()**和**inet_ntoa()**:IP地址字符串和二进制表示之间的转换。 10. **ioctlsocket()**:控制Socket的特殊操作,如查询硬件状态。 11. **listen()**:使Socket进入监听状态,等待客户端的连接。 12. *...
9. `closesocket`函数:关闭套接字,释放与之相关的资源。 四、Winsock实例 一个简单的TCP服务器示例会创建一个套接字,绑定到特定端口,监听连接,然后对每个新连接调用`accept`。客户端则会创建一个套接字,尝试...
此外,还有shutdown()函数,可以关闭Socket的读或写能力。 六、多线程与多进程 在实际应用中,服务器端往往需要处理多个并发连接。通过多线程或多进程技术,服务器可以同时处理多个客户端请求,提高服务效率。线程...
Windows API提供了丰富的函数库,允许程序员执行各种系统级别的操作,包括控制系统的启动、暂停、重启和关闭。下面我们将深入探讨如何在C语言中实现这个功能,并通过一个具体的程序实例来解释其工作原理。 首先,...
9. **`closesocket`**:最后,`closesocket`函数用于关闭套接字,释放系统资源。 在Windows程序设计中,了解和正确使用这些函数至关重要,因为它们构成了网络通信的基础。通过熟练掌握Winsock API,开发者可以构建...
Socket函数集合为开发人员提供了强大的工具箱,用于创建、配置、管理以及在网络间发送和接收数据。以下是对给定文件中提及的Socket常用函数的详细解析,旨在帮助读者深入理解每个函数的功能与应用场景。 ### accept...