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è)步驟:
- 網(wǎng)卡接受到數(shù)據(jù)包后瓦阐,將數(shù)據(jù)包存放到包接受隊(duì)列中去,并且發(fā)出硬中斷信號(hào)
- 硬中斷處理程序?qū)?shù)據(jù)包篷牌,存放到sk_buff緩沖區(qū)中,并且發(fā)出軟中斷信息
- 內(nèi)核收到軟中斷后踏幻,將sk_buff中取出傳遞給linux網(wǎng)絡(luò)協(xié)議棧
- 經(jīng)過網(wǎng)絡(luò)協(xié)議棧的一些列的處理枷颊,將數(shù)據(jù)存放到了socket緩存中
- 應(yīng)用程序通過調(diào)用socket接口從socket緩存中取出數(shù)據(jù)
小實(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ò)命令
- nslookup: 查看域名的ip地址
- /etc/resolv.conf: 設(shè)置DNS服務(wù)器
- time:查看一個(gè)命令的運(yùn)行時(shí)間
- wrk和ab: http壓力測(cè)試工具
- sar: 查看網(wǎng)絡(luò)每秒的傳輸數(shù)據(jù)
- /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ū)大小 |
- iptables: 數(shù)據(jù)包轉(zhuǎn)發(fā)的控制
- ip: 查看ip地址情況
- 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