2011-01-14 9 views
2

サーバーがクライアントにプログラムを送信し、クライアントが受信したプログラムを実行するクライアントサーバープログラムを作成しました。この場合、OpenGLの線描画プログラムです。問題は、サーバおよびクライアントを実行すると、プログラム全体、すなわちサーバおよびクライアントへのプログラムの送信が時々行われるが、時には実行が行われないことである。クライアントが接続され、その接続が滞ってしまいます。プログラムは停止しません。私の友人システムでも同じことができますが、私の場合は時には(たいていの場合、それは習慣にならない)動作します。理由は何でしょうか?何か不足していますか? ネットワークプログラミングの問題

私のサーバーコード:

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

私のクライアントコード:

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

シングルスペースインデント...実際にこれを読み取ることはできますか?あなたの目を撫でることなく? – thkala

+2

あなただけがスレッドを実装する必要がある理由はありますか?ツイスト? – Kimvais

+0

@Kimvais:彼は組み込みの 'threading'モジュールを使用しています。これはTwistedよりはるかに軽量ですが、すべてのPythonディストリビューションに標準で付属しています。だから問題はどこにあるの? –

答えて

4

1. TCPソケットがあるストリーム(パケットで実装の詳細)

送信されるパケット数を制限するためにネットワークスタックが最適化されます。したがって、ソケット上でsend()を複数回呼び出すと、ネットワークスタックは自由に複数のパケット間でデータを分割(または分割しない)することができます。

send()の呼び出し時にまだパケットが送信されるのを待っている場合、ループバック接続よりもはるかに遅い実際のネットワーク上のコンピュータでは、新しいデータが待機パケットに追加されます。受信側では、ネットワークスタックは自由に複数のパケットをマージすることができます(ソケットから受信したデータはバッファリングされます)。

ので、サーバーでは、あなたが書いた:

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

最後のパケットがプログラムおよびターミネーターの終わりを含むことができ、一緒に参加しました。クライアントで :

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

ターミネータは、受信したパケットの先頭でないかもしれない、とパケット内の既存の文字だけではないかもしれません。この場合、ターミネータを検出せず、ループを終了せずにrecv()に再度コールしてください。recv()がブロッキングコールであり、サーバはこれ以上データを送信しないため、ここで停止します。

したがって、サーバーがデータの送信を完了したときに別の方法を選択する必要があります。

  • 最初の明らかな方法は、if a=="#p":の代わりに、if a.endswith("#p"):と書くことです。これにより、コードに存在する問題が修正されます。
  • 送信する前に、プログラムの長さを最初に送信することもできます。クライアントは長さを読み取り、その文字数を読み取って停止します。
  • サーバーは、プログラムの送信が完了したら接続を閉じることができます。クライアントでは、recv()が空文字列を返したときに接続が閉じられたことを検出すると、プログラムが受信されたことがわかります。残念ながら、プログラム全体を送信する前にサーバがクラッシュすると、クライアント上で不完全なプログラムが実行されます

この問題を解決するには他にも数多くの可能性があります。

2.受信ロジックは、

今間違っている受信ロジック(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 

ここに、あなたが最初に待ちますいくつかのデータ(行1)。 whileループ(4行目)に入り、もう一度データを待つ(5行目)!

recv()

は、ブロッキング呼び出しです:これはそこに戻るには、いくつかのデータがあるか、接続が閉じられるまで、それが戻らないことを意味します。サーバーがターミネータを含めて1024バイト未満を送信する場合は、1行目(ターミネータを含む)のすべてを受信しますが、5行目のデータをさらに待ちます。

テストにはまだバグがあります:セパレータは2つのrecv()コール(プログラムが正確に1023バイトの長さであればそれが起こる)との間で分割されている場合、a.endswith('#p')Trueに評価することはありません。これは非常に不必要な変数を除去することにより、簡略化することができる

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

ノート:

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

はすぐにいくつかのデータが受信されるターミネーターをテスト正しい受信ロジック、あります

3.適切に私が最初にこの点を強調していなかった

あなたのコード内で使用されるリソースを解放しますしかし、あなたは常にあなたの接続を適切に閉じなければなりません! (それがスコープの外に出たとき、あなたのケースで)接続が暗黙のうちに、すぐにそれを保持する変数がもう参照されないように閉じられますように、Python言語が作られて

。ただし、明示的に閉じると、接続が終了すると予想される時点が明確になります。

いくつかの実装では、リソースのリリースを後で延期することになり(Jythonユーザーがこの問題に遭遇した可能性があります)、接続を開いたままにしておきます...これは問題ありませんが、プログラムはより完全な製品に成長します。

+0

ああ、ストリーム・ソケット(TCP)を使用してデータグラム(UDPになる)を送信することの喜び。 – ndim

+1

@ndim:UDPは、このアプリケーションでは明確なNO-NOです。UDPは、パケットの到着順序やパケットが配信されたことを保証しておらず、明白な理由でスクランブルまたは不完全なプログラムを受信したくない場合。さらにUDPは、パケットを断片化するためにどのサイズを使用するか、どのように送信が成功するかなどを確認するなど、多くの問題を提起します。 –

+0

ありがとうございました。あなたはur response.Aのためにとてもありがとうございました。私はa == "#p": 'with'をa.endswith( "#p"): 'に置き換え、サーバーとクライアントプログラムを実行しました...私はサーバープログラムを閉じた後でなければなりません... watは私は今行うのですか? – Amritha

関連する問題