Python socket的Recv不正常,能否解释一下

5 投票
4 回答
7982 浏览
提问于 2025-04-17 04:39

我正在尝试为我的命令行服务器创建一个图形用户界面(GUI)客户端。不过,我遇到了一些烦人的问题,怎么也解决不了。

我不太确定实际的问题是什么,因为有时候代码能正常工作,有时候又不行。我觉得主要问题是我最开始尝试了这个

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 调用中,我收到了一个大块的文件名,我猜大约有 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 "---------------------------------"

4 个回答

2

使用一个抽象层,比如PyroXML-RPC或者zeromq,或者自己定义一个协议来区分消息。

举个例子,如果你自己定义协议,可以在每条消息前面先发送一个“头”,告诉接收方这条消息的长度。这样的话,你需要用到struct模块,把长度转换成二进制格式。如果你想走这条路,可以再问我,但我强烈建议你选择上面提到的某个抽象层。

2

你的代码有几个问题。

首先,有些人已经提到过一个基本的概念,那就是发送(sends())和接收(recv())之间没有直接的关系。你不能控制在调用recv()时返回哪些数据。你需要某种协议来管理数据。在你的情况下,可以简单地用"\n"来结束命令字符串,然后在服务器上检查"\n"来处理数据。

接下来还有其他问题:

  • 你在使用send时没有检查返回的大小。send()并不保证数据会完全写入,如果你需要确保数据全部发送,请使用sendall()。
  • 如果你在一个阻塞的套接字中使用recv(1024)(这是默认设置),你的服务器代码可能会一直等到接收到1024字节的数据,这样会导致你无法处理消息,直到收到完整的数据块。你需要使用非阻塞的套接字,并且使用select模块。
3

你在第一部分遇到的问题是,套接字(sockets)是基于流的,而不是基于消息的。你需要设计一种消息的抽象层,放在流的上面。这样,另一端就能知道发生了什么(比如,作为一个命令应该接收多少数据),而不是在猜测应该发生什么。

撰写回答