Python套接字Recv无法正常工作,有人可以解释

时间:2011-10-19 20:36:53

标签: python sockets

我正在尝试为我的命令行服务器创建一个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 "---------------------------------"

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

使用抽象层(PyroXML-RPCzeromq)或定义自己的协议来区分消息。

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

答案 2 :(得分:2)

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

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

现在还有其他问题:

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

答案 3 :(得分:0)

我的源代码:

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的官方文档