• 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的绝对可靠不是对用户来说。而是对于机制来说。

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

  • 相关阅读:
    深入解读kubernetes网络基本原理
    Go!Go!Go!设计模式-组合设计模式
    Go!Go!Go!设计模式-创建型模式(简单工厂,工厂方法,抽象工厂)
    Linux内核之磁盘和分区
    Docker容器网络基础
    chart仓库之创建-入库-使用(helm,helm-push,chartmuseum)
    Go语言完整解析Go!Go!Go!(一)数据类型 之 Channel & Goroutine
    k8s爬坑集锦[网络问题]-服务无法访问
    数字证书的原理与应用&爬坑
    ingress的用法与原理
  • 原文地址:https://www.cnblogs.com/lsfv/p/6367395.html
Copyright © 2020-2023  润新知