我正在尝试使用python套接字试图更好地理解整个概念,但我遇到了一个问题。我有一个简单的服务器和一个客户端,客户端将一个列表发送到服务器,然后等待服务器发送一个字符串,表明该过程已完成。
这是客户端文件:
import socket
import json
host = '192.168.1.102'
port = 14314
def request():
print 'Connecting'
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.connect((host, port))
print 'Sending request'
clientsocket.sendall(json.dumps([1, 2, 3, 4, 5, 6, 7, 8, 9]))
print 'Receiving data'
data = clientsocket.recv(512)
print 'Received: {}'.format(data)
request()
这是服务器文件:
import socket
import json
host = '192.168.1.102'
port = 14314
def run():
print 'Binding socket'
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind((host, port))
print 'Waiting for client'
serversocket.listen(1)
clientsocket, addr = serversocket.accept()
print 'Receiving data'
raw_data = ''
while True:
tmp = clientsocket.recv(1024)
if not tmp:
break
raw_data += tmp
data = json.loads(raw_data)
print 'Received: {}'.format(data)
print 'Sending data'
clientsocket.sendall('done')
run()
问题在于,当客户端完成发送列表时,服务器卡在recv循环中,等待什么都没有。整个数据已经在第一次迭代中被接收,在第二次迭代中没有任何东西被接收,因为客户端已经移动到接收部分。
奇怪的是,如果我从客户端注释掉接收部分并从服务器注释发送部分,则该过程成功完成。那么,我做错了什么?为什么这不起作用?
感谢。
答案 0 :(得分:1)
socket.recv的文档讨论了能够传递到unix文档中描述的recv函数的其他标志。所以转向documentation,我发现了以下信息:
如果套接字上没有可用消息,则接收呼叫等待 要到达的消息,除非套接字是非阻塞的(参见fcntl(2)), 在这种情况下返回值-1
所以再一次,我们被引导到另一页。 fcntl的文档说
在打开的文件上执行下面描述的操作之一 描述符
因此,通常socket.recv函数是阻塞的(它将无限期地等待新数据),除非我们使用文件描述符。我们怎么做?好吧有一个socket.makefile函数,它给我们一个附加到套接字的文件描述符。凉。这个SO question给出了一个示例,说明如何使用文件描述符读取和写入套接字。
如果我们不想使用文件描述符,该怎么办?进一步阅读recv函数的unix文档,我看到我可以使用MSG_DONTWAIT标志。这在Windows中不起作用,但我确实发现我们可以使用socket.setbocking(False)永久地将套接字更改为非阻塞模式。然后,您需要忽略任何“无法立即完成非阻塞套接字操作”错误。这些是正常的和非致命的(this page的错误#10035提到它是非致命的)。
另一种可能的实现是多线程程序,您可以为套接字实现接收和发送线程。这可能会给您带来最佳性能,但设置起来需要做很多工作。
Python太棒了。我刚刚发现一些Python也有异步套接字的库。 asyncore,asynchat如果在您使用的Python版本中可用,则已弃用asyncio。
很抱歉在那里扔了那么多。我不太了解套接字。我在Paramiko库中使用过一次,就是这样。但看起来有很多方法可以实现它们。