Reputation: 1
I've just started on socket programming. I want to have the client as TCL and server as Python. I tried the following few scripts, but I'm unable to make any progress.
Here are my scripts:
server.py:
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), 45000))
s.listen(5)
while True:
clt, adr=s.accept()
print(adr)
clt.send(bytes("Socket programming in Python"))
client.tcl:
set host "127.0.0.1"
set port 45000
set mysock [socket -myaddr $host -myport $port $host $port]
fconfigure $mysock -buffering none
gets $mysock line
close $mysock
puts "Message is $line"
However, the above TCL script just hangs and doesn't go beyond the gets command.
This is the equivalent Python client script which works:
client.py:
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(s)
s.connect((socket.gethostname(), 45000))
msg=s.recv(1024)
print(msg.decode("utf-8"))
Now based on few suggestions, to mimic the above python client script in TCL, I tried the following:
client_modified.tcl:
set s [socket [info hostname] 45000]
puts $s
fconfigure $s -translation binary
puts [encoding convertfrom utf-8 [read $s 28]]
I'm still not able to proceed beyond: "socket localhost 45000".
It gives me the error:
> tclsh client.tcl
couldn't open socket: connection refused
while executing
"socket [info hostname] 45000"
invoked from within
"set s [socket [info hostname] 45000]"
Can anyone please guide here.
Upvotes: 0
Views: 1728
Reputation: 1
It works with the following small edit(-async):
set s [socket -async [info hostname] 45000]
puts $s
fconfigure $s -translation binary
puts [encoding convertfrom utf-8 [read $s 28]]
Thanks for helping.
Upvotes: 0
Reputation: 137567
Given that:
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(s)
s.connect((socket.gethostname(), 45000))
msg=s.recv(1024)
print(msg.decode("utf-8"))
is a working Python client, the equivalent in Tcl is:
set s [socket [info hostname] 45000]
puts $s
fconfigure $s -translation binary
puts [encoding convertfrom utf-8 [read $s 28]]; # You're writing 28 bytes, not 1024
But I'd be more inclined to write:
set s [socket localhost 45000]; # For complex reasons, [info hostname] takes a long time on macOS
fconfigure $s -encoding utf-8
puts [read $s 28]
Note that if you're writing text data over a socket, you really should also write newlines to act as a message boundary. On the Tcl side, this would let you use gets
instead of read
; without the newline, Tcl's gets
will merrily wait indefinitely for it to turn up when in the default blocking mode. (For binary-oriented protocols, you'd probably be sending the length of data first if that's variable.)
A more sophisticated version uses non-blocking mode. Unless you're really sure what you're doing, non-blocking mode should be used exclusively with fileevent
callbacks:
set s [socket localhost 45000]
fconfigure $s -encoding utf-8 -blocking 0
fileevent $s readable {apply {{} {
global s done
puts [read $s 1024]
# Remember to close that socket at EOF
if {[eof $s]} {
close $s
set done "ok"; # Terminates the event loop running this
}
}}}
vwait done; # Launch an event loop for processing the socket
If you write messages back to the server, you probably want to set -buffering none
so that you don't have to fuss around with flush
. And if you're doing a complex conversation, you'll probably want to use a coroutine to manage it; they've got features for complex programming (and some support libraries exist to help) that make doing complex protocols enormously simpler.
Upvotes: 1
Reputation: 5723
On the Tcl side, to open a client socket, do not specify -myport
. This option specifies a particular port for the client connection to use. Since your code sets this to port 45000, which is already in use by the server, it fails.
set host "127.0.0.1"
set port 45000
try {
set mysock [socket $host $port]
} on error { err res } {
puts $res
}
try {
chan configure $mysock -buffering line
chan configure $mysock -encoding utf-8
} on error { err res } {
puts $res
}
try {
gets $mysock line
} on error { err res } {
puts $res
}
puts "Message is $line"
close $mysock
I changed the python code to include a newline so that it would flush the message. Not knowing any python or how the python socket code works, I got lucky there.
import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), 45000))
s.listen(5)
while True:
clt, adr=s.accept()
print(adr)
clt.send(bytes("Socket programming in Python\n"))
Upvotes: 0