我们知道TCP是全双工的,可以在接收数据的同时发送数据。
假设有主机A在和主机B通信,可以认为是在两者之间存在两个管道。就像这样:
A ---------> B
A <--------- B

1.close
  close可以用来关闭一个文件描述符。也就可以用来关闭一个套接字。
  当关闭一个套接字时,该套接字不能再由调用进程使用。如果调用进程再去read、write就会出错。

我们知道关闭一个socket描述符时,会给对方发送一个FIN数据段。比如在主机A中close了与主机B通信的sockA。相当于终止了全双工的那两个管道。而从传输层来看,TCP会尝试将目前发送缓冲区中积压的数据发到链路层上,然后才会发起TCP的4次挥手以彻底关闭TCP连接。
  之后在主机A中就不能用sockA来接收数据和发送数据了,同时由于是面向连接的。之前与sockA连接的sockB也收不到数据了。
  如果依然通过sockB往主机A上写数据,开始会触发一个RST重连包,然后会收到一个SIGPIPE信号。

但是如果存在父子进程共用socket描述符的时候(比如fork了一个子进程),父子进程都有相同数值的文件描述符,且都是打开的。这时候去关闭父进程中的描述符并不会发送FIN包给对方。只有子进程也关闭了才会发送FIN。

原因在于,fork时,父子进程共享着套接字,套接字描述符的引用计数记录着共享着的进程个数。fork一次时相当于引用计数为2了。这时候去关闭一个,只会让引用计数减一。只有当引用计数为0时(也就是子进程也close了),才会发送FIN给连接方。
  (就有点像windows下的句柄handle,是一个内核对象,当每被打开一次时,引用计数就会加一,CloseHandle时引用计数减一,若引用计数为0时,操作系统会回收这个内核对象)

2.shutdown
也可以用来关闭TCP数据传输的一个或两个方向。
原型:

SYNOPSIS
       #include <sys/socket.h>

int shutdown(int sockfd, int how);

DESCRIPTION
       The  shutdown()  call causes all or part of a full-duplex connection on
       the socket associated with sockfd to be shut down.  If how is  SHUT_RD,
       further  receptions  will  be  disallowed.   If how is SHUT_WR, further
       transmissions will be disallowed.  If how is SHUT_RDWR, further  recep‐
       tions and transmissions will be disallowed.

RETURN VALUE
       On  success,  zero is returned.  On error, -1 is returned, and errno is
       set appropriately.

参数:第一个表示socket描述符
第二个表示关闭读还是写。具体有三个值:
1)SHUT_WR:关闭读,表示不能用第一个参数对应的描述符往管道里面写数据了。(但是依然可以写数据)
2)SHUT_RD:关闭写,不能写数据了。(依然可以接收数据)
3)SHUT_RDWR:同时关闭读和写

3.close和shutdown的区别
1)close只会让引用计数减一,只有在引用计数减为零的时候才会给对方发送FIN段来断开连接。而shutdown会直接关闭连接,不受引用计数的限制,这就意味着在多进程中,只有调用了这个关闭了写端,那么其他进程也都不能写了。
2)close会关闭两端,shutdown可以选择关闭某个端。(这点非常有用处,比如主机A和B正在通信,A觉得没数据发送了,想要断开连接。然后A调用了close,那么B的数据也将发不过来,但是可以选择用shutdown关闭写端,这时候可以接收完B发的数据)

