2

ポート 8020 を使用して同じマシン上で両方を実行しているセットアップに、Python サーバーと TCL クライアントがあります。

私は基本的に、いくつかの操作を実行するためにPythonサーバーにいくつかの機能を実装しています。

そして、特に TCL クライアントがまだ利用できない Python の関数を呼び出そうとするときに、Python サーバーにいくつかの例外処理を追加しようとしています。Python サーバーは、機能がまだ利用できないことを TCL クライアントに警告する必要があります。

以下にコードをコピーして貼り付けました:-

Python サーバーコード

import socket
import sys
import os

DUT_Browser = ""
host = 'localhost'
port = 8020
print 'Current working Directory:',os.getcwd()


def LogError(message):
    try:
        logfile = open("log.txt", "a")
        try:
            logfile.write(message)
        finally:
            logfile.close()
    except IOError:
        pass

def GetParam(sParams, i):
    params = sParams.split(",")
    p = params[i].strip()
    p = p.strip("\"")

    return p

def Login(sParams):
    print "In method Login with parameters:", sParams

    return 0


def ExecuteKeyword(sCommand):
    vals = sCommand.partition("(")
    sKeyword = vals[0].strip()
    sParams = vals[2].strip()
    # Remove the trailing ")" from the parameters string
    sParams = sParams[:-1]
    #print "KEYWORD: ", sKeyword, "PARAMETERS: ", sParams
    func = getattr(sys.modules[__name__],sKeyword)
    failed = 1
    failed=func(sParams)
    if failed:
        return 1
    else:
        return 0

def clientthread(conn):
    while True:
        keyword = conn.recv(1024)
        errorMsg = 'none'
        failed = 1
        try:
           failed=ExecuteKeyword(keyword)
        except (Exception, FindFailed), err:
        #except Exception, err:
            errorMsg=str(err)
            LogError(errorMsg)
        if failed:
            reply = 'FAIL START' + errorMsg + 'FAIL END'
            print 'ERROR MSG:',reply
        else:
            reply = 'PASS: ' + keyword
        if not keyword:
           break
        print 'SERVER REPLY',reply
        conn.sendall(reply)
        print 'SERVER REPLY SENT'
    conn.close()

sdServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'TCP (SOCK_STREAM) Socket created'

try:
    sdServer.bind((host, port))
except socket.error , msg:
    print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
    sys.exit()

print 'Socket bind to port:',port,'on host:',host,'is complete'

#Start listening on socket
sdServer.listen(10)
print 'Socket now listening for 10 connections'
while 1:
    #wait to accept a connection - blocking call
    conn, addr = sdServer.accept()
    print 'Connected with ' + addr[0] + ':' + str(addr[1])
    #try:
    clientthread(conn)
    #except socket.error , err:
    #    print str(err)

sdServer.close()

TCL クライアント コード

set port 8020
set host localhost
set ntoIP "http://10.218.164.58/"
set sdClient [socket $host $port]
fconfigure $sdClient -buffering line
set ntoGuiCmd start
while {$ntoGuiCmd != "end"} {
    puts "Enter some command for $sdClient"
    set ntoGuiCmd [gets stdin]
    puts $sdClient $ntoGuiCmd
    if {[eof $sdClient]} {close $sdClient}
    gets $sdClient serverMessage
    puts "From Server : $serverMessage"
}

クライアントから次のコマンドを入力すると、期待どおりにパスして動作し、サーバーからのメッセージが出力されます。

Login("10.218.164.58",admin,admin,\"FALSE\")

ただし、まだ実装されていない次のコマンドを使用すると、クライアントは単にハングし、サーバーから例外を出力せず、まだ実装されていないことを示します。

Logout("10.218.164.58")

私はwiresharkを使用してみましたが、サーバーがメッセージを送信し、TCLクライアントも応答としてACKを送信したようです. したがって、「取得」がハングする理由がよくわかりません。

4

1 に答える 1

1

何が問題なのかよくわかりませんが、問題は Tcl コードにあると思われます。考えられる問題はいくつかあります (改行が返されるのでしょうか?) が、重要なことは、コードが実際には特に慣用的ではないということです。より慣用的なバージョンは次のとおりです。

set port 8020
set host localhost
set sdClient [socket $host $port]
fconfigure $sdClient -buffering line
while true {
    puts "Enter some command for $sdClient"
    gets stdin ntoGuiCmd
    if {[eof stdin] || $ntoGuiCmd eq "end"} {
        break
    }
    if {[catch {puts $sdClient $ntoGuiCmd}]} {
        break
    }
    ### Replacement discussed below starts here
    gets $sdClient serverMessage
    if {[eof $sdClient]} {
        break
    }
    puts "From Server: $serverMessage"
    # If multiline messages are possible, extra work is required here!
    ### Replacement ends here
}
close $sdClient

複数行の応答がある場合は、かなり注意が必要です。最善の方法は、応答の最後に到達したことがわかるまでそれらを読み取ることです (たとえば、サーバーがバイト数または行数を通知したため) が、常に可能であるとは限りません。応答が常に 1 つのパケットに収まると仮定する場合は、これを使用して利用可能な行を読み取ることができます (未読バイトがある間、ノンブロッキング モードでソケットから読み取ることによって機能します)。

fconfigure $sdClient -blocking 0
while {[gets $sdClient serverMessage] >= 0} {
    puts "From Server: $serverMessage"
}
fconfigure $sdClient -blocking 1
if {[eof $sdClient]} {
    break
}

応答が十分に大きい場合 (OS バッファなどに依存)、1 つのパケットに書き込まれません。メッセージング プロトコルで、応答の終了位置を示す明確なマーカーが本当に必要になるのは、このような場合です。(同じことが逆方向にも当てはまります。ただし、問題を引き起こす大きな要求がある場合を除きます。)


これがすべて私自身のコードである場合、完全に非同期のノンブロッキング クライアントを作成したくなるでしょう。

proc transfer {from to} {
    if {[gets $from line] >= 0} {
        puts $to $line
    } elseif {[eof $from]} {
        exit
    }
}

set sock [socket "localhost" 8020]
fconfigure stdin -blocking 0
fconfigure $sock -blocking 0
fileevent stdin readable [list transfer stdin $sock]
fileevent $sock readable [list transfer $sock stdout]
vwait forever

ただし、これは非常に異なるスタイルのクライアントです (また、フロント エンド API にラップするのは容易ではありません)。

于 2013-03-14T21:42:49.240 に答える