(一)TCP十種狀態數據庫
上一篇咱們學習了tcp三次握手和四次揮手的過程,其中設計到的狀態一共有10種,即爲下圖所示:後端
(二)TCP的2MSL問題服務器
2MSL即兩倍的MSL,MSL即爲數據包在傳輸過程當中能夠存活的最長時間,TCP的TIME_WAIT狀態也稱爲2MSL等待狀態:當TCP的一端發起主動關閉,在發出最後一個ACK包後,即第3次握手完成後發送了第四次握手的ACK包後就進入了TIME_WAIT狀態,其必須在此狀態上停留兩倍的MSL時間,等待2MSL時間主要目的是怕最後一個 ACK包對方沒收到,那麼對方在超時後將重發第三次握手的FIN包,主動關閉端接到重發的FIN包後能夠再發一個ACK應答包。併發
在TIME_WAIT狀態時兩端的端口不能使用,要等到2MSL時間結束纔可繼續使用。app
當鏈接處於2MSL等待階段時任何遲到的報文段都將被丟棄。socket
不過在實際應用中能夠經過設置 SO_REUSEADDR選項達到沒必要等待2MSL時間結束再使用此端口。tcp
(三)TCP的長短鏈接學習
模擬一種TCP短鏈接的狀況:網站
在步驟5中,通常都是 client 先發起 close 操做。固然也不排除有特殊的狀況。spa
從上面的描述看,短鏈接通常只會在 client/server 間傳遞一次讀寫操做!
再模擬一種長鏈接的狀況:
長鏈接能夠省去較多的TCP創建和關閉的操做,減小浪費,節約時間。
對於頻繁請求資源的客戶來講,較適用長鏈接。
client與server之間的鏈接若是一直不關閉的話,會存在一個問題:
隨着客戶端鏈接愈來愈多,server遲早有扛不住的時候,這時候server端須要採起一些策略,如關閉一些長時間沒有讀寫事件發生的鏈接,這樣能夠避免一些惡意鏈接致使server端服務受損;若是條件再容許就能夠以客戶端機器爲顆粒度,限制每一個客戶端的最大長鏈接數,這樣能夠徹底避免某個蛋疼的客戶端連累後端服務。
長鏈接多用於操做頻繁,點對點的通信,並且鏈接數不能太多狀況。
每一個TCP鏈接都須要三次握手,這須要時間,若是每一個操做都是先鏈接,再操做的話那麼處理速度會下降不少,因此每一個操做完後都不斷開,再次處理時直接發送數據包就OK了,不用創建TCP鏈接。
例如:數據庫的鏈接用長鏈接,若是用短鏈接頻繁的通訊會形成socket錯誤,並且頻繁的socket 建立也是對資源的浪費。
而像WEB網站的http服務通常都用短連接,由於長鏈接對於服務端來講會耗費必定的資源,而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的鏈接用短鏈接會更省一些資源,若是用長鏈接,並且同時有成千上萬的用戶,若是每一個用戶都佔用一個鏈接的話,那可想而知吧。因此併發量大,但每一個用戶無需頻繁操做狀況下需用短連好。
(四)listen的隊列長度
服務器端:
1 from socket import * 2 from time import sleep 3 4 # 建立socket 5 tcpSerSocket = socket(AF_INET, SOCK_STREAM) 6 7 # 綁定本地信息 8 address = ('', 7788) 9 tcpSerSocket.bind(address) 10 11 connNum = int(raw_input("請輸入要最大的連接數:")) 12 13 # 使用socket建立的套接字默認的屬性是主動的,使用listen將其變爲被動的,這樣就能夠接收別人的連接了 14 tcpSerSocket.listen(connNum) 15 16 while True: 17 18 # 若是有新的客戶端來連接服務器,那麼就產生一個新的套接字專門爲這個客戶端服務器 19 newSocket, clientAddr = tcpSerSocket.accept() 20 print clientAddr 21 sleep(1)
客戶端:
1 from socket import * 2 3 connNum = raw_input("請輸入要連接服務器的次數:") 4 for i in range(int(connNum)): 5 s = socket(AF_INET, SOCK_STREAM) 6 s.connect(("192.168.1.102", 7788)) 7 print(i)