tcp異常關閉後端口被佔用情況的解決

轉載鏈接:https://blog.pinkd.moe/others/2018/07/29/TCP-reuse

解決進程結束後端口仍然被佔用的問題

socket分配

一個服務端進程向操作系統申請一個 socket 來監聽,但是當進程退出後,還未關閉的連接不會立即消失,而是會留給操作系統處理。操作系統會嘗試關閉這個連接。但是如果關閉時出現問題,這個連接就會一直處於 TIME_WAIT 或其他非正常狀態,而這是相應的端口還處於佔用狀態,如果這個時候再重新啓動這個服務端程序,就會出現地址被佔用的情況。

案例

測試代碼:

import socket

s = socket.socket()
s.bind(('0.0.0.0', 12345))
s.listen()
(client, addr) = s.accept()
print(client)
print(addr)

使用 nc 進行連接:

nc 127.0.0.1 12345

服務端會打印 clientaddr ,然後正常退出,但是此時使用 netstat -anop | grep 12345 查看,發現對應連接並沒有被立即釋放

tcp        0      0 127.0.0.1:12345         127.0.0.1:59408         TIME_WAIT   -                    timewait (28.18/0/0)

此時再次啓動服務端,發現報錯了:

Traceback (most recent call last):
  File "server.py", line 5, in <module>
    s.bind(('0.0.0.0', 12345))
OSError: [Errno 98] Address already in use

解決方案

使用 setsockopt :

import socket

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 12345))
s.listen()
(client, addr) = s.accept()
print(client)
print(addr)

此時就不會出現地址被佔用的提示了

c 中也有一樣的方法,只是方法聲明不同, c 版的用法爲

struct sockaddr_in addr;

addr.sin_family = AF_INET;
addr.sin_port = htons(12345);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int reuse = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
bind(s, (struct sockaddr *) &addr, sizeof(addr))
listen(s, )

struct sockaddr_in in_addr;
int len = sizeof(in_addr);
int client = accept(socket, (struct sockaddr *) in_addr, &len);
//handle client
//...

其他

  • 發現除了 SO_REUSEADDR 之外還有一個 SO_REUSEPORT 的選項,查詢後得知是 BSD 獨有的, Linux 並不能用
  • 如果是客戶端綁定端口用這個屬性可能會出現剛連接上服務器就莫名其妙收到一個 FIN 的問題,導致其立即關閉,因此客戶端使用此選項時需注意

參考鏈接

小議socket關閉
PortProtection

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章