• linux一切皆文件之tcp socket描述符(三)


    一、知识准备

    1、在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件(比如:块设备,socket套接字,pipe队列)
    2、操作这些不同的类型就像操作文件一样,比如增删改查等


    二、环境准备

    组件 版本
    OS CentOS Linux release 7.5.1804

    三、tcp socket 文件描述符

    ● 当我们建立一条TCP连接时,在linux操作系统中会创建一个socket文件描述符
    ● 通过文件描述符就能找到socket的几本信息,比如TCP四元组(client-ip:client-port --> server-ip:server-port

    先准备2个脚本:
    server.py主要用于建立客户端的连接请求,并且接收客户端传来的数据,然后将收到的数据回传给客户端
    client.py每隔1秒向服务端发送一次'hello world'

    server.py

    import socket
    
    server_addr = ('127.0.0.1' , 22222)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(server_addr)
    sock.listen(5)
    
    while True:
        conn, clientAddr = sock.accept()
        while True:
            data = conn.recv(100)
            conn.sendall(data)
            
    sock.close()
    

    client.py

    import socket
    import time
    
    server_addr = ('127.0.0.1' , 22222)
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(server_addr)
    
    while True:
        message = 'hello world!'
        sock.send(message)
        sock.recv(100)
        time.sleep(1)
    
    sock.close()
    

    分别启动server.py与client.py

    [root@localhost ~]# python /tmp/server.py  &
    [1] 14199
    [root@localhost ~]# python /tmp/client.py  &
    [2] 14202
    

    查看server.py打开的文件描述符

    [root@localhost ~]# ls -l /proc/14199/fd
    total 0
    lrwx------ 1 root root 64 Nov  7 07:42 0 -> /dev/pts/0
    lrwx------ 1 root root 64 Nov  7 07:42 1 -> /dev/pts/0
    lrwx------ 1 root root 64 Nov  7 07:42 2 -> /dev/pts/0
    lrwx------ 1 root root 64 Nov  7 07:42 3 -> socket:[99154]
    lrwx------ 1 root root 64 Nov  7 07:42 4 -> socket:[99155]
    [root@localhost ~]# lsof -n | grep -E '99154|99155'
    python    14199         root    3u     IPv4              99154       0t0        TCP 127.0.0.1:22222 (LISTEN)
    python    14199         root    4u     IPv4              99155       0t0        TCP 127.0.0.1:22222->127.0.0.1:56946 (ESTABLISHED)
    

    我们主要关注ESTABLISHED状态的socket描述符,也就是4 -> socket:[99155]

    [root@localhost fd]# more /proc/net/tcp
      sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
      ...
       4: 0100007F:56CE 0100007F:DE72 01 00000000:00000000 00:00000000 00000000     0        0 99155 1 ffff90d8bb0145c0 20 4 31 10 -1
    

    进程打开了tcp socket 描述符4 -> socket:[99155],socket描述符指向内存中的socket结构体,该结构体详细描述了这个socket的详细信息

    最重要的是TCP四元组(local_ip:local_port --> remote_ip:remote_port),拆分转换成10进制

    0100007F:56CE

    [root@localhost ~]# ((d=0x01))
    [root@localhost ~]# ((c=0x00))
    [root@localhost ~]# ((b=0x00))
    [root@localhost ~]# ((a=0x7F))
    [root@localhost ~]# ((e=0x56CE))
    [root@localhost ~]# echo "$a.$b.$c.$d:$e"
    127.0.0.1:22222
    
    

    0100007F:DE72

    [root@localhost ~]# ((d=0x01))
    [root@localhost ~]# ((c=0x00))
    [root@localhost ~]# ((b=0x00))
    [root@localhost ~]# ((a=0x7F))
    [root@localhost ~]# ((e=0xDE72))
    [root@localhost ~]# echo "$a.$b.$c.$d:$e"
    127.0.0.1:56946
    

    在/proc/net/tcp包含了tcp连接的重要状态信息:
    00000000:00000000 : 发送队列与接收队列 (正数第四个字段)
    -1 : 慢启动门限 (倒数第一个字段)
    10 : 拥塞窗口 (倒数第二个字段)

    这里面还有很多描述:比如慢启动门限、传输队列以及接收队列、窗口探查等TCP相关的重要参数都可以查询到,具体的大家可以去看下《TCP/IP详解卷》

    client.py也存在同样的行为:

    [root@localhost ~]# ls -l /proc/14202/fd
    total 0
    lrwx------ 1 root root 64 Nov 19 04:43 0 -> /dev/pts/0
    lrwx------ 1 root root 64 Nov 19 04:43 1 -> /dev/pts/0
    lrwx------ 1 root root 64 Nov 19 04:43 2 -> /dev/pts/0
    lrwx------ 1 root root 64 Nov 19 04:43 3 -> socket:[28728]
    [root@localhost ~]# lsof -n | grep 28728
    python    14202         root    3u     IPv4              28728       0t0        TCP 127.0.0.1:56946->127.0.0.1:22222 (ESTABLISHED)
    
    [root@localhost fd]# more /proc/net/tcp
      sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
      ...
    3: 0100007F:C31A 0100007F:DE72 01 00000000:00000000 00:00000000 00000000     0        0 28728 3 ffff8a74ba1a0f80 20 4 30 10 -1
    

    0100007F:56CE

    [root@localhost ~]# ((d=0x01))
    [root@localhost ~]# ((c=0x00))
    [root@localhost ~]# ((b=0x00))
    [root@localhost ~]# ((a=0x7F))
    [root@localhost ~]# ((e=0x56CE))
    [root@localhost ~]# echo "$a.$b.$c.$d:$e"
    127.0.0.1:22222
    
    

    0100007F:DE72

    [root@localhost ~]# ((d=0x01))
    [root@localhost ~]# ((c=0x00))
    [root@localhost ~]# ((b=0x00))
    [root@localhost ~]# ((a=0x7F))
    [root@localhost ~]# ((e=0xDE72))
    [root@localhost ~]# echo "$a.$b.$c.$d:$e"
    127.0.0.1:56946
    

    总结一下:
    ● server.py与client.py各自打开tcp socket 描述符,该描述符指向内存中的socket结构体
    ● socket结构体描述了关于TCP的所有信息,其中通过TCP 4元组找到对端的通信节点
    ● socket将用户数据以及自身结构数据封装完成之后会交给底层的TCP协议,然后是IP协议、链路层信息,最后通过物理链路到达对端
    ● 对端也会依次解包,直至将发送端数据写入到指定的内存当中,最终由应用程序读取(本文中的server.py或client.py)

                            client.py                         server.py
                            +---------------+                 +---------------+
                            |pid:14202      |                 |pid:14199      |
                            |    +-----+    |                 |    +-----+    |
                            |    |fd:3 |    |                 |    |fd:4 |    |
                            |    +-----+    |                 |    +-----+    |
                            +---------------+                 +---------------+
                                    |                                 |
                 user space         |                                 |
                 +---------------------------------------------------------------------+
                 kernel space       |                                 |
                                    |                                 |
                                    v                                 v
                             +------+-------+                  +------+-------+
                             |socket:[28728]|                  |socket:[99155]|
                             +------+-------+                  +------+-------+
                                    |                                 |
                                    |                                 |
                                    v                                 v
                               +----+----+                       +----+----+
                               | socket  |                       | socket  |
                               +----+----+                       +----+----+
                                    |                                 |
                                    |                                 |
                                    v                                 v
                                   ++---------------------------------+-
                                   |                tcp                |
                                   +------------------------------------
    
    

    四、小结

    ● TCP连接中最重要的是TCP四元组,而进程打开TCP socket描述符可以找到四元组信息,从而确定双方的IP和port
    ● 通过socket文件描述符可以找到内存中的socket结构体,获取到TCP连接的详细信息,包括必备四元组、文件的inode、时间、出队入队状态等等
    ● 1个进程可以创建多个TCP连接,也就是创建多个socket文件描述符,这由该进程能够打开的文件数量限制(ulimit -n

    五、参考资料

    http://www.cs.colostate.edu/~gersch/cs457/CS457_tutorial2.pdf
    https://gist.github.com/jkstill/5095725



    至此,本文结束
    在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

  • 相关阅读:
    记录一次无法很好解决的问题
    java与进制转换
    花了点时间写了下测试框架
    利用eclipse或者pycharm编写monkeyrunner脚本,cmd打开应用“转转”并截图保存到D盘
    Instrumentation
    关于学生机受控应用的问题总结
    忙里偷闲一天
    linux下python3的安装(已安装python2的情况下)
    ROS上利用usb_cam读取摄像头图像
    ch8 -- directMethod
  • 原文地址:https://www.cnblogs.com/MrVolleyball/p/9987208.html
Copyright © 2020-2023  润新知