2011-01-14 3 views
2

J'ai écrit un programme de serveur client dans lequel le serveur envoie un programme au client et le client exécute le programme reçu. Dans ce cas, il s'agit d'un programme de dessin au trait dans OpenGL. Le problème est que lors de l'exécution du serveur et du client, l'ensemble du programme, c'est-à-dire l'envoi du programme à l'exécution du serveur et du client, a lieu parfois, mais parfois l'exécution n'a pas lieu. Le client se connecte et se bloque. Le programme ne s'arrête pas non plus. La même chose fonctionne dans le système de mes amis, mais dans le mien, cela ne fonctionne que très rarement (la plupart du temps). Quelle pourrait être la raison? Est-ce que je manque quelque chose?Problème de programmation réseau

Mon code serveur:

from OpenGL.GLUT import * 
from OpenGL.GLU import * 
from OpenGL.GL import * 
import sys 
import threading 
import os 
import socket 

class ClientThread (threading.Thread): 
# Override Thread's __init__ method to accept the parameters needed: 
def __init__ (self, channel, details): 
    self.channel = channel 
    self.details = details 
    threading.Thread.__init__ (self) 

#Codes to be executed when thread is executed: 
def run (self): 
    a1=self.channel.recv(1024) 
    print "client says:"+a1 
    print 'Received connection:', self.details [ 0 ] 
    finp = open("stringcheck.py","r") 
    #self.channel.send("#start\n") 
    info = finp.readlines() 
    for record in info: 
    self.channel.send(record) 
    #self.channel.send(info) 
    self.channel.send("#p") 


server = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
server.bind (('127.0.0.1', 4500)) 
#Listens for connections made to socket. 
#Specifies maximum number of queued connection. 
server.listen (5) 
while True: 
channel, details = server.accept() 
#Create an instance of thread class and call its start method. 
ClientThread (channel, details).start() 

Mon code client:

from OpenGL.GLUT import * 
from OpenGL.GLU import * 
from OpenGL.GL import * 
OpenGL.ERROR_CHECKING=False 
import os 
import socket 
import threading 
import thread 
import subprocess 

class ConnectionThread(threading.Thread): 

def run (self): 

    client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
    client.connect(('127.0.0.1', 9000)) 
    client.send("connected") 
    a=client.recv(1024) 
    b=a 
    f=1 
    while f: 
    print 'sdf' 
    a = client.recv(1024) 
    print 'qwe' 
    if a=="#p": 
    f=0 
    break 
    b+=a 
    print b 
    exec(b) 

    client.close() 
ConnectionThread().start() 
from OpenGL.GLUT import * 
from OpenGL.GLU import * 
from OpenGL.GL import * 
OpenGL.ERROR_CHECKING=False 
import os 
import socket 
import threading 
import thread 
import subprocess 

class ConnectionThread(threading.Thread): 

def run (self): 

    client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
    client.connect(('127.0.0.1', 9000)) 
    client.send("connected") 
    a=client.recv(1024) 
    b=a 
    f=1 
    while f: 

    a = client.recv(1024) 
    print 'qwe' 
    if a=="#p": 
    f=0 
    break 
    b+=a 
    print b 
    exec(b) 

    client.close() 
ConnectionThread().start() 
+5

Indentation à espace unique ... pouvez-vous lire ceci? Sans piquer tes yeux? – thkala

+2

Y a-t-il une raison pour laquelle vous devez implémenter les threads vous-même, au lieu de simplement utiliser par exemple. Tordu? – Kimvais

+0

@Kimvais: il utilise le module 'threading' intégré. il est beaucoup plus léger que Twisted, et il est livré en standard dans toutes les distributions python. Alors, où est le problème ? –

Répondre

4

1. un socket TCP est un flux(paquets sont un détail de mise en œuvre)

la La pile réseau est optimisée afin de limiter le nombre de paquets envoyés. Ainsi, lorsque vous appelez send() plusieurs fois sur un socket, la pile réseau est libre de diviser (ou non) les données entre plusieurs paquets. Sur un ordinateur fonctionnant sur un réseau réel, qui est beaucoup plus lent qu'une connexion en boucle, si vous avez encore un paquet en attente d'être envoyé au moment où vous appelez send(), les nouvelles données sont ajoutées au paquet en attente . côté réception, la pile réseau est libre de fusionner plusieurs paquets (les données reçues d'une socket sont mises en tampon) pour vous présenter les données en une seule fois.

donc, dans le serveur, vous écrivez:

for record in info: 
    self.channel.send(record) 
self.channel.send("#p") 

le dernier paquet peut contenir la fin du programme et le terminateur, Réunis. dans le client:

a = client.recv(1024) 
if a=="#p": 
    break 

la terminaison peut ne pas être au début du paquet reçu, et peut ne pas être les seuls caractères existants dans le paquet. si tel est le cas, vous ne détectez pas le terminateur, ne quittez pas la boucle, appelez de nouveau le recv(), et bloquez ici car recv() est un appel bloquant et le serveur n'enverra plus aucune donnée. Par conséquent, vous devez choisir une autre façon de savoir quand le serveur a fini d'envoyer les données. il y a beaucoup de possibilités:

  • la première façon évidente est sur le client, au lieu de if a=="#p":, écrire if a.endswith("#p"):. cela corrige le problème existant dans votre code
  • vous pouvez également choisir d'envoyer d'abord la longueur du programme avant de l'envoyer. le client lit la longueur, puis lit ce nombre de caractères et s'arrête
  • le serveur peut simplement fermer la connexion lorsque l'envoi du programme est terminé. Dans le client, vous détectez que la connexion est fermée lorsque recv() renvoie une chaîne vide, vous savez alors que le programme a été reçu. malheureusement, si le serveur tombe en panne avant d'envoyer le programme entier, vous exécuterez un programme incomplet sur le client

il existe d'innombrables autres possibilités pour corriger ce problème ...

2. votre logique de réception est mauvaise

maintenant, regardez de plus près la logique de réception (code modifié pour supprimer les lignes unrelevant):

1. a=client.recv(1024) 
2. b=a 
3. f=1 
4. while f: 
5.  a = client.recv(1024) 
6.  if a=="#p": 
7.   f=0 
8.   break 
9.  b+=a 

ici, vous devez d'abord attendre pour certaines données (ligne 1). alors vous entrez votre boucle while (ligne 4), et attendez encore quelques données (ligne 5)!

recv() est un appel bloquant: cela signifie qu'il ne retournera pas tant qu'il n'y a pas de données à retourner ou que la connexion est fermée. Si le serveur envoie moins de 1024 octets, y compris le terminateur, vous recevez tout sur la ligne 1 (y compris le terminateur), mais attendez toujours plus de données sur la ligne 5 ...

Notez qu'il y a encore un bogue dans votre test : si le séparateur est divisé entre 2 recv() appels (ce qui se passe si le programme est exactement 1023 octets de long), a.endswith('#p') n'évaluera jamais à True.

Voici donc une logique correcte de réception, qui teste le terminateur dès que certaines données sont reçues:

a = client.recv(1024)  
b = a 
f = 1 
while f: 
    if b.endswith("#p"): 
     f=0 
     break 
    a = client.recv(1024) 
    b += a 

Notez que cela peut être grandement simplifiée en supprimant les variables inutiles:

b = client.recv(1024) 
while not b.endswith("#p"): 
    b += client.recv(1024) 

3. dérouler correctement les ressources utilisées dans votre code

je n'ai pas insister sur ce point au premier , mais vous devriez toujours fermer correctement votre connexion!

le langage python est fait de sorte que la connexion soit implicitement fermée dès que la variable qui la contient n'est plus référencée (dans notre cas, quand elle sort du cadre). Cependant, la fermeture explicite indiquera à quel moment vous vous attendez à ce que la connexion se ferme. Certaines implémentations peuvent décider de reporter la publication de la ressource à une date ultérieure (les utilisateurs Jython peuvent avoir rencontré ce problème), laissant la connexion ouverte ... ce n'est pas un problème, mais peut entraîner des comportements étranges plus tard lorsque votre le programme se développe en un produit plus complet.

+0

Ah, les joies de l'utilisation des sockets de flux (TCP) pour l'envoi de datagrammes (ce qui serait UDP). – ndim

+1

@ndim: UDP est un NON-NO certain pour cette application: UDP ne garantit pas l'ordre d'arrivée des paquets ni si les paquets ont été livrés, et vous ne voulez pas recevoir un programme brouillé ou incomplet pour des raisons évidentes. plus UDP pose beaucoup d'autres problèmes comme la taille à utiliser pour fragmenter les paquets, comment s'assurer que l'envoi a réussi, etc., donc l'OP a raison d'utiliser TCP! –

+0

Merci beaucoup pour votre réponse.J'ai remplacé 'si a == "# p":' avec 'si a.endswith ("# p"):' puis a couru le serveur et le programme client ... mais la sortie est apparue seulement après avoir fermé le programme du serveur ... wat dois-je faire maintenant ?? – Amritha