剛剛開始使用flask框架寫了兩個(gè)小程序。然后我先訪問了第一個(gè)“Hello,Web”的小程序,運(yùn)行通過,然后想看看第二個(gè)inputname的程序是不是寫對(duì),但是linux報(bào)錯(cuò):
socket.error: [Errno 98] Address already in use
大概意思就是地址被占用。因?yàn)閟ocket默認(rèn)是不支持地址復(fù)用的。為什么程序跑完了端口還是被占用著?這個(gè)問題就要TCP連接的“四次揮手”。
我們可能都有聽過TCP/IP中“三次握手,四次揮手”,前者我們可能會(huì)更加了解一點(diǎn),后者就不知道是什么樣子。我也是T_T,所以我決定弄懂它。
三次握手

(1)第一次握手:Client將標(biāo)志位SYN置為1,隨機(jī)產(chǎn)生一個(gè)值seq=J,并將該數(shù)據(jù)包發(fā)送給Server,Client進(jìn)入SYN_SENT狀態(tài),等待Server確認(rèn)。
(2)第二次握手:Server收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道Client請求建立連接,Server將標(biāo)志位SYN和ACK都置為1,ack=J+1,隨機(jī)產(chǎn)生一個(gè)值seq=K,并將該數(shù)據(jù)包發(fā)送給Client以確認(rèn)連接請求,Server進(jìn)入SYN_RCVD狀態(tài)。
(3)第三次握手:Client收到確認(rèn)后,檢查ack是否為J+1,ACK是否為1,如果正確則將標(biāo)志位ACK置為1,ack=K+1,并將該數(shù)據(jù)包發(fā)送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,Client和Server進(jìn)入ESTABLISHED狀態(tài),完成三次握手,隨后Client與Server之間可以開始傳輸數(shù)據(jù)了。
四次揮手

由于TCP連接時(shí)全雙工的,因此,每個(gè)方向都必須要單獨(dú)進(jìn)行關(guān)閉,這一原則是當(dāng)一方完成數(shù)據(jù)發(fā)送任務(wù)后,發(fā)送一個(gè)FIN來終止這一方向的連接,收到一個(gè)FIN只是意味著這一方向上沒有數(shù)據(jù)流動(dòng)了,即不會(huì)再收到數(shù)據(jù)了,但是在這個(gè)TCP連接上仍然能夠發(fā)送數(shù)據(jù),直到這一方向也發(fā)送了FIN。首先進(jìn)行關(guān)閉的一方將執(zhí)行主動(dòng)關(guān)閉,而另一方則執(zhí)行被動(dòng)關(guān)閉,上圖描述的即是如此。
(1)第一次揮手:Client發(fā)送一個(gè)FIN,用來關(guān)閉Client到Server的數(shù)據(jù)傳送,Client進(jìn)入FIN_WAIT_1狀態(tài)。
(2)第二次揮手:Server收到FIN后,發(fā)送一個(gè)ACK給Client,確認(rèn)序號(hào)為收到序號(hào)+1(與SYN相同,一個(gè)FIN占用一個(gè)序號(hào)),Server進(jìn)入CLOSE_WAIT狀態(tài)。
(3)第三次揮手:Server發(fā)送一個(gè)FIN,用來關(guān)閉Server到Client的數(shù)據(jù)傳送,Server進(jìn)入LAST_ACK狀態(tài)。
(4)第四次揮手:Client收到FIN后,Client進(jìn)入TIME_WAIT狀態(tài),接著發(fā)送一個(gè)ACK給Server,確認(rèn)序號(hào)為收到序號(hào)+1,Server進(jìn)入CLOSED狀態(tài),完成四次揮手。

在TCP/IP終止連接的四次握手中,當(dāng)最后的ACK回復(fù)發(fā)出后,有個(gè)2MSL的時(shí)間等待,MSL指一個(gè)片段在網(wǎng)絡(luò)中最大的存活時(shí)間,這個(gè)時(shí)間一般是30秒,所以基本上過60秒后就可以重新連接!
為什么要等待2MSL?是因?yàn)樵谧詈蟀l(fā)出ACK回復(fù)后,發(fā)送方不能確認(rèn)ACK是否被另一端正常收到,如果另一端沒有收到ACK回復(fù)的話,將會(huì)在1MSL后再次發(fā)送FIN片段。所以說發(fā)送方等待2MSL時(shí)間,也就是剛好它發(fā)ACK回復(fù)和對(duì)方發(fā)送FIN片段的時(shí)間,如果此時(shí)間內(nèi)都沒有再次收到FIN片段的話,發(fā)送方就假設(shè)對(duì)方已經(jīng)正常接收到了ACK回復(fù),此時(shí)它就會(huì)正常關(guān)閉連接!
以上就解釋了為什么會(huì)出現(xiàn)跑另一個(gè)程序時(shí)會(huì)出現(xiàn)地址占用的情況。
接下去就是解決方案:
如果并發(fā)連接請求過多的時(shí)候,即短時(shí)間內(nèi)連接請求很多,系統(tǒng)自動(dòng)釋放已占用端口的時(shí)間還沒有到,又有連接請求(可用的端口已經(jīng)被用完),所以還會(huì)出現(xiàn) Address already in use錯(cuò)誤提示),就會(huì)產(chǎn)生大量的TIME_WAIT狀態(tài)的連接。這種情況下就有必要調(diào)整下linux的TCP/IP內(nèi)核參數(shù),讓系統(tǒng)更快的釋放TIME_WAIT連接。對(duì)于并發(fā)連接量大的情況我們需要這樣設(shè)置:
用vi打開配置文件:
# vi /etc/sysctl.conf
然后,在這個(gè)文件中,加入下面的幾行內(nèi)容:
net.ipv4.tcp_syncookies = 1 # 這一行配置文件里如果有就不用添加了
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 5
最后輸入下面的命令,讓內(nèi)核參數(shù)生效:
# /sbin/sysctl -p