先說一般情況的解決:
lsof -i:8000 查出PID,然后 kill掉程序,接着就可以了

軟件重啟之后綁定沒有釋放,lsof -i:8080也查不出來占用的情況
再來個長連接版Python解決法:(軟件重啟之后綁定沒有釋放,lsof -i:8080也查不出來占用的情況)
OSError: [Errno 98] Address already in use
端口被占的處理: tcp_socket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
一般情況圖示:

解決后圖示:

完整示例代碼:
from socket import socket, SOL_SOCKET, SO_REUSEADDR
def main():
with socket() as tcp_socket:
# 防止端口占用
tcp_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 綁定端口
tcp_socket.bind(('', 8080))
# 監聽
tcp_socket.listen()
# 等待
client_socket, address = tcp_socket.accept()
# 收發消息
with client_socket:
print(f"[來自{address}的消息:\n")
msg = client_socket.recv(2048)
if msg:
print(msg.decode("utf-8"))
client_socket.send(
"""HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n<h1>哈哈哈</h1>"""
.encode("utf-8"))
if __name__ == "__main__":
main()
服務器版解決
from socket import SOL_SOCKET, SO_REUSEADDR
from socketserver import ThreadingTCPServer, BaseRequestHandler
class MyHandler(BaseRequestHandler):
def handle(self):
print(f"[來自{self.client_address}的消息:]")
data = self.request.recv(2048)
print(data)
self.request.send(
"HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n<h1>小明,晚上吃魚湯嗎?</h1>"
.encode("utf-8"))
def main():
# bind_and_activate=False 手動綁定和激活
with ThreadingTCPServer(('', 8080), MyHandler, False) as server:
# 防止端口占用
server.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.server_bind() # 自己綁定
server.server_activate() # 自己激活
server.serve_forever()
if __name__ == "__main__":
main()
解決前:

解決后:
這個就涉及到`TCP4次握手`相關的內容了,如果不是長連接,你先斷開客戶端,再斷開服務端就不會遇到這個問題了,具體問題下次繼續探討~
有時候會這樣簡化寫(雖然簡化了,但有時候也會出現端口占用的情況)
from socket import SOL_SOCKET, SO_REUSEADDR
from socketserver import ThreadingTCPServer, BaseRequestHandler
class MyHandler(BaseRequestHandler):
def handle(self):
print(f"[來自{self.client_address}的消息:]")
data = self.request.recv(2048)
print(data)
self.request.send(
"HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n<h1>小明,晚上吃魚湯嗎?</h1>"
.encode("utf-8"))
def main():
# 防止端口占用
ThreadingTCPServer.allow_reuse_address = True
with ThreadingTCPServer(('', 8080), MyHandler) as server:
server.serve_forever()
if __name__ == "__main__":
main()
源碼比較簡單,一看就懂:
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
# 看這
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
def server_bind(self):
# 看這
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
