简体   繁体   English

python 多线程服务器无法从客户端接收数据

[英]python multithreaded server unable to receive data from client

In the frame of our course, our teacher asked us to write a client-server program, where the server split two matrices that it wants to multiply then send them to the client and the client should calculate their part of the result and send it back to the server.在我们课程的框架中,我们的老师要求我们编写一个客户端-服务器程序,其中服务器拆分两个要相乘的矩阵,然后将它们发送给客户端,客户端应该计算它们的部分结果并将其发送回到服务器。

I succeed to divide the matrix and send it to clients but my problem is that my client cannot send back the results to the server.我成功划分矩阵并将其发送给客户端,但我的问题是我的客户端无法将结果发送回服务器。 When I try to receive any message at the server-side, my client no longer receives the matrix to compute.当我尝试在服务器端接收任何消息时,我的客户端不再接收要计算的矩阵。

Here is my server code这是我的服务器代码

!/usr/bin/env python
# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM, timeout
from threading import Thread
import numpy as np
import pickle
buf = 4096
class ErrorLevels:
 
    OK = "OK"
    ERROR = "ERREUR"
 
 
class Server(Thread):
 
 
    def __init__(self):
 
        Thread.__init__(self)
 
        self.socket = socket(AF_INET, SOCK_STREAM)
        self.socket.bind(("localhost", 2020))
        self.socket.settimeout(0.5)
 
        self.running = False
        self.client_pool = []
 
    def client_handling_stopped(self, client, error_level, error_msg):
 
        print("Le gerant de {} s'est arrete avec le niveau d'erreur {} ({})".format(client.address[0],error_level,error_msg))
 
        self.clean_up()
 
        # self.log_connection_amount()
 
    def log_connection_amount(self):
 
        print("Il y a maintenant {} client(s) connecte(s)".format(len(self.client_pool)))
 
    def stop(self):
 
        print("Arrêt du serveur")
 
        for client in self.client_pool:
            client.close_connection()
 
        self.running = False
 
    def clean_up(self):
        """
        Enleve tous les gérants de clients innactifs de la liste des gerants de clients
        """
        self.client_pool = [client for client in self.client_pool if client.alive]
    #le serveur genere le calcul a envoyer aux clients
     #generation de matrices
    def matrice_aleatoire(self,intervalle, ligne, colonne):
        matrice = np.random.randint(intervalle, size=(ligne, colonne))
        return matrice


    def run(self):
        A = self.matrice_aleatoire(10,100,100)
        B = self.matrice_aleatoire(10,100,100)
#code fonctionnnant pour 10 clients
    #division de A  en 10 sous matrices de 10 lignes et envoie aux clients
        C = np.vsplit(A, 10)
            #dictionnaire a envoyer a chaque client
        data = []
        for i in range(10):
            dic = {'num':i,'partA':C[i],'partB':B}
            data.append(dic)
        print("Démarrage du serveur\nAttente des connexions clients...")
 
        self.running = True
 
        self.socket.listen(5)
        i=-1
 
        while self.running:
 
            try:
 
                client, address = self.socket.accept()
                i=i+1
            except timeout:
                continue # on retourne au début de la boucle jusqu'à avoir un client
 
            print("Connexion depuis {}".format(address))
            #envoie et reception du calcul aux clients connnectes
            #actuellement 10 clients
            client_handling = ClientHandling(client, address,data[i], self.client_handling_stopped)
            self.client_pool.append(client_handling)
            client_handling.start()
            
            
                
 
            # self.log_connection_amount()
 
 #classe d'ojbets thread pour gerer les connections clients
class ClientHandling(Thread):
 
    def __init__(self, client, address,data, exit_callback):
 
        Thread.__init__(self)
 
        self.client = client
        self.address = address
        self.data = data
        self.exit_callback = exit_callback # une fonction qui devra être appelée lorsque cet objet sera devenu inactif
        self.alive = True
 
    def _stop(self, error_level, error_msg):
 
        self.alive = False
        self.close_connection()
        self.exit_callback(self, error_level, error_msg)
 
    def close_connection(self):
 
        self.alive = False
        self.client.close()
        print("Fin de la communication avec {}".format(self.address))
   
   
    def run(self):
 
        try:
 #envoie du calcul
            print("debut envoie du calcul")
            data_string = pickle.dumps(self.data)
            self.client.sendall(data_string)
            print("fin envoie")
 #reception resultat
            ''' 
            here is the problem when i try to receive the result 
            pick_ = b''
            while 1:
                dat = self.client.recv(buf)
                pick_ += dat
                print("reception resultat")
                if not dat:break
            res = pickle.loads(dat)
            print("fin reception")
           # print(res)'''
            
  #quelques exceptions possibles
        except ZeroDivisionError:
 
            self._stop(ErrorLevels.ERROR, "Une division par zero tente")
 
        except ConnectionAbortedError:
 
            if self.alive: # innatendu
                self._stop(ErrorLevels.ERROR, "La connexion abandonnee")
 
            else: # on est dans le cas où le gérant est volontairement arrêté
                return # on arrête donc tout, plus besoin de faire quoi que ce soit
 
        self._stop(ErrorLevels.OK, "Le client a ferme la connection")
 
 
try:
 #lancement du thread serveur
    server = Server()
    server.start()
 
    while True: continue
 
except KeyboardInterrupt:
 
    server.stop()
    server.join()

here is my client.py这是我的client.py

