簡體   English   中英

服務器腳本未在 python sockets 中發送消息

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM