淺談linux中的網(wǎng)絡(luò)

linux網(wǎng)絡(luò)棧和tcp/ip

簡(jiǎn)單的來說,socket就是對(duì)tcp/ip的api接口。通過socket接口,兩個(gè)主機(jī)建立連接后直接通過recv()和send()即可以完成兩個(gè)主機(jī)之間的通信。

但是有沒有想過,在TCP/IP中,在鏈路層,我們需要對(duì)數(shù)據(jù)包添加幀頭(MAC地址信息等),IP層,我們又要為數(shù)據(jù)包添加IP頭(ip地址),在傳輸層,我們又需要添加端口信息等。而且其中還有一些差錯(cuò)校驗(yàn)等工作我們都沒有做。但是,事實(shí)上,在每一次包傳輸?shù)倪^程中,linux網(wǎng)絡(luò)協(xié)議棧都完成了對(duì)這些工作。而我們只需要調(diào)用內(nèi)核暴露出來的系統(tǒng)調(diào)用(socket)就可以了

如下圖所示,網(wǎng)絡(luò)包從網(wǎng)卡傳遞到應(yīng)用層可以分為如下幾個(gè)步驟:

  1. 網(wǎng)卡接受到數(shù)據(jù)包后,將數(shù)據(jù)包存放到包接受隊(duì)列中去,并且發(fā)出硬中斷信號(hào)
  2. 硬中斷處理程序?qū)?shù)據(jù)包,存放到sk_buff緩沖區(qū)中,并且發(fā)出軟中斷信息
  3. 內(nèi)核收到軟中斷后,將sk_buff中取出傳遞給linux網(wǎng)絡(luò)協(xié)議棧
  4. 經(jīng)過網(wǎng)絡(luò)協(xié)議棧的一些列的處理,將數(shù)據(jù)存放到了socket緩存中
  5. 應(yīng)用程序通過調(diào)用socket接口從socket緩存中取出數(shù)據(jù)
network1.png

小實(shí)驗(yàn)

# 一臺(tái)服務(wù)端server,兩個(gè)客戶端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)  # 監(jiān)聽該端口
# 在服務(wù)端另外打開一個(gè)端口,輸入以下命令
# 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
# 發(fā)現(xiàn)server端開始監(jiān)聽20000端口,從recv-q和send-q為0可以看出此時(shí)還沒有客戶發(fā)起連接請(qǐng)求
# 注:在listen狀態(tài)下recv-q表示半連接狀態(tài)的個(gè)數(shù),即客戶端發(fā)送了連接請(qǐng)求,但服務(wù)端還沒有發(fā)ack報(bào)文
# 打開一個(gè)c1客戶端
>>> import socket
>>> s = socket.socket()
>>> s.connect(("x.x.x.x", 20000))
# 在c1客戶端中發(fā)送連接請(qǐng)求
# 回到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 -  
# 再打開另外一個(gè)客戶端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 -
# 此時(shí)發(fā)現(xiàn)recv-q中字段為2,表示半連接的個(gè)數(shù),即表示客戶端發(fā)送了連接請(qǐng)求,但還沒得到服務(wù)端的ack報(bào)文。半連接狀態(tài)在linux也有也顯示,但看不到對(duì)應(yīng)的進(jìn)程(對(duì)比下面建立時(shí)的區(qū)別)

# 回到server中的python終端窗口
>>> import socket
>>> s = socket.socket()
>>> s.bind(("x.x.x.x", 20000))
>>> s.listen(10)
>>> c1, addr = s.accept()  # 從半連接隊(duì)中,選擇一個(gè)半連接,發(fā)送確定幀,形成連接
>>> 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
# 發(fā)現(xiàn)監(jiān)聽狀態(tài)的端口的recv-q字段變?yōu)?了,并且下方顯示成功建立的連接。
# 去其中一個(gè)client發(fā)送一下數(shù)據(jù)
>>> 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狀態(tài)時(shí),recv-q表示的接收緩沖去中還有多少數(shù)據(jù)沒有被取走,發(fā)現(xiàn)正好是上面輸入的19個(gè)字符還沒有被取走
# 回到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)   # 從接受隊(duì)列中取走五個(gè)字符的數(shù)據(jù)
'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
# 發(fā)現(xiàn)19變成14了,正好符合實(shí)驗(yàn)預(yù)期

#回到一個(gè)client終端,關(guān)閉一端的連接
>>> 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的要求,關(guān)閉tcp連接時(shí),需要雙方都要關(guān)閉,此時(shí)僅客戶端關(guān)閉了,也要到server端去關(guān)閉連接
# 回到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
# 連接完全關(guān)閉了

linux常見網(wǎng)絡(luò)命令

  1. nslookup: 查看域名的ip地址
  2. /etc/resolv.conf: 設(shè)置DNS服務(wù)器
  3. time:查看一個(gè)命令的運(yùn)行時(shí)間
  4. wrk和ab: http壓力測(cè)試工具
  5. sar: 查看網(wǎng)絡(luò)每秒的傳輸數(shù)據(jù)
  6. /etc/sysctl.conf: 配置linux網(wǎng)絡(luò)協(xié)議棧參數(shù)(sysctl -p 是配置生效)
配置內(nèi)容 配置描述
net.ipv4.ip_forward linux內(nèi)核是否可以轉(zhuǎn)發(fā)數(shù)據(jù)包
net.ipv4.tcp_max_sys_backlog tcp半連接最大數(shù)目
net.ipv4.tcp_xx 一些列和tcp相關(guān)的參數(shù)
net.ipv4.udp_xx 一些列和udp相關(guān)的參數(shù)
net.core.rmem.max 套接字接受緩沖區(qū)大小
net.core.wmem_max 套接字發(fā)送緩沖區(qū)大小
  1. iptables: 數(shù)據(jù)包轉(zhuǎn)發(fā)的控制
  2. ip: 查看ip地址情況
  3. ifconfig: 查看網(wǎng)卡及ip地址,還有網(wǎng)絡(luò)接受/發(fā)送包的個(gè)數(shù)和字節(jié)數(shù)

小實(shí)驗(yàn)

# cat /etc/resolv.conf    # 查看系統(tǒng)配置的DNS服務(wù)器
# 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服務(wù)器地址
Address:    202.115.32.36#53 # DNS服務(wù)器地址及其監(jiān)聽的端口

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命令所展示的時(shí)間,實(shí)際用了0.011秒
user    0m0.005s
sys 0m0.006s
# 換一個(gè)DNS服務(wù)器
[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秒,現(xiàn)在是0.29秒啊,慢了好多??!還是換回來把
user    0m0.008s
sys 0m0.007s

# echo "nameserver 202.115.32.36" > /etc/resolv.conf
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容