![](/img/trans.png)
[英]Python Sockets - Sending a packet to a server and waiting for a response
[英]server script is not sending message in python sockets
我正在嘗試使用線程從客戶端獲取消息。我需要將消息發送到我的服務器代碼所在的所有連接的客戶端線程
import socket
import threading
class seversocket:
def __init__(self):
self.header=64
self.port=5055
self.format='utf-8'
self.hostname=socket.gethostname()
self.host=socket.gethostbyname(self.hostname)
self.close='close'
self.messagelist=['world']
self.addr=(self.host,self.port)
self.soc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.soc.bind(self.addr)
print('server socket created and binded;')
self.listener()
def messageupdate(self,msgcount):
result_list=[]
if(len(self.messagelist)>msgcount):
result_list=self.messagelist[msgcount:]
return result_list,len(self.messagelist)
else:
return None,msgcount
def clients(self,conn,addr):
print(f'connected to {addr}')
connected=True
msgcount=0
while connected:
msglen=conn.recv(self.header).decode(self.format)
if msglen:
msglen=int(msglen)
msg = conn.recv(msglen).decode(self.format)
if msg == self.close:
connected=False
print(f"[{addr}]{msg}")
li,msgcount= self.messageupdate(msgcount)
if li is not None:
for i in li:
print(i)
message=i.encode(self.format)
print(message)
msglen=len(message)
print(msglen)
sendlen=str(msglen).encode(self.format)
sendlen += b' '*(self.header-len(sendlen))
conn.send(sendlen)
conn.send(message)
conn.close()
def listener(self):
self.soc.listen()
print(f'socket is listening to {self.host}')
while True:
conn,addr=self.soc.accept()
thread=threading.Thread(target=self.clients,args=(conn,addr))
thread.start()
def listappened(self,mes):
self.messagelist.append(mes)
return None
seversocket()
我的客戶腳本是
import socket
import threading
class clientsocket:
def __init__(self):
self.header=64
self.port=5055
self.format='utf-8'
self.hostname=socket.gethostname()
self.host=socket.gethostbyname(self.hostname)
self.close='close'
self.addr=(self.host,self.port)
self.client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.client.connect(self.addr)
self.check=threading.Thread(target=self.checkformessgae)
def send(self,msg):
message=msg.encode(self.format)
msglen=len(message)
sendlen=str(msglen).encode(self.format)
sendlen += b' '*(self.header-len(sendlen))
self.client.send(sendlen)
self.client.send(message)
def checkformessgae(self):
msglen=self.client.recv(self.header).decode(self.format)
if msglen:
msglen=int(msglen)
msg =self.client.recv(msglen).decode(self.format)
if msg == self.close:
connected=False
print(f"[{addr}]{msg}")
k=clientsocket()
k.send('hello')
當我運行服務器時,服務器正在運行,但是當我運行 clientscript 服務器腳本時拋出以下錯誤
當我逐行調試代碼時,當我嘗試發送消息 conn.send(sendlen) conn.send(message) 時出現在服務器腳本中的錯誤
因此,您的情況的一個最小示例是:
import socket, time
import threading
header = 64
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind(('', 5055))
soc.listen()
conn, addr = soc.accept()
def loop(conn):
while True:
msglen = conn.recv(header).decode('UTF-8')
if msglen:
msglen = int(msglen)
msg = conn.recv(msglen).decode('UTF-8')
print(msg)
conn.send(b'10')
conn.send(b'0123456789')
time.sleep(0.025)
thread = threading.Thread(target=loop, args=(conn,))
thread.start()
現在,因為線程與此無關,我也可以將代碼簡化為實際問題,即套接字部分。
(這可能來自經驗,也可能只是來自在線搜索問題,您會發現大量資源涵蓋“對等連接重置”) 。
因此,如果我們將其簡化為問題的核心組成部分,它看起來像這樣:
import socket
# No threading needed to reproduce the problem
header = 64
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind(('', 5055))
soc.listen()
conn, addr = soc.accept()
while True: # Replaced thread with a simple while loop
msglen = conn.recv(header).decode('UTF-8')
if msglen:
msglen = int(msglen)
msg = conn.recv(msglen).decode('UTF-8')
print(msg)
conn.send(b'10')
conn.send(b'0123456789')
這給了我:
PS C:\Users\anton> python .\server.py
hello
Traceback (most recent call last):
File ".\server.py", line 12, in <module>
msglen = conn.recv(header).decode('UTF-8')
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
現在,我的錯誤信息略有不同。 那是因為我在 windows 而你在 Linux。 但是 jizt 是相同的,連接的某些方“重置連接”。
現在,這是為什么呢?
很簡單,這是因為連接的另一端(在這種情況下是客戶端,因為問題出在服務器端)關閉了連接。 為什么是這樣? 那是因為沒有什么可以讓客戶活着。 它所做的只是發送hello
,檢查是否有任何傳入數據並執行print(f"[{addr}]{msg}")
。 之后,客戶退出。
因此,當這種情況發生時,您正在嘗試(多次) :
conn.recv(msglen)
這是行不通的,因為另一端什么都沒有。
因此,解決您的問題的方法是,不要退出客戶端。 而且,做錯誤處理。 由於服務器端和客戶端軟件存在很多缺陷,因此本主題要涵蓋的內容很多。 但在你的情況下,做:
try:
ConnectionResetError
except ConnectionResetError:
self.close()
或者,您可以切換到一種對故障更友好的方式,首先通過select庫檢查是否有數據。
import socket, select
header = 64
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind(('', 5055))
soc.listen()
conn, addr = soc.accept()
while True:
readable, writable, exceptional = select.select([conn], [conn], [])
print(readable)
if readable:
msglen = conn.recv(header).decode('UTF-8')
if msglen:
msglen = int(msglen)
msg = conn.recv(msglen).decode('UTF-8')
print(msg)
# Could technically check writable here, just to be sure
print(writable)
conn.send(b'10')
conn.send(b'0123456789')
但即便如此,由於檢查和嘗試閱讀之間的時間問題,這可能會失敗。 這就是epoll的用武之地,它可以觸發事件並且更加靈活(也許你可以用select.select
實現相同的效果,但我很久以前就放棄了,所以不了解自己) 。
import socket, select
header = 64
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind(('', 5055))
soc.listen()
conn, addr = soc.accept()
poll_obj = select.epoll()
poll_obj.register(conn.fileno(), select.EPOLLIN | select.EPOLLHUP)
while True:
for fileno, eventid in poll_obj.poll(0.5):
if eventid == select.EPOLLIN:
msglen = conn.recv(header).decode('UTF-8')
if msglen:
msglen = int(msglen)
msg = conn.recv(msglen).decode('UTF-8')
print(msg)
conn.send(b'10')
conn.send(b'0123456789')
else:
print('Client disconnected (prob eventid 25)')
break
這將首先驗證 pipe 上有數據,然后確定數據是實際數據還是底層套接字事件(例如發送了RST/ACK
)。 如果一切正常,您(應該)可能可以無錯誤地接收數據。 只要您處理斷開連接。
再次將其放入您的線程邏輯中,您應該對 go 很好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.