import socket
from threading import Thread
#import numpy as np
import pickle
hote = "localhost"
port = 2020
buf = 4096
connexion_avec_serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connexion_avec_serveur.connect((hote, port))
print("Connexion établie avec le serveur sur le port {}".format(port))
#thread pour le calcul du client 
class Calcul(Thread):
    def __init__(self):
        Thread.__init__(self)
    #fonction qui extrait les donnees et multiplie
    def multmat(self,data):
        num = data['num']
        A = data['partA']
        B = data['partB']
        C = A @ B
        resul = {'num':num,'partC':C}
        return resul
    def run(self):
        #reception calcul
        pick_str = b''
        while 1:
            data = connexion_avec_serveur.recv(buf)
            pick_str += data
            if not data:break
            #connexion_avec_serveur.close()
        dic = pickle.loads(pick_str)
        #print(dic)
       #calcul du produit
        res = self.multmat(dic)
        print(res)
        #envoie du resultat du calcul
        data_string = pickle.dumps(res)
        connexion_avec_serveur.sendall(data_string)       
cal = Calcul()
cal.start()
cal.join()
connexion_avec_serveur.close() 

The main problem is that the client does not know when the complete message from the server has been received.主要问题是客户端不知道何时收到来自服务器的完整消息。 The receiving code expects the server to close the connection before it can process the incoming data.接收代码希望服务器在处理传入数据之前关闭连接。 However, the server can not close the connection because it is waiting for the client to send a response over the same connection.但是,服务器无法关闭连接,因为它正在等待客户端通过同一连接发送响应。

The client is blocked at data = connexion_avec_serveur.recv(buf) until the server closes the connection, or some other network event occurs that severs that connection.客户端在data = connexion_avec_serveur.recv(buf)处被阻塞,直到服务器关闭连接,或者发生其他一些切断该连接的网络事件。 Meanwhile the server is also blocked at dat = self.client.recv(buf) awaiting a response from the client - there is a deadlock.同时,服务器也被阻塞在dat = self.client.recv(buf)等待来自客户端的响应 - 存在死锁。

The solution is to arrange for the client to know when it has received the complete message from the server, which means adding some protocol.解决方案是安排客户端知道它何时收到来自服务器的完整消息,这意味着添加一些协议。 One way is for the sever to append a sentinel value to signal the end of the message, and for the client to watch for that sentinel.一种方法是让服务器向 append 发送一个标记值来表示消息的结束,并让客户端监视该标记。 Another way is for the server to prepend to the message the length of the payload, in this case the length of the pickled data, which I show here.另一种方法是让服务器在消息前面加上有效负载的长度,在这种情况下是腌制数据的长度,我在这里展示。

For the client change the run() function:对于客户端更改run() function:

import struct

    def run(self):
        # First 4 bytes are the length of the payload
        data = connexion_avec_serveur.recv(4)
        msglen = struct.unpack('!L', data)[0]
        print(f'Length of payload {msglen = }')
        payload = []

        while msglen > 0:
            print(f'{msglen = } calling recv...')
            data = connexion_avec_serveur.recv(buf)
            print(f'received {len(data)} bytes')
            payload.append(data)
            msglen -= len(data)

        print(f'total bytes read {sum(len(s) for s in payload)}')
        dic = pickle.loads(b''.join(payload))
        #print(dic)
       #calcul du produit
        res = self.multmat(dic)
        print(res)
        #envoie du resultat du calcul
        data_string = pickle.dumps(res)
        connexion_avec_serveur.sendall(data_string)

And for the server:对于服务器:

import struct

    def run(self):

        try:
 #envoie du calcul
            print("debut envoie du calcul")
            data = pickle.dumps(self.data)
            # prepend message with length of the pickled data
            msg = struct.pack(f'!L{len(data)}s', len(data), data)
            print(f'sending {len(msg)} bytes to client')
            self.client.sendall(msg)

            print("fin envoie")
 #reception resultat

            pick_ = b''
            while True:
                print('calling recv()')
                dat = self.client.recv(buf)
                print(f'recv() returned {len(dat)} bytes')
                pick_ += dat
                print("reception resultat")
                if not dat:
                    break

            res = pickle.loads(pick_)
            print(f'{res = }')
            print("fin reception")

  #quelques exceptions possibles
        except ZeroDivisionError:

            self._stop(ErrorLevels.ERROR, "Une division par zero tente")

        except ConnectionAbortedError:

            if self.alive: # innatendu
                self._stop(ErrorLevels.ERROR, "La connexion abandonnee")

            else: # on est dans le cas où le gérant est volontairement arrêté
                return # on arrête donc tout, plus besoin de faire quoi que ce soit

        self._stop(ErrorLevels.OK, "Le client a ferme la connection")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Python从服务器接收数据 - Receive data from server in Python 当没有数据要从服务器接收时,python客户端挂起,并且在该线程中挂起,而没有让客户端发送 - python Client hangs when no data to receive from server and hangs in that thread w/o letting client send 对于多人游戏,只有一个客户端在多线程服务器中接收消息 - ONLY one client receive message in multithreaded server for a multiplayer game 使用python从客户端发送和接收数据 - sending and receive data from client using python python中具有Twisted框架的聊天服务器无法从Flash Client接收数据 - Chat server with Twisted framework in python can't receive data from flash client 套接字Python(服务器)到Java(客户端)的连接Java无法从主机接收数据 - Socket Python(Server) to Java(Client) connection Java is not able to receive data from host Python 客户端无法将数据发送到 Ruby 服务器 - Python Client Unable to send data to Ruby Server rabbitmq服务器未从python客户端接收消息 - rabbitmq server does not receive messages from a python client android客户端无法收到python服务器的响应 - The android client can't receive a response from the python server Android客户端不会从python服务器接收 - Android client won't receive from python server
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM