Skip to main content
 首页 » 编程设计

python之当我向 python 服务器发送 "invalid command"时,TCL 客户端挂起

2025年05月04日9lyhabc

我的设置中有一个 python 服务器和一个 TCL 客户端,它们都在使用端口 8020 的同一台机器上运行。

我基本上在 python 服务器中实现了一些函数来执行一些操作。

我正在尝试在 python 服务器中添加一些异常处理,尤其是当 TCL 客户端试图调用 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 作为响应。所以不太确定为什么“gets”应该挂起。

请您参考如下方法:

我不太确定哪里出了问题,但我怀疑问题出在 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 

如果您有多行响应,则需要格外小心。最好的方法是阅读它们,直到您知道已经到达响应的末尾(例如,因为服务器告诉您字节数或行数),但这并不总是可行的。如果您假设响应总是适合单个数据包,您可以使用它来读取可用的行(它通过以非阻塞模式从套接字读取来工作当有未读字节时):

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

如果响应足够大(取决于操作系统缓冲区等),则不会将其写入一个数据包。那是您真正需要在消息传递协议(protocol)中明确标记响应结束位置的时候。 (反之亦然,只是请求大了才会出问题。)


如果这都是我自己的代码,我很想做一个完全异步的非阻塞客户端:

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 中)。