web-dev-qa-db-fra.com

python enregistre plusieurs messages sur la même connexion

Ma question est plus générale que spécifique. Je souhaite implémenter une simple application client/serveur pour transmettre messages du client au serveur et obtenir des accusés de réception de la part du serveur.

Je me demandais ce que je devais prendre en compte lorsque je utilisais des sockets. Dois-je implémenter ma propre interface de communication et gérer la messagerie sur la même connexion ou créer une nouvelle connexion pour chaque message?

(supposez que le message actuel soit inférieur à BUFFER_SIZE)

Le code ressemble à ceci:

server.py

server_info = (Host, PORT)
sock = socket.socket(family=AF_INET, type=SOCK_STREAM)
sock.bind(server_info)
sock.listen(NUMBER_OF_SOCKETS)
try:
    while True:
        connection, client_address = sock.accept()
        try:
            while True:
                data = connection.recv(BUFFER_SIZE)
                print('message received: {data}'.format(data=data))
                connection.send("ok")
        finally:
            connection.close()

client.py

server_info = (Host, PORT)
sock = socket.socket(family=AF_INET, type=SOCK_STREAM)
sock.connect(server_info)
try:
    print("connection established")
    while True:
        print("Please enter a message you want to pass to the server")
        msg = raw_input()

        print('sending "{message}"'.format(message=msg))
        sock.send(msg)

        while True:
            data = sock.recv(constants.BUFFER_SIZE)
            print('received "{data}"'.format(data=data))
            break

finally:
    print('closing socket')
    sock.close()

ce code me permet de recevoir plusieurs messages côté serveur et d'envoyer plusieurs messages du côté client. Est-ce la bonne façon de le faire? Pour ce faire, je devais faire 2 boucles infinies côté client. Qu'en est-il de la fermeture de la connexion? Lorsque j'envoie un message de 0 octet, le serveur et le client sont bloqués.

merci beaucoup!

4
Dima Gimburg

L'ajout de deux types de serveurs client-client est multi-processus et l'autre asynchrone, ils font presque la même chose, le mode asynchrone est plus robuste. Lisez pourquoi ici: Threads vs. Async .

Mes exemples: Utilisation de processus multiples:

import multiprocessing
import socket
import time

Host = "0.0.0.0"
PORT = 9000


def handle(connection, address):

    try:
        while True:
            data = connection.recv(1024)
            connection.sendall(data + ' server time {}'.format(time.time()))
    except:
        pass
    finally:
        connection.close()


