Python socket Recv 无法正常工作,有人可以解释一下吗

Posted

技术标签:

【中文标题】Python socket Recv 无法正常工作,有人可以解释一下吗【英文标题】:Python socket Recv not working properly could someone explain 【发布时间】:2011-10-19 20:36:52 【问题描述】:

我正在尝试为我的命令行服务器创建一个 gui 客户端。但是,我遇到了一些我似乎无法解决的恼人问题。

我不能 100% 确定实际的问题是什么,因为有时代码会起作用,有时则不会。我认为主要问题是最初我尝试了

while 1:
    self.data = s.recv(1024)
    if not self.data():
          break
    else:
          print self.data()

然后我用这个发送给它

  for f in files:
      s.send(f)

每个 f 都是一个文件名字符串。我希望它会出现在 recv 方面,因为每个 recv 调用都会收到一个文件名,但是在一次 recv 调用中,我得到了一大块文件名,我假设 1024 个字符价值

这使得无法检查数据的结尾,因此循环从未退出。

这是我现在的代码

def get_data(self,size = 1024):

    self.alldata = ""
    while 1:

        while gtk.events_pending():
             gtk.main_iteration()

        self.recvdata = self.s.recv(size)
        self.alldata += self.recvdata
        if self.alldata.find("\r\n\r\nEOF"):
           print "recieved end message"
           self.rdata = self.alldata[:self.alldata.find("\r\n\r\nEOF")]
           break



    print "All data Recieved: " + str(len(self.rdata)) + "Bytes"
    print "All data :\n" + self.rdata + "\n-------------------------------------------------"  

    self.infiles = self.rdata.split("-EOS-")
    for nf in self.infiles:
        if len(nf) > 2:
            self.add_message(self.incomingIcon,nf)

此刻我试图让客户端从服务器正确读取。我想要发生的是当输入命令列表并将其发送到客户端时,服务器会发回数据并将每个文件附加到列表存储中

有时这可以正常工作,有时仅返回 1200 个文件中的一个,如果它执行正常,如果我尝试键入另一个命令并发送它,整个 gtk 窗口就会消失并且程序变得无响应。

对不起,我无法更好地解释这个问题,我尝试了很多不同的解决方案,所有这些都给出了不同的错误。

如果有人可以解释 recv 命令以及为什么它可能会给出错误,这就是我向客户端发送数据的方式

if(commands[0] == 'list'):
    whatpacketshouldlooklike=""
    print "[Request] List files ", address
    fil = list_files(path)
    for f in fil:
         sdata =  f  
         whatpacketshouldlooklike += sdata + "-EOS-"
         newSock.send(sdata +"-EOS-")
         #print "sent: " + sdata
         newSock.send("\r\n\r\nEOF")
    whatpacketshouldlooklike += "\r\n\r\nEOF"
    print "---------------------------------"
    print whatpacketshouldlooklike
    print "---------------------------------"

【问题讨论】:

您的原始帖子中有错字吗? self.data = s.recv(1024) 会使下一行 self.data() 看起来是个坏主意。套接字上的 recv 方法不返回可调用对象,afaik。 stonemetal 的回答是正确的。如果你对一个套接字进行两次send() 调用,每个调用都有一个 20 字节的字符串(例如文件名),recv()ing 端可能会在单个 recv() 中获得 40 个字节,或者它可能会在一个上获得 39 个字节recv() 和下一个字节,或者可能必须调用 40 次,每次传递一个字节。您可以保证的是,它最终会非常努力地交付,并且永远不会交付任何乱序的东西。 【参考方案1】:

您在第一部分中遇到的问题是套接字是基于流而不是基于消息的。您需要提出一个消息抽象层来覆盖流。这样,管道的另一端就知道发生了什么(作为一个命令的一部分期望有多少数据),而不是猜测应该发生什么。

【讨论】:

嗨,我现在明白你的意思了,我认为尝试编写自己的协议会是一个有趣的测试。我会尝试按照 ms4py 的建议实现 struct 模块【参考方案2】:

使用抽象层(Pyro、XML-RPC、zeromq)或定义您自己的协议来区分消息。

例如,作为自己的协议,您可以在每个字符串之前将消息的长度作为“标头”发送。在这种情况下,您应该使用struct 模块将长度解析为二进制格式。再次询问,如果您想采用这种方式,但我强烈建议选择上述抽象层之一。

【讨论】:

【参考方案3】:

您的代码存在不同的问题。

让我们从一些人已经评论过的基础开始,send() 和 recv() 之间没有关系,你无法控制在 recv(call) 上返回的数据的哪一部分,你需要某种协议,在您的情况下,它可能就像用“\n”终止命令字符串一样简单,并在服务器上检查“\n”以使用数据。

现在其他问题:

您在使用 send 时未检查其返回大小,send() 并不能保证数据已完全写入,如果您需要,请使用 sendall()。 通过在阻塞套接字中使用 recv(1024)(默认),您的服务器代码可能会等待 1024 字节接收,这将不允许您在获得完整块之前处理消息,您需要使用非阻塞套接字和选择模块。

【讨论】:

【参考方案4】:

我的源代码:

def readReliably(s,n):
    buf = bytearray(n)
    view = memoryview(buf)
    sz = 0
    while sz < n:
        k = s.recv_into(view[sz:],n-sz)
        sz += k
    # print 'readReliably()',sz
    return sz,buf

def writeReliably(s,buf,n):
    sz = 0
    while sz < n:
        k = s.send(buf[sz:],n-sz)
        sz += k
    # obj = s.makefile(mode='w')
    # obj.flush()
    # print 'writeReliably()',sz
    return sz

这些函数的用法:

# Server
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
s.bind((host,port))
s.listen(10) # unaccepted connections
while True:
  sk,skfrom = s.accept()
  sz,buf = io.readReliably(sk,4)
  a = struct.unpack("4B",buf)
  print repr(a)
  # ...
  io.writeReliably(sk,struct.pack("4B",*[0x01,0x02,0x03,0x04]))

另请参阅有关recv_into(...)、https://docs.python.org/2/library/socket.html#socket.socket.recv_into 的官方文档

【讨论】:

以上是关于Python socket Recv 无法正常工作,有人可以解释一下吗的主要内容,如果未能解决你的问题,请参考以下文章

socket编程·send和recv

linux epoll socket recv 不能获取正确的数据

python3.6:socket.recv() 与 socket.recv_into() 性能对比

判断socket断开连接的方法

Python网络编程之socket之send和recv原理剖析

Python 学习笔记 - socket