Reputation: 23
I have a python server and a TCL client in my setup both running on the same machine using port 8020.
I basically have some functions implemented in python server to perform some operations.
And I am trying to add some exception handling in the python server especially when a TCL client tries to call some function in python which is not yet available. The python server should alert the TCL client that the functionality is not available as yet.
I have copy pasted my code below:-
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()
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"
}
When I enter following command from the client it passes and behaves as expected and the message from server is printed.
Login("10.218.164.58",admin,admin,\"FALSE\")
However when I use the following command which is not yet implemented the client simply hangs and does not print the exception from the server indicating that its not yet implemented.
Logout("10.218.164.58")
I even tried using wireshark and noticed that the server did send out the message and also the TCL client seems to have sent out an ACK in response. So not really sure why "gets" should hang.
Upvotes: 2
Views: 813
Reputation: 137567
I'm not quite sure what's going wrong, but I suspect that the problem is in the Tcl code. There are a number of issues possible (is there a newline being passed back?) but the key thing is that the code is actually not especially idiomatic. Here's a more idiomatic version:
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
If you've got multiline responses, you need to take considerably more care. The best way is to read them until you know you've reached the end of the response (e.g., because the server told you the number of bytes or the number of lines) but that's not always possible. If you assume that responses always fit in a single packet, you can use this to read the lines that are available (it works by reading from the socket in non-blocking mode while there are unread bytes):
fconfigure $sdClient -blocking 0
while {[gets $sdClient serverMessage] >= 0} {
puts "From Server: $serverMessage"
}
fconfigure $sdClient -blocking 1
if {[eof $sdClient]} {
break
}
If a response is large enough (which depends on OS buffers, etc) then it will not get written in one packet. That's when you really need a clear marker in your messaging protocol of where the end of the response is. (The same is also true in the reverse direction, except there it's large requests that cause problems.)
If this was all my own code, I'd be tempted to do a fully asynchronous nonblocking client:
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
However, this is a very different style of client (and one that isn't easy to wrap into a front end API).
Upvotes: 1