【问题标题】:Python socket Recv not working properly could someone explainPython socket Recv 无法正常工作,有人可以解释一下吗
【发布时间】: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 次,每次传递一个字节。您可以保证的是,它最终会非常努力地交付,并且永远不会交付任何乱序的东西。

标签: python sockets


【解决方案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 的官方文档

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-16
        • 2011-11-22
        • 2016-12-12
        相关资源
        最近更新 更多