tcp 状态转换.

命令行:
root@ubuntu:/home/linson# netstat -apt | grep 3030

server,listen

服务端根据端口生成一个socket.用于监听连接.也就是监听3次握手,当3次握手成功,建立一个连接接放入队列中.

也就是说执行了listen,会自动应答3次握手,如果不执行listen,没有api会帮你处理3次握手.

所以客户端的connect应该也是自动执行3次握手.

tcp 0 0 localhost:3030 *:* LISTEN 11170/epoll

server 还没应答之前

可以看到连接已经建立,但是服务端的连接却没有pid和程序名字.

说明3次握手和连接的建立都和accept方法没有关系.

也说明一个端口可以建立很多连接.listen建立起来的,被api赋予专门的用途.就是接受和应答3次握手,并建立新连接.
tcp 0 0 localhost:3030 *:* LISTEN 11170/epoll
tcp 0 0 localhost:3030 localhost:33092 ESTABLISHED -
tcp 0 0 localhost:33092 localhost:3030 ESTABLISHED 11504/esocket6


应答之后

可以看到监听链接(由listen建立的)和客户端握手之后建立的新连接终于有pid了.也就是理论上.可以一个程序监听.一个程序accept.可是其他程序获取不到监听连接的描述符.所以...

所以accept,完全和tcp没有关系,只是和系统内核打交道.从队列中取链接而已.
tcp 0 0 localhost:3030 *:* LISTEN 11170/epoll
tcp 0 0 localhost:3030 localhost:33092 ESTABLISHED 11170/epoll
tcp 0 0 localhost:33092 localhost:3030 ESTABLISHED 11504/esocket6

客户端关闭.服务断还未应答.

可以看到客户端发送close,其实是发送一个字节.服务端的req是1,说明有一个字节从客户端发送来了,而没有读取.应该就是eof标记字节.

并且可以知道,服务端内核通过客户端的ip和port区分对于自己同一个端口的不同连接,并且应该是通过某个标记来注明特殊的listen链接.

如果握手,那么给listen文件描述符,如果接受信息.那么定位一下特定的文件描述符.

也就说明端口是系统的端口.来定位系统中的程序的。

socket是某个端口的socket.端口可以有很多socket,比如服务端。一个端口可以有很多socket,来区分客户端。而客户端也可以用一个端口来连不同的服务端。也可以用不同的端口连同一个服务端。

而文件描述符是一个读写属性的东西.可以由socket的listen得到.也就是一个由系统自动接受3次握手,也就是可以有读写属性的东西.用于和任何客户通讯.

并且文件描述符,也可以由文件描述符本身得到.也就是 liston文件描述符,的accept方法.由系统生成一个描述符,用于和特定客户通讯.

客户端关闭,那么客户端是期待服务端发送结束命令。进入等待fin阶段。也就是FIN_WAIT2

而服务端是等待服务程序响应关闭,如果不手动响应,那么就等待tcp协议自己经过20秒左右的自己应答。
tcp 0 0 localhost:3030 *:* LISTEN 11170/epoll
tcp 1 0 localhost:3030 localhost:33092 CLOSE_WAIT 11170/epoll
tcp 0 0 localhost:33092 localhost:3030 FIN_WAIT2 11504/esocket6

服务端应答,之后,服务端彻底释放了socket.客户端进入time_wait阶段。等待是否还有路上的数据没接受到。
tcp 0 0 localhost:3030 *:* LISTEN 11170/epoll
tcp 0 0 localhost:33092 localhost:3030 TIME_WAIT -

服务端应答 之后某几分钟之内. 客户端也彻底释放socket.
tcp 0 0 localhost:3030 *:* LISTEN 11170/epoll

知识点

1)程序奔溃会发送fin标记。

2)客户断的 TIME_WAIT是等待2msl.因为最后一个分组信息是客户端发送的.发送完毕之后.服务端并不会回应.所以

  客户端辛苦一点,迟点下班,万一路由发生错误,还可以再发一次.

3)客户端连续发送2次close,是会导致发送rst?这个要再测试下.

tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 0 0 192.168.87.130:3046 192.168.87.130:40604 ESTABLISHED -
tcp 0 0 192.168.87.130:40604 192.168.87.130:3046 ESTABLISHED 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 0 0 192.168.87.130:3046 192.168.87.130:40604 ESTABLISHED -
tcp 0 0 192.168.87.130:40604 192.168.87.130:3046 ESTABLISHED 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 2 0 192.168.87.130:3046 192.168.87.130:40604 ESTABLISHED -
tcp 0 0 192.168.87.130:40604 192.168.87.130:3046 ESTABLISHED 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 3 0 192.168.87.130:3046 192.168.87.130:40604 CLOSE_WAIT -
tcp 0 0 192.168.87.130:40604 192.168.87.130:3046 FIN_WAIT2 -
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 3 0 192.168.87.130:3046 192.168.87.130:40604 CLOSE_WAIT 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 3 0 192.168.87.130:3046 192.168.87.130:40604 CLOSE_WAIT 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
tcp 0 0 192.168.87.130:3046 192.168.87.130:40604 CLOSE_WAIT 22452/linsonnetlib
root@ubuntu:/home/linson# netstat -apt |grep 3046
tcp 0 0 192.168.87.130:3046 *:* LISTEN 22452/linsonnetlib
root@ubuntu:/home/linson#

在linux 下有个ipython,

确实是个神器。

开2个终端。输入以下相关代码,

就可以立马实时查看网络通讯状态。

再开一个终端。输入netstat相关命令查看双方状态。

方便的一塌糊涂。

ipython
import socket,select
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('',6000))
s.listen(10)
c1,a1=s.accept()
c1.setblocking(0)


import socket,select
c=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect(('',6000))
c.shutdown(socket.SHUT_WR)

关闭的四次握手。

可以是客户端,2次关闭(一次shutdown,一次close),或2次close

这样服务端可以处于被动关闭状态。因为多线程环境下。短链接,服务端比较难判断是否消息全部发送完毕。

而客户端可以异步接受后,再一次主动关闭连接。会发送reset信号,服务端如果是epoll管理,取消关注事件后。好像会立即断开。而如果是一般监听。会进入closewait.

client              server

shutdown

fin->

fin1

        -<act

        colsewait

fin2

close->

fin2->

                       -<act

over                 close wait

1)发起关闭的一方,会进入fin_wait1 阶段。

对端由tcp 协议回应,

对端进入 close_wait 阶段。

发起一方进入fin_wait2阶段。

2)对端接受0.关闭 进入lastack,

而起初发送的一方由tcp协议回应,进入time _wait

对端直接close.

所以经常见到的是close_wait,fin_wait2,time _wait这3个状态。

其他状态,由于tcp协议自动的回应,是立马消失的

3)发送数据如果对方缓冲满。发送会失败。而不是说我们的发送缓冲区没满就会接受。

经过测试,对方满。滑动窗口为0的时候。我们的发送就失败。这个时候,我们的发送缓冲区还是有些数据的,是由于网络通知的时间差,导致发现不能发送的时候,程序已经send不少了。但之后的send函数会失败。

这个时候不但我们应用层挤压了数据。连发送缓冲区也挤压了。所以tcp的绝对可靠不是对用户来说。而是对于机制来说。

这个时候如果取消接受事件,但是之前还是有挤压的接收事件存在。

原文地址:https://www.cnblogs.com/lsfv/p/6367395.html