2017-02-14 5 views
2

Meine Frage ist eher allgemein als spezifisch. Ich möchte eine einfache Client-Server-Anwendung implementieren, nur um Nachrichten vom Client an den Server zu liefern und Bestätigungen zurück vom Server zu erhalten.Python-Sockets mehrere Nachrichten auf derselben Verbindung

Ich frage mich, was muss ich bei Sockets beachten, muss ich meine eigene Kommunikationsschnittstelle implementieren und die Nachrichten auf der gleichen Verbindung verwalten oder eine neue Verbindung für jede Nachricht erstellen?

(bitte davon ausgehen, dass die Nachricht jetzt für als BUFFER_SIZE weniger ist)

Der Code so etwas wie dieses:

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

Dieser Code gibt mir die Möglichkeit, mehrere Nachrichten auf dem Server si zu empfangen de und senden mehrere Nachrichten von der Client-Seite. Ist es der richtige Weg? Ich musste 2 Endlosschleifen auf der Client-Seite machen, um das zu tun, was ist mit dem Schließen der Verbindung? Wenn ich eine 0-Byte-Nachricht sende, bleiben sowohl der Server als auch der Client stecken.

vielen dank!

+1

Sie würde in der Regel ein eigenes Client-Server-Protokoll nicht umsetzen, sondern verwendet eine Webserver. Zum Beispiel [Apache httpd mit WSGI] (https://docs.python.org/2/howto/webservers.html). – Andomar

+0

Ich möchte nicht wirklich einen Webserver, ich möchte nur zwischen zwei Hosts kommunizieren, senden und empfangen von Nachrichten. Ich habe nicht nach Alternativen gesucht, danke. –

+0

Webserver werden für viel mehr als Webseiten verwendet. Die Hintergrundbenachrichtigung erfolgt, indem der Webserver eine JSON- oder XML-Antwort für eine Anfrage bereitstellt. – Andomar

Antwort

1

In der bidirektionalen Kommunikation kann ein Client standardmäßig wissen, wann das Senden erfolgt ist, aber er kann nicht wissen, ob er empfangen wurde. Und auch der Server kann nicht wissen, ob der Client das Senden beendet hat.

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

Sie etwas tun könnte, wie ein Zählbyte setzen vor den Daten, oder ein Ende Marker haben, damit der Server wissen kann, wenn sie alle Bytes bekam.

Das führt jedoch zu einem Problem. Was ist, wenn die Anzahl der Bytes falsch ist oder die Endmarkierung nie ankommt? Mit einer socket.close() kann der Server den Client nicht sagen, "Seltsam. Sie sind fertig mit dem Senden von Daten an mich, aber ich habe nicht alle Daten erhalten", da die Client-Verbindung nicht offen bleibt, nachdem der Client das Senden abgeschlossen ist.

Mit einem socket.shutdown(1) kann der Client immer noch durch den Server gesagt werden, dass etwas nicht in Ordnung war und geeignete Maßnahmen ergreifen.

Der Befehl shutdown hat drei Optionen: 0 = done receiving, 1 = done sending, 2 = both

In dem obigen Code auf 1 fokussiert, den implict in einem engen Betrieb senden loszuwerden. Beachten Sie, dass in send_data die Close-Operation (relativ) weit vom Shutdown entfernt ist. Dadurch kann der Server dem Client einen Trennkommentar mitteilen.

Führen Sie einfach den Code aus, um den Server zu starten. Der Server ist nur auf 2 Byte s recv für Demonstrationszwecke (es sollte etwas wie 8192 sein). Um Daten zu senden, importieren Sie es (nennen Sie es shut_srv oder was auch immer) und rufen Sie send_data für die Clientseite auf.

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

Sie erhalten eine Antwort wie erhalten: 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!

Wenn Sie die Markierungen das gleiche machen. Die Antwort sollte: 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

ein zwei Arten von Server-Client Hinzufügen eines über Multi-Prozess und das andere ist asynchron, sie fast die gleiche Sache, die asynchrone ein robuster ist, lesen Sie, warum hier: Threads vs. Async.

Meine Beispiele: Multi-Prozess verwenden:

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 __name__ == "__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' 

Und der Kunde für sie:

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 __name__ == "__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) 

Der nächste Server viel robuster ist !!! Daten gehen nicht verloren !!! der Server:

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 __name__ == "__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') 

Und der Asynchron-Client:

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 __name__ == "__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)) 

und eine kleine Konfigurationsdatei:

{ 
    "HOST": "127.0.0.1", 
    "PORT": 5007, 
    "RATE": 8096, 
    "SERVER_QUEUE_SIZE": 16, 
    "SOCKET_AMOUNT": 100 
} 
Verwandte Themen