J'utilise le module SocketServer pour un serveur TCP . Je rencontre un problème avec la fonction recv()
, car les paquets entrants ont toujours une taille différente, donc si je spécifie recv(1024)
(j'ai essayé avec une valeur plus grande et plus petite), il se bloque après 2 ou 3 requêtes car la longueur du paquet sera plus petite (je pense), puis le serveur reste bloqué jusqu'à l'expiration du délai imparti.
class Test(SocketServer.BaseRequestHandler):
def handle(self):
print "From:", self.client_address
while True:
data = self.request.recv(1024)
if not data: break
if data[4] == "\x20":
self.request.sendall("hello")
if data[4] == "\x21":
self.request.sendall("bye")
else:
print "unknow packet"
self.request.close()
print "Disconnected", self.client_address
launch = SocketServer.ThreadingTCPServer(('', int(sys.argv[1])),Test)
launch.allow_reuse_address= True;
launch.serve_forever()
Si le client envoie plusieurs requêtes sur le même port source, mais que le serveur reste bloqué, toute aide serait la bienvenue, merci!
La réponse de Larry Hastings donne d'excellents conseils d'ordre général sur les sockets, mais il existe quelques erreurs en ce qui concerne le fonctionnement de la méthode recv(bufsize)
dans le module socket Python.
Alors, pour clarifier, car cela peut être déroutant pour les autres qui cherchent de l'aide:
recv(bufsize)
n'est pas facultatif. Vous obtiendrez une erreur si vous appelez recv()
(sans le paramètre).recv(bufsize)
est une taille de maximum. Le recv retournera volontiers moins d'octets s'il y en a moins disponible.Voir la documentation pour plus de détails.
Maintenant, si vous recevez des données d'un client et que vous voulez savoir quand vous les avez toutes reçues, vous devrez probablement les ajouter à votre protocole - comme le suggère Larry. Voir cette recette pour les stratégies permettant de déterminer la fin du message.
Comme le souligne cette recette, pour certains protocoles, le client se déconnectera simplement une fois l'envoi des données terminé. Dans ces cas, votre boucle while True
devrait fonctionner correctement. Si le client not ne se déconnecte pas, vous devez trouver un moyen de signaler la longueur de votre contenu, de délimiter vos messages ou d'implémenter un délai d'attente.
Je serais heureux d'essayer de vous aider davantage si vous pouviez publier votre code client exact et une description de votre protocole de test.
Vous pouvez également utiliser recv(x_bytes, socket.MSG_WAITALL)
, qui semble ne fonctionner que sous Unix, et renverra exactement x_bytes
.
C’est la nature de TCP: le protocole remplit les paquets (la couche inférieure étant des paquets IP) et les envoie. Vous pouvez avoir un certain degré de contrôle sur le MTU (Maximum Transfer Unit).
En d'autres termes: vous devez concevoir un protocole qui se superpose à TCP où votre "délimitation de la charge utile" est définie. Par "délimitation de la charge utile", j'entends la façon dont vous extrayez l'unité de message que votre protocole prend en charge. Cela peut être aussi simple que "chaque chaîne terminée par un caractère NULL".
Notez que la raison exacte pour laquelle votre code est gelé est pas car vous définissez une taille de mémoire tampon request.recv () trop élevée. Ici est expliqué Que signifie taille du tampon dans socket.recv (buffer_size)
Ce code fonctionnera jusqu'à ce qu'il reçoive un message empty TCP (si vous imprimez ce message vide, il affichera b''
):
while True:
data = self.request.recv(1024)
if not data: break
Et notez qu'il n'y a que aucun moyen d'envoyer un message vide TCP. socket.send(b'')
ne fonctionnera tout simplement pas.
Pourquoi? Comme un message vide est envoyé uniquement lorsque vous tapez socket.close()
, votre script sera mis en boucle tant que vous ne fermerez pas votre connexion. Comme Hans L a souligné voici quelques de bonnes méthodes pour mettre fin au message .
Je sais que c'est vieux, mais j'espère que cela aidera quelqu'un.
En utilisant des sockets python classiques, j'ai découvert que vous pouvez envoyer et recevoir des informations par paquets en utilisant sendto et recvfrom
# tcp_echo_server.py
import socket
ADDRESS = ''
PORT = 54321
connections = []
Host = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Host.setblocking(0)
Host.bind((ADDRESS, PORT))
Host.listen(10) # 10 is how many clients it accepts
def close_socket(connection):
try:
connection.shutdown(socket.SHUT_RDWR)
except:
pass
try:
connection.close()
except:
pass
def read():
for i in reversed(range(len(connections))):
try:
data, sender = connections[i][0].recvfrom(1500)
return data
except (BlockingIOError, socket.timeout, OSError):
pass
except (ConnectionResetError, ConnectionAbortedError):
close_socket(connections[i][0])
connections.pop(i)
return b'' # return empty if no data found
def write(data):
for i in reversed(range(len(connections))):
try:
connections[i][0].sendto(data, connections[i][1])
except (BlockingIOError, socket.timeout, OSError):
pass
except (ConnectionResetError, ConnectionAbortedError):
close_socket(connections[i][0])
connections.pop(i)
# Run the main loop
while True:
try:
con, addr = Host.accept()
connections.append((con, addr))
except BlockingIOError:
pass
data = read()
if data != b'':
print(data)
write(b'ECHO: ' + data)
if data == b"exit":
break
# Close the sockets
for i in reversed(range(len(connections))):
close_socket(connections[i][0])
connections.pop(i)
close_socket(Host)
Le client est similaire
# tcp_client.py
import socket
ADDRESS = "localhost"
PORT = 54321
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ADDRESS, PORT))
s.setblocking(0)
def close_socket(connection):
try:
connection.shutdown(socket.SHUT_RDWR)
except:
pass
try:
connection.close()
except:
pass
def read():
"""Read data and return the read bytes."""
try:
data, sender = s.recvfrom(1500)
return data
except (BlockingIOError, socket.timeout, AttributeError, OSError):
return b''
except (ConnectionResetError, ConnectionAbortedError, AttributeError):
close_socket(s)
return b''
def write(data):
try:
s.sendto(data, (ADDRESS, PORT))
except (ConnectionResetError, ConnectionAbortedError):
close_socket(s)
while True:
msg = input("Enter a message: ")
write(msg.encode('utf-8'))
data = read()
if data != b"":
print("Message Received:", data)
if msg == "exit":
break
close_socket(s)