纪一次TCP/IP连接关闭全程
2010-05-29 14:16
309 查看
TCP/IP众所周知在连接的时候,需要经历三次握手,而在终止的时候需要经历四次(有某些是以三次实现)握手才能“完美的”终止一次TCP/IP,因为TCP/IP的可靠性导致了一种互不信任的通信模式。故非此周折;
TCP/IP三次握手过程。
三次握手好理解。下面是TCP/IP关闭的四次握手流程图;
当一方发送close的时候,则向服务器发送一个FIN报文分节。得到服务器的响应,这个时候,客户机无法再进行任何的读写,也许会存在位于客户机套接字发送缓存区的数据没有发送的问题。这个时候,可以选择设置SO_LINGER选项。将close进行限时阻塞。这个时候服务器如果进行读写的话,则读会读取到流结束符,写会导致异常抛出。
C++服务器代码:
服务器主要是读从客户端中读取字节。
C++客户端:
客户端主要是持续的向服务器写入字节;
另外用JAVA代码进行了一个C/S程序做对比;
JAVA服务器:
JAVA客户端代码:
在LINUX系统中,每一个合理关闭(main执行结束)和意外关闭(SIGKILL)被系统中断的程序,都在结束的时候向远程服务器发送一个FIN分节。
其中四个程序分别运行在以下环境:
C++ server:Fedora
C++ client:cygwin windows
JAVA server:windows
JAVA client:windows
下面分别是几种关闭客户端进程的情况:
JAVA客户端退出
1) 非正常终止(不显示调用close):
a) 服务器读:Connection reset异常;
b) 服务器写:socket write error;
2) close终止:
a) 服务器读:-1,流结束符;
b) 服务器写:socket write error;
C++客户端退出
1) 非正常终止(不显示调用close):
a) 服务器读:0,流结束符
b) 服务器写:-1 异常
2) close终止:
a) 服务器读:0,流结束符;
b) 服务器写:-1 异常
TCP/IP的状态图如下:
状态描述:
1) 服务器启动。这个时候状态为
状态为:
2) 客户端连接:
客户机状态和服务器状态:
这个时候,我们可以用tcpdump看到两台机器中不断的发送数据,
3)关闭客户端
此时服务器状态为
这个时候,我们在tcpdump中可以见到:
在服务器ACK之前,事实上客户端经历了FIN_WAIT_1的状态,不过因为服务器很快的给予了FIN的ACK答复分节,所以这个过程转瞬即逝。
此时,客户端和服务器之间实现了所谓的半关闭。
3) 关闭服务器
客户机状态为:
我们tcpdump查看的数据为:
这表示服务器那半连接也被关闭了。
我们也可以采用JAVA客户端在WINDOWS平台上连接Fedora的C++服务器,我们在tcpdump中发现如果我们不执行close操作,则不会向服务器发送FIN分节,这也是导致了为什么在讨论JAVA客户端非正常关闭的情况下,read的结果是系统异常而不是流结束符。
TIME_WAIT一般会经历两个MSL,主要是为了防止化身进程和上一个关闭的进程在网络中还没有接受到的分节产生混淆。
TCP/IP三次握手过程。
三次握手好理解。下面是TCP/IP关闭的四次握手流程图;
当一方发送close的时候,则向服务器发送一个FIN报文分节。得到服务器的响应,这个时候,客户机无法再进行任何的读写,也许会存在位于客户机套接字发送缓存区的数据没有发送的问题。这个时候,可以选择设置SO_LINGER选项。将close进行限时阻塞。这个时候服务器如果进行读写的话,则读会读取到流结束符,写会导致异常抛出。
C++服务器代码:
#define LINKQ 200 int main(int args,char *argc[]){ int listenfd; listenfd=socket(AF_INET,SOCK_STREAM,0); /**display the sock send buf size*/ int sndbufold; socklen_t sndbufoldsize=sizeof(sndbufold); getsockopt(listenfd,SOL_SOCKET,SO_SNDBUF,&sndbufold,&sndbufoldsize); cout << "--" << sndbufold << "--" << endl; sndbufold=1000; setsockopt(listenfd,SOL_SOCKET,SO_SNDBUF,&sndbufold,sndbufoldsize); int sndbufold2; socklen_t sndbufoldsize2=sizeof(sndbufold2); getsockopt(listenfd,SOL_SOCKET,SO_SNDBUF,&sndbufold2,&sndbufoldsize2); cout << "--" << sndbufold2 << "--" << endl; /**display the sock send buf size*/ struct sockaddr_in addr; bzero(&addr,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_port=htons(10355); addr.sin_addr.s_addr=htonl(INADDR_ANY); if((bind(listenfd,(struct sockaddr *)&addr,sizeof(addr)))<0){ perror("error:"); } listen(listenfd,LINKQ); struct sockaddr_in clientaddr; socklen_t clientlen; int clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,&clientlen); int sndbufold3=200; socklen_t sndbufoldsize3=sizeof(sndbufold3); setsockopt(clientfd,SOL_SOCKET,SO_SNDBUF,&sndbufold3,sndbufoldsize3); int sndbufold4; socklen_t sndbufoldsize4=sizeof(sndbufold4); getsockopt(listenfd,SOL_SOCKET,SO_SNDBUF,&sndbufold4,&sndbufoldsize4); cout << "client:" << sndbufold4 <<endl; char res[5000]; memset(res,'a',5000); int wlen; for(;;){ cout << "go to read" << endl <<flush; int readsize=read(clientfd,res,100); cout << "read back:" << readsize << endl << flush; } }
服务器主要是读从客户端中读取字节。
C++客户端:
int main(int args,char *argc[]){ int listenfd; listenfd=socket(AF_INET,SOCK_STREAM,0); /**display the sock send buf size*/ int sndbufold; socklen_t sndbufoldsize=sizeof(sndbufold); getsockopt(listenfd,SOL_SOCKET,SO_RCVBUF,&sndbufold,&sndbufoldsize); cout << "--" << sndbufold << "--" << endl; sndbufold=1033; setsockopt(listenfd,SOL_SOCKET,SO_RCVBUF,&sndbufold,sndbufoldsize); int sndbufold2; socklen_t sndbufoldsize2=sizeof(sndbufold2); getsockopt(listenfd,SOL_SOCKET,SO_RCVBUF,&sndbufold2,&sndbufoldsize2); cout << "--" << sndbufold2 << "--" << endl; /**display the sock send buf size*/ struct sockaddr_in addr; bzero(&addr,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_port=htons(10355); struct in_addr host; inet_pton(AF_INET,"192.168.1.101",(void *)&(addr.sin_addr)); connect(listenfd,(struct sockaddr *)&addr,sizeof(addr)); int canread; char getc[]="coreymylife"; for(int i=0;i<300;i++){ write(listenfd,getc,3); } for(;;){ write(listenfd,getc,3); } cout << "the end!" << endl << flush ; }
客户端主要是持续的向服务器写入字节;
另外用JAVA代码进行了一个C/S程序做对比;
JAVA服务器:
package org.corey.test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(10000); Socket requestClient = server.accept(); InputStream is = requestClient.getInputStream(); while (true) { System.out.println("begin:"); int a=is.read(); if(a==-1){ OutputStream os=requestClient.getOutputStream(); os.write("a".getBytes()); } System.out.println("end:"+a); } } catch (IOException e) { e.printStackTrace(); } } }
JAVA客户端代码:
package org.corey.test; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) { try { Socket client = new Socket("127.0.0.1", 10000); OutputStream os = client.getOutputStream(); for (int i = 0; i < 1000; i++) { os.write(i); } os.close(); client.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
在LINUX系统中,每一个合理关闭(main执行结束)和意外关闭(SIGKILL)被系统中断的程序,都在结束的时候向远程服务器发送一个FIN分节。
其中四个程序分别运行在以下环境:
C++ server:Fedora
C++ client:cygwin windows
JAVA server:windows
JAVA client:windows
下面分别是几种关闭客户端进程的情况:
JAVA客户端退出
1) 非正常终止(不显示调用close):
a) 服务器读:Connection reset异常;
b) 服务器写:socket write error;
2) close终止:
a) 服务器读:-1,流结束符;
b) 服务器写:socket write error;
C++客户端退出
1) 非正常终止(不显示调用close):
a) 服务器读:0,流结束符
b) 服务器写:-1 异常
2) close终止:
a) 服务器读:0,流结束符;
b) 服务器写:-1 异常
TCP/IP的状态图如下:
状态描述:
1) 服务器启动。这个时候状态为
状态为:
2) 客户端连接:
客户机状态和服务器状态:
这个时候,我们可以用tcpdump看到两台机器中不断的发送数据,
3)关闭客户端
此时服务器状态为
这个时候,我们在tcpdump中可以见到:
在服务器ACK之前,事实上客户端经历了FIN_WAIT_1的状态,不过因为服务器很快的给予了FIN的ACK答复分节,所以这个过程转瞬即逝。
此时,客户端和服务器之间实现了所谓的半关闭。
3) 关闭服务器
客户机状态为:
我们tcpdump查看的数据为:
这表示服务器那半连接也被关闭了。
我们也可以采用JAVA客户端在WINDOWS平台上连接Fedora的C++服务器,我们在tcpdump中发现如果我们不执行close操作,则不会向服务器发送FIN分节,这也是导致了为什么在讨论JAVA客户端非正常关闭的情况下,read的结果是系统异常而不是流结束符。
TIME_WAIT一般会经历两个MSL,主要是为了防止化身进程和上一个关闭的进程在网络中还没有接受到的分节产生混淆。
相关文章推荐
- 纪一次TCP/IP连接关闭全程
- TCP/IP (四) TCP连接的关闭
- TCP/IP的三次握手连接和四次握手关闭
- TCP/IP的三次握手连接和四次握手关闭
- TCP/IP的三次握手连接和四次握手关闭
- TCP/IP的三次握手连接和四次挥手关闭
- TCP/IP的三次握手连接和四次握手关闭
- TCP/IP TIME_WAIT状态原理(四次握手关闭连接原理)
- TCP-IP详解:TCP半打开连接及同时打开同时关闭
- 话说,那是一次完整的连接建立过程【C/S, tcp三次握手,发包,关闭连接】
- TCP/IP建立连接(三次握手)和关闭连接(四次挥手)
- TCP/IP的三次握手连接和四次握手关闭
- 看段有趣的故事,理解TCP/IP的TCP连接建立与关闭
- 说下TCP/IP UDP协议 及TCP的连接与关闭
- TCP/IP的三次握手连接和四次握手关闭【问题:为什么建立连接协议是三次握手,而关闭连接却是四次握手?】
- 关闭TCP连接的学问
- TCP/IP,http,socket,长连接,短连接
- TCP/IP建立连接与终止连接
- TCP的三次握手(建立连接)和四次挥手(关闭连接)
- TCP/IP 长连接 心跳 重连 重发 线程