首页 > 解决方案 > Python与TCL的socket连接

问题描述

我刚刚开始进行套接字编程。我希望将客户端作为 TCL,将服务器作为 Python。我尝试了以下几个脚本,但我无法取得任何进展。

这是我的脚本:

服务器.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"))

客户端.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"

但是,上面的 TCL 脚本只是挂起,并没有超出 gets 命令的范围。

这是等效的 Python 客户端脚本,它可以工作:

客户端.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")) 

现在基于一些建议,为了在 TCL 中模仿上述 python 客户端脚本,我尝试了以下操作:

client_modified.tcl

set s [socket [info hostname] 45000]
puts $s
fconfigure $s -translation binary
puts [encoding convertfrom utf-8 [read $s 28]]

我仍然无法继续:“socket localhost 45000”。

它给了我错误:

> tclsh client.tcl 
couldn't open socket: connection refused
    while executing
"socket [info hostname] 45000"
    invoked from within
"set s [socket [info hostname] 45000]"

任何人都可以在这里指导。

标签: pythontclpython-sockets

解决方案


鉴于:

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

是一个工作的Python 客户端,在 Tcl 中的等价物是:

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

但我更倾向于写:

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]

请注意,如果您正在通过套接字写入文本数据,您确实还应该编写换行符来充当消息边界。在 Tcl 方面,这将让您使用gets而不是read; 如果没有换行符,Tclgets在默认阻塞模式下会无限期地等待它出现。(对于面向二进制的协议,如果它是可变的,您可能会首先发送数据长度。)

更复杂的版本使用非阻塞模式。除非你真的确定你在做什么,否则非阻塞模式应该专门用于fileevent回调:

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

如果您将消息写回服务器,您可能需要进行设置-buffering none,这样您就不必为flush. 如果你正在做一个复杂的对话,你可能想要使用协程来管理它;他们具有复杂编程的功能(并且存在一些支持库可以提供帮助),这使得执行复杂协议变得非常简单。


推荐阅读