服务器脚本未在python套接字中发送消息

时间:2020-05-05 14:49:31

标签: 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服务器脚本时,会引发以下错误

enter image description here

当我逐行调试代码时,尝试发送消息时,服务器脚本中出现错误 发送(发送) conn.send(消息)

1 个答案:

答案 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)。如果一切正常,您(应该)可能会收到没有错误的数据。只要您处理断开连接。

再次将其放入线程逻辑中,您应该会很好。