4.实例,用于更好的分析理解shutdown的机制:
client从标准输入中接收数据发送给server。server用来接收client的数据,并且回射回去。
这里做一个处理,client发送一次数据之后马上按下Ctrl+D(会导致fgets返回NULL),然后shutdown写端(相当于往server发送了FIN段)。server收到数据后,sleep10s再回射回去。
具体关于下面代码的理解可以参考:http://www.cnblogs.com/xcywt/p/8087677.html

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<netinet/in.h>
#include<arpa/inet.h> #include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<signal.h> #define CLIENTCOUNT 100 void sig_recvpipe(int sig)
{
printf("recv pipe = %d\n", sig);
} int main(int argc, char **argv)
{
signal(SIGPIPE, sig_recvpipe);
int listenfd = socket(AF_INET, SOCK_STREAM, );
if(listenfd < )
{
perror("socket");
return -;
} unsigned short sport = ;
if(argc == )
{
sport = atoi(argv[]);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
printf("port = %d\n", sport);
addr.sin_port = htons(sport);
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(bind(listenfd, (struct sockaddr*)&addr, sizeof(addr)) < )
{
perror("bind");
return -;
} if(listen(listenfd, ) < )
{
perror("listen");
return -;
} struct sockaddr_in connaddr;
int len = sizeof(connaddr); int i = , ret = ;
int client[CLIENTCOUNT];
for(i = ; i<CLIENTCOUNT; i++)
client[i] = -; fd_set rset;
fd_set allset;
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
int maxfd = listenfd;
int nready = ;
char buf[] = {};
while()
{
rset = allset;
nready = select(maxfd+, &rset, NULL, NULL, NULL);
if(nready == -)
{
perror("select");
return -; }
if(nready == )
{
continue;
} if(FD_ISSET(listenfd, &rset))
{
int conn = accept(listenfd, (struct sockaddr*)&connaddr, &len);
if(conn < )
{
perror("accept");
return -;
} char strip[] = {};
char *ip = inet_ntoa(connaddr.sin_addr);
strcpy(strip, ip);
printf("new client connect, conn:%d,ip:%s, port:%d\n", conn, strip,ntohs(connaddr.sin_port)); FD_SET(conn, &allset);
if(maxfd < conn) // update maxfd
maxfd = conn; int i = ;
for(i = ; i<CLIENTCOUNT; i++)
{
if(client[i] == -)
{
client[i] = conn;
break;
}
}
if(i == CLIENTCOUNT)
{
printf("to many client connect\n");
exit();
} if(--nready <= )
continue;
}
for(i = ; i < CLIENTCOUNT; i++)
{
if(client[i] == -)
continue;
if(FD_ISSET(client[i], &rset))
{
ret = read(client[i], buf, sizeof(buf));
if(ret == -)
{
perror("read");
return -;
}
else if(ret == )
{
printf("client close remove:%d\n", client[i]);
FD_CLR(client[i], &allset);
close(client[i]);
client[i] = -; // 要在这里移除
} // fputs(buf, stdout);
printf("Recv client%d:%s", client[i], buf);
sleep();
write(client[i], buf, sizeof(buf));
memset(buf, , sizeof(buf)); if(--nready <= )
continue;
}
}
} close(listenfd);
return ;
}

client端:

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h> #include<stdlib.h>
#include<stdio.h>
#include<string.h> void select_test(int conn)
{
int ret = ;
fd_set rset;
FD_ZERO(&rset); int nready;
int maxfd = conn;
int fd_stdin = fileno(stdin);
if(fd_stdin > maxfd)
{
maxfd = fd_stdin;
} int stdinoff = ;
int len = ;
char readbuf[] = {};
char writebuf[] = {};
while()
{
FD_ZERO(&rset);
if(!stdinoff)
FD_SET(fd_stdin, &rset);
FD_SET(conn, &rset);
nready = select(maxfd+, &rset, NULL, NULL, NULL);
if(nready == -)
{
perror("select");
exit();
}
else if(nready == )
{
continue;
} if(FD_ISSET(conn, &rset))
{
ret = read(conn, readbuf, sizeof(readbuf));
if(ret == )
{
printf("server close1\n");
break;
}
else if(- == ret)
{
perror("read1");
break;
} fputs(readbuf, stdout);
memset(readbuf, , sizeof(readbuf));
} if(FD_ISSET(fd_stdin, &rset))
{
if(fgets(writebuf, sizeof(writebuf), stdin) == NULL)
{
#if 0
printf("After 5s client exit\n");
close(conn);
sleep();
exit(EXIT_FAILURE);
#else
shutdown(conn, SHUT_WR);
stdinoff = ;
#endif
}
else
{
write(conn, writebuf, sizeof(writebuf));
memset(writebuf, , sizeof(writebuf));
}
}
}
close(conn);
} int sockfd = ;
int main(int argc, char **argv)
{
sockfd = socket(AF_INET, SOCK_STREAM, );
if(sockfd < )
{
perror("socket");
return -;
} unsigned short sport = ;
if(argc == )
{
sport = atoi(argv[]);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
printf("port = %d\n", sport);
addr.sin_port = htons(sport);
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < )
{
perror("connect");
return -;
} struct sockaddr_in addr2;
socklen_t len = sizeof(addr2);
if(getpeername(sockfd, (struct sockaddr*)&addr2, &len) < )
{
perror("getsockname");
return -;
} printf("Server: port:%d, ip:%s\n", ntohs(addr2.sin_port), inet_ntoa(addr2.sin_addr)); select_test(sockfd); close(sockfd);
return ;
}

编译运行:
makefile:

CC=gcc
CFLAGS=-Wall -g
LIBS=-lpthread
all:echoser echocli
echoser:server.c
$(CC) $< $(CFLAGS) $(LIBS) -o $@
echocli:client.c
$(CC) $< $(CFLAGS) $(LIBS) -o $@
.PHONY:clean
clean:
rm -f *.o echoser echocli *~

client端运行:
在发送完1111111时马上按下Ctrl+d,将读端关闭。然后会发现10s以后还是可以收到server的数据。

xcy@xcy-virtual-machine:~/test/sock8_shutdown$ ./echocli
port =
Server: port:, ip:127.0.0.1 server close1
xcy@xcy-virtual-machine:~/test/sock8_shutdown$

server端运行:
server收到数据,10s后发送给client。之后还会read返回0,会认为是client关闭了,然后就把套接字关闭了。最后client也能收到read返回0。

xcy@xcy-virtual-machine:~/test/sock8_shutdown$ ./echoser
port =
new client connect, conn:,ip:127.0.0.1, port:
Recv client4:
client close remove:
^C
xcy@xcy-virtual-machine:~/test/sock8_shutdown$

查看TCP状态:
当按下Ctrl+d时去查看状态,下面第2行可以看出来client已经变成CLOSE_WAIT状态了,server变成了FIN_WAIT2.
等10s到了,再去看client变成了TIME_WAIT状态(要保持2MSL)
具体可以参考:十一种状态 http://www.cnblogs.com/xcywt/p/8082428.html

xcy@xcy-virtual-machine:~$ netstat -an | grep
tcp 127.0.0.1: 0.0.0.0:* LISTEN
tcp 127.0.0.1: 127.0.0.1: CLOSE_WAIT
tcp 127.0.0.1: 127.0.0.1: FIN_WAIT2
xcy@xcy-virtual-machine:~$ netstat -an | grep
tcp 127.0.0.1: 0.0.0.0:* LISTEN
tcp 127.0.0.1: 127.0.0.1: TIME_WAIT
xcy@xcy-virtual-machine:~$ netstat -an | grep
xcy@xcy-virtual-machine:~$

我们可以看出来,client可以关闭写端,但是还是可以接收到到server发来的数据。

关于close和shutdown的更多相关文章

  1. Tomcat shutdown执行后无法退出进程问题排查及解决

    问题定位及排查 上周无意中调试程序在Linux上ps -ef|grep tomcat发现有许多tomcat的进程,当时因为没有影响系统运行就没当回事.而且我内心总觉得这可能是tomcat像nginx一 ...

  2. apche启动错误|httpd.pid overwritten — Unclean shutdown of previous Apache run?

    APACHE启动成功,但无法接受任何请求,查看ERROR.LOG文件[warn] pid file /opt/apache/logs/httpd.pid overwritten - Unclean s ...

  3. 登陆Oracle,报oracle initializationg or shutdown in progress 错误提示

    前两天,登陆Oracle,发现登陆不上去了,报”oracle initializationg or shutdown in progress 错误提示” 错误. 然后就想着怎么去解决,首先自己到win ...

  4. init shutdown reboot poweroff halt区别

    init 首先看看LINUX系统几种运行级别# 0 - 停机(千万别把initdefault设置为0,否则系统永远无法启动)# 1 - 单用户模式# 2 - 多用户,没有 NFS# 3 - 完全多用户 ...

  5. 谢欣伦 - 原创软件 - 工具软件 - 快速关机Shutdown

    快速关机Shutdown,含源码. 公司公用的笔记本电脑实在太烂,不知从什么时候开始关机永远都关不了,一直停留在“关闭系统中……”.忍无可忍之下,自己写了一个快速关机程序. 下载: Shutdown_ ...

  6. MongoDB学习系列(3)--解决MongoDB Unexpected Shutdown问题

    晚上准备继续学习PHP+MongoDB,点击Run_MongoDB_Service.bat文件,这个文件是我写的bat文件,就是快速启动MongoDB.但是命令行一闪而过,我很奇怪.昨天晚上写代码还是 ...

  7. Oracle shutdown immediate无法关闭数据库解决方法

    在测试服务器上使用shutdown immediate命令关闭数据库时,长时间无法关闭数据库,如下所示 1: [oracle@DB-Server admin]$ sqlplus / as sysdba ...

  8. Oracle数据库shutdown immediate被hang住的几个原因

    实验操作环境:         操作系统:Red Hat Enterprise Linux ES release 4 (Nahant Update 6)                         ...

  9. Linux命令学习总结:shutdown

    命令简介: 该命令可以安全关闭或者重新启动系统.你没有看错,shutdown命令不仅可以关闭系统.也可以重启Linux系统.   命令语法: /sbin/shutdown [-t sec] [-ark ...

  10. Linux常用命令学习3---(文件的压缩和解压缩命令zip unzip tar、关机和重启命令shutdown reboot……)

    1.压缩和解压缩命令    常用压缩格式:.zip..gz..bz2..tar.gz..tar.bz2..rar .zip格式压缩和解压缩命令        zip 压缩文件名 源文件:压缩文件   ...

随机推荐

  1. 基于SwiperJs的H5/移动端下拉刷新上拉加载更多的效果

    最早时,公司的H5项目中曾用过点击一个"加载更多"的DOM元素来实现分页的功能,后来又用过网上有人写的一个上拉加载更多的插件,那个插件是页面将要滚动到底部时就自动请求数据并插入到页 ...

  2. 如何用while循环输出十行十列变色★☆

    输出十行十列星星 k = 0 #设置一个终止变量 while k < 10: i = 0 #设置一个满十换行变量 while i < 10: print('★',end='') i += ...

  3. ecshop根据订单号查询物流信息

    目标:订单详情页可以根据订单查询当前物流信息. 效果图: 思路:点击后异步请求快递查询api,接受返回信息,拼接. 代码: admin下:order_info.htm //一:顶部插入jquery,在 ...

  4. 并发容器之写时拷贝的 List 和 Set

    对于一个对象来说,我们为了保证它的并发性,通常会选择使用声明式加锁方式交由我们的 Java 虚拟机来完成自动的加锁和释放锁的操作,例如我们的 synchronized.也会选择使用显式锁机制来主动的控 ...

  5. RaspberryPi2B使用bcm2835c库控制GPIO

    RaspberryPi2B使用bcm2835c库控制GPIO 网上有很多RaspberryPi控制GPIO的方法,有Python.WiringPi.bcm2835 C library 使用bcm283 ...

  6. 实际应用中遇到TimedRotatingFileHandler不滚动的问题

    需求: 程序每天晚上8点和10点定时运行,期望日志按日期记录 添加Handler部分代码如下: formatter = logging.Formatter("%(asctime)s %(fi ...

  7. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(上)

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构   前言 学习Python也有一个半月时间了,学到现在感觉 ...

  8. JAVA提高十九:WeakHashMap&EnumMap&LinkedHashMap&LinkedHashSet深入分析

    因为最近工作太忙了,连续的晚上支撑和上班,因此没有精力来写下这篇博客,今天上午正好有一点空,因此来复习一下不太常用的集合体系大家族中的几个类:WeakHashMap&EnumMap&L ...

  9. OpenStack搭建遇到的问题2(组件配置错误了,别重装全部,就把模块卸载就行了)

    apt-get remove -y mysql-server python-mysqldb 在装OpenStack的时候,出错的可能就是就是一个模块,比如keysstone或者是glance出错了,我 ...

  10. 初识CSS

    css解释 css样式: css是英文Cascading Style Sheets的缩写,称为层叠样式表,用于对页面进行美化,CSS的可以使页面更加的美观.基本上所有的html页面都或多或少的使用cs ...