Python сокет Recv не работает должным образом, может кто-то объяснить - PullRequest
2 голосов
/ 20 октября 2011

Я пытаюсь создать клиент 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 ]

3 голосов
/ 20 октября 2011

Проблема, с которой вы столкнулись в первой части, заключается в том, что сокеты основаны на потоках, а не на сообщениях.Вам нужно придумать абстракцию сообщения для слоя поверх потока.Таким образом, другой конец канала знает, что происходит (сколько данных ожидать как часть одной команды) и не догадывается, что должно произойти.

2 голосов
/ 20 октября 2011

Существуют различные проблемы с вашим кодом.

Давайте начнем с того, что некоторые люди уже прокомментировали, что нет никакой связи между send () и recv (), вы не контролируете, какая часть данныхвозвращается по recv (вызову), вам нужен какой-то протокол, в вашем случае это может быть так же просто, как завершение строк команд с помощью «\ n» и проверка «\ n» на сервере для получения данных.

Теперь другие проблемы:

  • Вы используете send без проверки размера возвращаемого значения, send () не гарантирует, что данные полностью записаны, если вам это нужно, используйте sendall().
  • Используя recv (1024) в блокирующем сокете (по умолчанию), ваш серверный код может ожидать получения 1024 байтов, это не позволит вам обрабатывать сообщения, пока вы не получите полный блок,вам нужно использовать неблокирующий сокет и модуль выбора.
2 голосов
/ 20 октября 2011

Используйте уровень абстракции ( Pyro , XML-RPC , zeromq ) или определите свой собственный протокол для различения сообщений.

Например, в качестве собственного протокола вы можете отправить длину сообщения в виде «заголовка» перед каждой строкой. В этом случае вы должны использовать модуль struct для анализа длины в двоичном формате. Спросите еще раз, хотите ли вы пойти по этому пути, но я настоятельно рекомендую выбрать один из упомянутых слоев абстракции.

0 голосов
/ 01 апреля 2019

Мой исходный код:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...