class Server(object):

    def __init__(self, hostname, port):
        self.hostname = hostname
        self.port = port

    def start(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind((self.hostname, self.port))
        self.socket.listen(1)

        while True:
            conn, address = self.socket.accept()
            process = multiprocessing.Process(
                target=handle, args=(conn, address))
            process.daemon = True
            process.start()


if __== "__main__":
    server = Server(Host, PORT)
    try:
        print 'start'
        server.start()
    except:
        print 'something wrong happened, a keyboard break ?'
    finally:
        for process in multiprocessing.active_children():
            process.terminate()
            process.join()
    print 'Goodbye'

Et le client pour cela:

    import sys
import threading
import time
import socket

SOCKET_AMOUNT = 100
Host = "localhost"
PORT = 9000


def myclient(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    sock.sendall(message)
    result = sock.recv(1024)
    print result + ' final clnt time {}'.format(time.time())
    sock.close()

if __== "__main__":
    thread_list = []
    for i in range(SOCKET_AMOUNT):
        msg = "Thread #{}, clnt time {}".format(i, time.time())
        client_thread = threading.Thread(
            target=myclient, args=(Host, PORT, msg))
        thread_list.append(client_thread)
        client_thread.start()

    waiting = time.time()
    [x.join() for x in thread_list]
    done = time.time()
    print 'DONE {}. Waiting for {} seconds'.format(done, done-waiting)

Le serveur suivant est beaucoup plus robuste !!! les données ne se perdent pas !!! le serveur:

import asyncore
import socket
import time
import logging
import json


class Server(asyncore.dispatcher):

    def __init__(self, Host, port):

        self.logger = logging.getLogger('SERVER')
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(('', port))
        self.listen(confjson.get('SERVER_QUEUE_SIZE', None))
        self.logger.debug('binding to {}'.format(self.socket.getsockname()))

    def handle_accept(self):
        socket, address = self.accept()
        self.logger.debug('new connection accepted')
        EchoHandler(socket)


class EchoHandler(asyncore.dispatcher_with_send):

    def handle_read(self):

        msg = self.recv(confjson.get('RATE', None))
        self.out_buffer = msg
        self.out_buffer += ' server recieve: {}'.format(time.time())
        if not self.out_buffer:
            self.close()


if __== "__main__":

    logging.basicConfig(level=logging.DEBUG,
                        format='%(name)s: %(message)s',
                        )
    with open('config.json', 'r') as jfile:
        confjson = json.load(jfile)
    try:
        logging.debug('Server start')
        server = Server(confjson.get('Host', None),
                        confjson.get('PORT', None))
        asyncore.loop()
    except:
        logging.error('Something happened,\n'
                      'if it was not a keyboard break...\n'
                      'check if address taken, '
                      'or another instance is running. Exit')
    finally:
        logging.debug('Goodbye')

Et le client asynchrone:

import asyncore
import socket
import time
import logging
import json


class Client(asyncore.dispatcher_with_send):

    def __init__(self, Host, port, message, pk):
        self.logger = logging.getLogger('CLIENT')
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.Host = Host
        self.port = port
        self.connect((Host, port))
        self.out_buffer = message
        self.clientID = pk
        self.logger.debug('Connected #{}'.format(self.clientID))

    def handle_close(self):
        self.close()

    def handle_read(self):
        rec_msg = self.recv(confjson.get('RATE', None))
        self.logger.debug('#{}, {} back at client {}'.format(self.clientID,
                                                             rec_msg,
                                                             time.time()
                                                             )
                          )
        self.close()


if __== "__main__":
    logging.basicConfig(level=logging.DEBUG,
                        format='%(name)s: %(message)s',
                        )

    with open('config.json', 'r') as jfile:
        confjson = json.load(jfile)
    clients = []
    for idx in range(confjson.get('SOCKET_AMOUNT', None)):
        msg = "Start: {}".format(time.time())
        clients.append(Client(confjson.get('Host', None),
                              confjson.get('PORT', None),
                              msg,
                              idx)
                       )
    start = time.time()
    logging.debug(
        'Starting async loop for all connections, unix time {}'.format(start))
    asyncore.loop()
    logging.debug('{}'.format(time.time() - start))

et un petit fichier de configuration:

{
    "Host": "127.0.0.1",
    "PORT": 5007,
    "RATE": 8096,
    "SERVER_QUEUE_SIZE": 16,
    "SOCKET_AMOUNT": 100
}
4
Ohad the Lad

Par défaut, dans une communication bidirectionnelle, un client peut savoir à quel moment il a fini d'envoyer, mais il ne peut pas savoir s'il a déjà reçu. De plus, le serveur ne peut pas savoir si le client a fini d'envoyer.

Code:

def recv_end(the_socket):
    End='SERVER WRONG MARKER'
    total_data=[];data='';got_end=False
    while True:
            data=the_socket.recv(8192)
            if not data: break
            if End in data:
                total_data.append(data[:data.find(End)])
                got_end=True
                break
            total_data.append(data)
            if len(total_data)>1:
                #check if end_of_data was split
                last_pair=total_data[-2]+total_data[-1]
                if End in last_pair:
                    total_data[-2]=last_pair[:last_pair.find(End)]
                    total_data.pop()
                    got_end=True
                    break
    return (got_end,''.join(total_data))

def basic_server(sock):
    got=[]
    got_end,data = recv_end(sock)
    if not got_end:  
        sock.send('ERROR:no end!') #<--- not possible w/close()
    else: sock.sendall(data*2)
    sock.shutdown(1)
    sock.close()

import socket
Port=4444
def start_server():
    sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sock.bind(('',Port))
    sock.listen(5)
    print 'started on',Port
    while True:
        newsock,address=sock.accept()
        basic_server(newsock)

def send_data(data):
    sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sock.connect(('localhost',Port))
    print 'connected'
    sock.sendall(data+'CLIENT WRONG MARKER')
    print 'sent',data
    sock.shutdown(1)
    print 'shutdown'
    result=[]
    while True:
       got=sock.recv(2)
       if not got: break
       result.append(got)
    sock.close()
    return ''.join(result)

if __name__=='__main__':
    start_server()

Vous pouvez faire quelque chose comme mettre un nombre d'octets devant les données, ou avoir un marqueur de fin afin que le serveur puisse savoir s'il possède tous les octets.

Cependant, cela pose un problème. Que se passe-t-il si le nombre d'octets est incorrect ou si le marqueur de fin n'arrive jamais? Avec un socket.close(), le serveur ne peut pas dire au client: "Étrange. Vous avez terminé de m'envoyer des données, mais je n'ai pas reçu toutes les données", car la connexion client n'est pas laissée ouverte une fois l'envoi du client terminé.

Avec un socket.shutdown(1), le serveur peut toujours informer le client que quelque chose ne va pas et prendre les mesures appropriées.

La commande d'arrêt a trois options : 0 = done receiving, 1 = done sending, 2 = both

Dans le code ci-dessus se concentre sur 1, pour se débarrasser de l’implication envoyer dans une opération rapprochée. Notez comment dans send_data l'opération de fermeture est (relativement) loin de l'arrêt. Cela permet au serveur d'indiquer au client tout commentaire de séparation.

Il suffit d’exécuter le code pour démarrer le serveur. Le serveur est défini sur recv uniquement 2 octets s à la fois à des fins de démonstration (cela devrait être quelque chose comme 8192). Pour lui envoyer des données, importez-le (appelez-le shut_srv ou autre) et appelez send_data pour le côté client.

data=('a1234','b1234','c1234','d1234','e1234') for d in data: print shut_srv.send_data(d)

Vous obtiendrez une réponse du type: connected sent a1234 shutdown ERROR:no end! connected sent b1234 shutdown ERROR:no end! connected sent c1234 shutdown ERROR:no end! connected sent d1234 shutdown ERROR:no end! connected sent e1234 shutdown ERROR:no end!

Si vous faites les marqueurs les mêmes. La réponse devrait être: connected sent a123456789 shutdown a1234a1234 connected sent b1234 shutdown b1234b1234 connected sent c1234 shutdown c1234c1234 connected sent d1234 shutdown d1234d1234 connected sent e1234 shutdown e1234e1234

2
Shivkumar kondi

J'ai eu le même problème, essayez ce serveur:

import socket
import select
open_client_sockets = []
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 8001))

server_socket.listen(1)
(new_socket1, address1) = server_socket.accept()
open_client_sockets.append(new_socket1)

while True:
    rlist, wlist, xlist = select.select(open_client_sockets, open_client_sockets, [])
    for current_socket in rlist:
        data = current_socket.recv(4096)
        if data != '':
            print "given data: ", str(data)
0
Idan Rotbart