серверный скрипт не отправляет сообщение в python сокетов - PullRequest
0 голосов
/ 05 мая 2020

Я пытаюсь получить сообщение от клиента, используя потоки. Мне нужно отправить сообщение всем подключенным клиентским потокам, мой код сервера

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')

когда я запускаю сервер, сервер работает, но когда я запускаю серверный скрипт clientcript, выдает следующую ошибку:

enter image description here

при отладке кода построчно ошибка, возникающая в servercript, когда я пытаюсь отправить сообщение conn.send (sendlen) conn.send (message)

1 Ответ

1 голос
/ 05 мая 2020

Итак, минимальный пример вашей ситуации:

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)

Что не сработает, потому что на другом конце ничего нет.

Итак, решение вашей проблемы - не выходить из клиента. А также сделайте обработку ошибок. В этом топе c есть о чем рассказать, поскольку серверное и клиентское программное обеспечение имеет много подводных камней. Но в вашем случае, выполняя:

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, но я давно отказался от go, поэтому не Я сам не знаю) .

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 например был отправлен). Если все в порядке, вы, вероятно, сможете получать данные без ошибок. Пока вы обрабатываете разъединения.

Включите это в свой лог потока c снова, и вы должны быть готовы go.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...