美文网首页
浅谈linux中的网络

浅谈linux中的网络

作者: 小小小笑呵 | 来源:发表于2019-06-27 22:23 被阅读0次

    linux网络栈和tcp/ip

    简单的来说,socket就是对tcp/ip的api接口。通过socket接口,两个主机建立连接后直接通过recv()和send()即可以完成两个主机之间的通信。

    但是有没有想过,在TCP/IP中,在链路层,我们需要对数据包添加帧头(MAC地址信息等),IP层,我们又要为数据包添加IP头(ip地址),在传输层,我们又需要添加端口信息等。而且其中还有一些差错校验等工作我们都没有做。但是,事实上,在每一次包传输的过程中,linux网络协议栈都完成了对这些工作。而我们只需要调用内核暴露出来的系统调用(socket)就可以了

    如下图所示,网络包从网卡传递到应用层可以分为如下几个步骤:

    1. 网卡接受到数据包后,将数据包存放到包接受队列中去,并且发出硬中断信号
    2. 硬中断处理程序将数据包,存放到sk_buff缓冲区中,并且发出软中断信息
    3. 内核收到软中断后,将sk_buff中取出传递给linux网络协议栈
    4. 经过网络协议栈的一些列的处理,将数据存放到了socket缓存中
    5. 应用程序通过调用socket接口从socket缓存中取出数据
    network1.png

    小实验

    # 一台服务端server,两个客户端c1, c2,os-centos7
    # server
    [root@master ~]# python
    Python 2.7.5 (default, Apr  9 2019, 14:30:50) 
    [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import socket
    >>> s = socket.socket()
    >>> s.bind(("x.x.x.x", 20000))  # 绑定端口
    >>> s.listen(10)  # 监听该端口
    # 在服务端另外打开一个端口,输入以下命令
    # netstat -tnlp | grep 20000
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python
    # 发现server端开始监听20000端口,从recv-q和send-q为0可以看出此时还没有客户发起连接请求
    # 注:在listen状态下recv-q表示半连接状态的个数,即客户端发送了连接请求,但服务端还没有发ack报文
    # 打开一个c1客户端
    >>> import socket
    >>> s = socket.socket()
    >>> s.connect(("x.x.x.x", 20000))
    # 在c1客户端中发送连接请求
    # 回到server打开另一终端,输入如下命令
    [root@master ~]# netstat -atnp | grep 20000
    tcp        1      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:39332    ESTABLISHED -  
    # 再打开另外一个客户端c2
    >>> import socket
    >>> s = socket.socket()
    >>> s.connect(('x.x.x.x', 20000))
    # 回到server中,输入如下命令
    # netstat -atnp | grep 20000
    tcp        2      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:47890    ESTABLISHED -                   
    tcp        0      0 x.x.x.x:20000    x.x.x.x:39332    ESTABLISHED -
    # 此时发现recv-q中字段为2,表示半连接的个数,即表示客户端发送了连接请求,但还没得到服务端的ack报文。半连接状态在linux也有也显示,但看不到对应的进程(对比下面建立时的区别)
    
    # 回到server中的python终端窗口
    >>> import socket
    >>> s = socket.socket()
    >>> s.bind(("x.x.x.x", 20000))
    >>> s.listen(10)
    >>> c1, addr = s.accept()  # 从半连接队中,选择一个半连接,发送确定帧,形成连接
    >>> c2, addr2 = s.accept()
    # 回到server中终端窗口
    [root@master ~]# netstat -atnp | grep 20000
    tcp        0      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:47890    ESTABLISHED 172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:39332    ESTABLISHED 172870/python
    # 发现监听状态的端口的recv-q字段变为0了,并且下方显示成功建立的连接。
    # 去其中一个client发送一下数据
    >>> import socket
    >>> s = socket.socket()
    >>> s.connect(("x.x.x.x", 20000))
    >>> s.send('today is a good day')
    19
    # 回到master的termal
    [root@master ~]# netstat -atnp | grep 20000
    tcp        0      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:47890    ESTABLISHED 172870/python       
    tcp       19      0 x.x.x.x:20000    x.x.x.x:39332    ESTABLISHED 172870/python 
    # 注意,在establish状态时,recv-q表示的接收缓冲去中还有多少数据没有被取走,发现正好是上面输入的19个字符还没有被取走
    # 回到master中python终端
    >>> import socket
    >>> s = socket.socket()
    >>> s.bind(("x.x.x.x", 20000))
    >>> s.listen(10)
    >>> c1, addr = s.accept()
    >>> c2, addr2 = s.accept()
    >>> c1.recv(5)   # 从接受队列中取走五个字符的数据
    'today'
    # 回到server的终端
    [root@master ~]# netstat -atnp | grep 20000
    tcp        0      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:47890    ESTABLISHED 172870/python       
    tcp       14      0 x.x.x.x:20000    x.x.x.x:39332    ESTABLISHED 172870/python
    # 发现19变成14了,正好符合实验预期
    
    #回到一个client终端,关闭一端的连接
    >>> s = socket.socket()
    >>> s.connect(("x.x.x.x", 20000))
    >>> s.send('today is a good day')
    19
    >>> s.close()
    # 回到server终端
    [root@master ~]# netstat -atnp | grep 20000
    tcp        0      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:47890    ESTABLISHED 172870/python       
    tcp        14      0 x.x.x.x:20000    x.x.x.x:39332    CLOSE_WAIT  172870/python 
    # 符合tcp的要求,关闭tcp连接时,需要双方都要关闭,此时仅客户端关闭了,也要到server端去关闭连接
    # 回到server终端
    >>> import socket
    >>> s = socket.socket()
    >>> s.bind(("x.x.x.x", 20000))
    >>> s.listen(10)
    >>> c1, addr = s.accept()
    >>> c2, addr2 = s.accept()
    >>> c1.recv(5)
    'today'
    >>> c1.close()
    # 回到server终端
    [root@master ~]# netstat -atnp | grep 20000
    tcp        0      0 x.x.x.x:20000    0.0.0.0:*               LISTEN      172870/python       
    tcp        0      0 x.x.x.x:20000    x.x.x.x:47890    ESTABLISHED 172870/python
    # 连接完全关闭了
    

    linux常见网络命令

    1. nslookup: 查看域名的ip地址
    2. /etc/resolv.conf: 设置DNS服务器
    3. time:查看一个命令的运行时间
    4. wrk和ab: http压力测试工具
    5. sar: 查看网络每秒的传输数据
    6. /etc/sysctl.conf: 配置linux网络协议栈参数(sysctl -p 是配置生效)
    配置内容 配置描述
    net.ipv4.ip_forward linux内核是否可以转发数据包
    net.ipv4.tcp_max_sys_backlog tcp半连接最大数目
    net.ipv4.tcp_xx 一些列和tcp相关的参数
    net.ipv4.udp_xx 一些列和udp相关的参数
    net.core.rmem.max 套接字接受缓冲区大小
    net.core.wmem_max 套接字发送缓冲区大小
    1. iptables: 数据包转发的控制
    2. ip: 查看ip地址情况
    3. ifconfig: 查看网卡及ip地址,还有网络接受/发送包的个数和字节数

    小实验

    # cat /etc/resolv.conf    # 查看系统配置的DNS服务器
    # Generated by NetworkManager
    nameserver 202.115.32.36
    nameserver 8.8.8.8 
    
    [root@master ~]# time nslookup www.baidu.com
    Server:     202.115.32.36    # DNS服务器地址
    Address:    202.115.32.36#53 # DNS服务器地址及其监听的端口
    
    Non-authoritative answer:
    www.baidu.com   canonical name = www.a.shifen.com.
    Name:   www.a.shifen.com   # 域名
    Address: 182.61.200.6  # 地址
    Name:   www.a.shifen.com
    Address: 182.61.200.7
    
    real    0m0.011s   # time命令所展示的时间,实际用了0.011秒
    user    0m0.005s
    sys 0m0.006s
    # 换一个DNS服务器
    [root@master ~]# echo "nameserver 8.8.8.8" > /etc/resolv.conf && cat /etc/resolv.conf
    nameserver 8.8.8.8  
    
    [root@master ~]# time nslookup www.baidu.com
    Server:     8.8.8.8
    Address:    8.8.8.8#53
    
    Non-authoritative answer:
    www.baidu.com   canonical name = www.a.shifen.com.
    www.a.shifen.com    canonical name = www.wshifen.com.
    Name:   www.wshifen.com
    Address: 103.235.46.39
    
    
    real    0m0.290s   # 刚刚是0.011秒,现在是0.29秒啊,慢了好多啊!还是换回来把
    user    0m0.008s
    sys 0m0.007s
    
    # echo "nameserver 202.115.32.36" > /etc/resolv.conf
    

    相关文章

      网友评论

          本文标题:浅谈linux中的网络

          本文链接:https://www.haomeiwen.com/subject/wwjrcctx.html