我正在尝试使用线程从客户端获取消息。我需要将消息发送到服务器代码所在的所有连接的客户端线程
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(消息)
答案 0 :(得分:1)
因此,您的情况的一个最小示例是:
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上。但是这种方式是相同的,即连接的某方“重置连接”。
现在,为什么呢?
很简单,这是因为连接的另一端(在这种情况下为客户端,因为问题出在服务器端)关闭了连接。为什么是这样?那是因为没有什么可以使客户保持生命。它所做的只是发送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
这将首先验证管道上是否有数据,然后将确定数据是实际数据还是底层套接字事件(例如,已发送RST/ACK
)。如果一切正常,您(应该)可能会收到没有错误的数据。只要您处理断开连接。
再次将其放入线程逻辑中,您应该会很好。