shutdown 与 close 函数 的区别
假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据,此时client调用read,如果接收到FIN 段会返回0,但client此时还是可以write 给server的,write调用只负责把数据交给TCP发送缓冲区就可以成功返回了,所以不会出错,而server收到数据后应答一个RST段,表示服务器已经不能接收数据,连接重置,client收到RST段后无法立刻通知应用层,只把这个状态保存在TCP协议层。如果client再次调用write发数据给server,由于TCP协议层已经处于RST状态了,因此不会将数据发出,而是发一个SIGPIPE信号给应用层,SIGPIPE信号的缺省处理动作是终止程序。
有时候代码中需要连续多次调用write,可能还来不及调用read得知对方已关闭了连接就被SIGPIPE信号终止掉了,这就需要在初始化时调用sigaction处理SIGPIPE信号,对于这个信号的处理我们通常忽略即可,signal(SIGPIPE,
SIG_IGN); 如果SIGPIPE信号没有导致进程异常退出(捕捉信号/忽略信号),write返回-1并且errno为EPIPE(Broken
pipe)。(非阻塞地write)
#include <unistd.h>
int close(int fd);
close 关闭了自身数据传输的两个方向。
#include <sys/socket.h>
int shutdown(int sockfd, int how);
shutdown 可以选择关闭某个方向或者同时关闭两个方向,shutdown
how = 0 or how = 1 or how = 2 (SHUT_RD or SHUT_WR or
SHUT_RDWR),后两者可以保证对等方接收到一个EOF字符(即发送了一个FIN段),而不管其他进程是否已经打开了这个套接字。而close不能保证,只有当某个sockfd的引用计数为0,close
才会发送FIN段,否则只是将引用计数减1而已。也就是说只有当所有进程(可能fork多个子进程都打开了这个套接字)都关闭了这个套接字,close
才会发送FIN 段。
所以说,如果是调用shutdown how = 1 ,则意味着往一个已经发送出FIN的套接字中写是允许的,接收到FIN段仅代表对方不再发送数据,但对方还是可以读取数据的,可以让对方可以继续读取缓冲区剩余的数据。
下面使用shutdown 修改客户端程序,在前面讲过的使用select函数修改后的客户端程序基础上,修改很小一部分:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
if (FD_ISSET(fd_stdin, &rset))
{ if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) } |
为了测试我们想要的效果,需要在select函数修改后的服务器端程序 的 134 行代码之后,即writen 之前 sleep(4); 目的是接收到客户端数据后不马上回射回去,睡眠4s 后在客户端已经关闭连接的情况下再发送数据。
先运行服务器端程序,再运行客户端程序,在客户端标准输入,迅速敲入两行:AAAAA\n
BBBBB\n 然后按下ctrl+d 即fgets
会返回NULL,然后调用shutdown关闭写端,虽然服务器端延时才发送数据,此时客户端写端已经关闭,但还是可以读取到回射回来的数据,服务器端最后得到一个FIN段,read
返回0,打印输出 client close ,并且close(conn); 而客户端在读取服务端回射回来的两次数据后,再次read
也返回0,故打印 server
connect close,break退出循环,进程顺利退出。从下面的输出还可以看出,因为延时的关系,所以不像以前那样发射一行就回射一行。
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_select
recv connect ip=127.0.0.1 port=54010
fdsgfgd
gfedg
client close
...........................
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_select_shutdown
local ip=127.0.0.1 port=54010
fdsgfgd
gfedg
fdsgfgd
gfedg
server connect close
如果我们将客户端程序中的shutdown 改成了
close,那么当延时后服务器端发送数据给客户端时,客户端的读端和写端都已经关闭,第一次发AAAAA会返回一个RST段,根据本文前面所说,再次发BBBBB直接产生SIGPIPE信号,默认会终止进程,但因为我们已经设置了忽略SIGPIPE信号,所以服务器端进程不会被终止,但客户端也会出错,因为回到while循环开头,select阻塞等待时发现套接字的读端已经关闭,所以不能再关心可读事件了,select会返回-1,错误码是
EBADF: Bad File Descriptor。
参考:
《Linux C 编程一站式学习》
《TCP/IP详解 卷一》
《UNP》
shutdown 与 close 函数 的区别的更多相关文章
- socket shutdown 与 close 函数 的区别
假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...
- UNIX网络编程——shutdown 与 close 函数 的区别
假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...
- linux网络编程中的shutdown()与close()函数
1.close()函数 int close(int sockfd); //返回成功为0,出错为-1 close 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字不能再由cl ...
- linux网络编程之shutdown() 与 close()函数详解
linux网络编程之shutdown() 与 close()函数详解 参考TCPIP网络编程和UNP: shutdown函数不能关闭套接字,只能关闭输入和输出流,然后发送EOF,假设套接字为A,那么这 ...
- PHP中fopen,file_get_contents,curl函数的区别
PHP中fopen,file_get_contents,curl函数的区别 1.fopen/file_get_contents每次请求都做DNS查询,并不对DNS的信息进行缓存,而curl会对DNS的 ...
- php正规则表达式学习笔记(几个常用函数的区别)
preg_mache()函数和 preg_mache_all()函数的区别: preg_mache()只会匹配规则中的字符一次, preg_mache_all()会匹配符合条件的所有字符! 例子对比: ...
- SQL表值函数和标量值函数的区别
SQL表值函数和标量值函数的区别 写sql存储过程经常需要调用一些函数来使处理过程更加合理,也可以使函数复用性更强,不过在写sql函数的时候可能会发现,有些函数是在表值函数下写的有些是在标量值下写的, ...
- exit()与_exit()函数的区别(Linux系统中)
注:exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXI ...
- fork 函数 和vfork 函数的区别
问题描述: fork 函数 和vfork 函数的区别 问题解决: fork函数使用: 注: 以上printf 属于标准IO库带缓冲,如果标准输出链接到终端设备,则它是行 ...
随机推荐
- 3 Sum leetcode java
题目: Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find al ...
- DateNavigator
<Border BorderThickness="1,1,1,1" BorderBrush="Black" Grid.Column="1&quo ...
- OpenSSL 中 RSA 加密解密实现源代码分析
1.RSA 公钥和私钥的组成.以及加密和解密的公式: 2.模指数运算: 先做指数运算,再做模运算.如 5^3 mod 7 = 125 mod 7 = 6 3.RSA加密算法流程: 选择一对不同的.而且 ...
- hibernate4.3.10环境搭建
1.首先还是引入所须要的包 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFC ...
- ZH奶酪:PHP安装扩展imagick
明明几个简单命令就能搞定,但是按照网上的方法就是不行,弄了一天,最后发现只需要两行命令,而且不需要修改什么php.ini: sudo apt-get install php5-imagick sudo ...
- 谈谈node(1)
node的出现,给了全栈工程师,一个绝佳的机会. node给我们带来的是更低的硬件成本来完成更高需求以及用户体验感,一台8G内存服务器如果用传统架构,那么最多可驱使4000台同时访问,如果 ...
- php之表单-2(表单验证)
PHP 表单验证 本章节我们将介绍如何使用PHP验证客户端提交的表单数据. PHP 表单验证 在处理PHP表单时我们需要考虑安全性. 本章节我们将展示PHP表单数据安全处理,为了防止黑客及垃圾信息我们 ...
- Linux下启动eclipse报错
A Java Runtime Environment (JRE) or Java Development Kit (JDK) must be avail Java RunTime Environm ...
- java thread dump日志分析
jstack Dump 日志文件中的线程状态 dump 文件里,值得关注的线程状态有: 死锁,Deadlock(重点关注) 执行中,Runnable 等待资源,Waiting on conditio ...
- yum安装MangoDB
1:操作系统信息 2:yum命令查看MongoDB的包信息 3:配置yum源 #vi /etc/yum.repos.d/10gen.repo 4:查看mongoDB的服务器包的信息 5:安装Mongo ...