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!
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
}
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
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)