как отправить изображение через TCP-сокет из малины pi (клиент) на сервер (ноутбук)? [Дубликат]

MySQLi намного безопаснее, чем MySQL, который в любом случае устарел. Вот почему вы должны придерживаться MySQLi, а также вы не можете смешивать их, поскольку они оба разные.

31
задан user2585107 16 July 2013 в 05:16
поделиться

6 ответов

TCP / IP - это протокол на основе потока , а не протокол на основе сообщений . Нет никакой гарантии, что каждый send() вызов одним одноранговым узлом приводит к единственному вызову recv() другим одноранговым узлом, получающему точные данные, - он может получать кусочек данных, разделенный на несколько вызовов recv(), из-за пакета фрагментация.

Чтобы дифференцировать границы сообщений, вам нужно определить свой собственный протокол на основе протокола TCP. Затем, чтобы прочитать сообщение, вы продолжаете называть recv() до тех пор, пока не прочитаете целое сообщение или не появится ошибка.

Один простой способ отправки сообщения - префикс каждого сообщения с его длиной. Затем, чтобы прочитать сообщение, сначала прочитайте длину, затем вы прочтете, что много байтов. Вот как вы можете это сделать:

def send_msg(sock, msg):
    # Prefix each message with a 4-byte length (network byte order)
    msg = struct.pack('>I', len(msg)) + msg
    sock.sendall(msg)

def recv_msg(sock):
    # Read message length and unpack it into an integer
    raw_msglen = recvall(sock, 4)
    if not raw_msglen:
        return None
    msglen = struct.unpack('>I', raw_msglen)[0]
    # Read the message data
    return recvall(sock, msglen)

def recvall(sock, n):
    # Helper function to recv n bytes or return None if EOF is hit
    data = b''
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data += packet
    return data

Затем вы можете использовать функции send_msg и recv_msg для отправки и получения целых сообщений, и у них не будет проблем с разбиением пакетов или объединены на сетевом уровне.

84
ответ дан Hedde van der Heide 21 August 2018 в 08:35
поделиться
  • 1
    Огромное спасибо. Я ценю это. – user2585107 16 July 2013 в 06:00
  • 2
    Я не уверен, понимаю ли я это полностью. Я понимаю, что должно произойти, но я, похоже, не понимаю. Я получаю Exception: Socket EOF trying to recv 4 bytes Я использую следующее: pastebin.com/raw.php?i=AvdN5RyW – user2585107 16 July 2013 в 06:22
  • 3
    @ user2585107: попробуйте обновленную версию, которая использует return None вместо повышения исключения при завершении потока. – Adam Rosenfield 16 July 2013 в 21:42
  • 4
    не должен ли packet быть .decode() до добавления его в data или recv() может принимать как байты, так и строки? – Siemkowski 21 September 2017 в 08:49
  • 5
    Работает очень хорошо. Благодаря!!! – José Luis 1 January 2018 в 22:27

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

См. этот ответ для другого описания проблемы.

Обратите внимание, что это означает, что вам нужен какой-то способ узнать, когда вы получили все данные , Если отправитель всегда будет отправлять ровно 8000 байт, вы можете подсчитать количество полученных байтов до сих пор и вычесть из 8000, чтобы узнать, сколько осталось получить; если данные имеют переменный размер, существуют различные другие методы, которые могут использоваться, например, когда отправитель отправляет заголовок с номерами байтов перед отправкой сообщения или если он отправляет текст ASCII, вы можете искать символ новой строки или NUL.

3
ответ дан Community 21 August 2018 в 08:35
поделиться

Принятый ответ хорош, но он будет очень медленным с большими файлами -string - это неизменный класс, это означает, что каждый объект будет создан каждый раз, когда вы используете знак +, используя list в качестве структуры стека будет больше эффективный.

Это должно работать лучше

while True: 
    chunck = s.recv(10000)
    if not chunck: 
        break
    fragments.append(chunck)

print "".join(fragments)
4
ответ дан Mina Gabriel 21 August 2018 в 08:35
поделиться

Вариант с использованием функции генератора (который я считаю более pythonic):

def recvall(sock, buffer_size=4096):
    buf = sock.recv(buffer_size)
    while buf:
        yield buf
        if len(buf) < buffer_size: break
        buf = sock.recv(buffer_size)
# ...
with socket.create_connection((host, port)) as sock:
    sock.sendall(command)
    response = b''.join(recvall(sock))
3
ответ дан Shadur 21 August 2018 в 08:35
поделиться
  • 1
    Это не работает, если ответ меньше размера буфера. – Shadur 6 November 2017 в 14:12
  • 2
    @Shadur, это интересно, что происходит, когда вы пытаетесь это сделать? можете ли вы поделиться кодом, чтобы воспроизвести проблему? Как написано, recvall должно давать содержимое каждого буфера, полученного независимо от размера, если он не пуст. – yoniLavi 6 November 2017 в 14:45
  • 3
    Судя по добавленным отладкам, он вдыхает весь ответ в первом куске, а затем зависает в ожидании следующего фрагмента. Следующий ответ «chunck» имеет ту же проблему, и я закончил его вторым тестированием, чтобы увидеть, была ли длина chunck меньше размера буфера. Я проверю, исправляет ли это ваше решение. - РЕДАКТИРОВАТЬ: Да. – Shadur 6 November 2017 в 14:47

Изменение кода Адама Розенфилда:

import sys


def send_msg(sock, msg):
    size_of_package = sys.getsizeof(msg)
    package = str(size_of_package)+":"+ msg #Create our package size,":",message
    sock.sendall(package)

def recv_msg(sock):
    try:
        header = sock.recv(2)#Magic, small number to begin with.
        while ":" not in header:
            header += sock.recv(2) #Keep looping, picking up two bytes each time

        size_of_package, separator, message_fragment = header.partition(":")
        message = sock.recv(int(size_of_package))
        full_message = message_fragment + message
        return full_message

    except OverflowError:
        return "OverflowError."
    except:
        print "Unexpected error:", sys.exc_info()[0]
        raise

Я бы, однако, очень хотел использовать оригинальный подход.

1
ответ дан sjMoquin 21 August 2018 в 08:35
поделиться

Вы можете использовать его как: data = recvall(sock)

def recvall(sock):
    BUFF_SIZE = 4096 # 4 KiB
    data = b''
    while True:
        part = sock.recv(BUFF_SIZE)
        data += part
        if len(part) < BUFF_SIZE:
            # either 0 or end of data
            break
    return data
15
ответ дан trilobyte 21 August 2018 в 08:35
поделиться
  • 1
    Это работает для обнаружения «Конец файла», но не для поддержания соединения и обнаружения конца сообщения. & quot; Конец файла & quot; будет достигнута только в том случае, если пизер закрывает свою часть гнезда или, по крайней мере, закрывает его половину. – glglgl 17 July 2013 в 14:46
  • 2
    Если полученная строка меньше 4096 символов, она снова зациклится и повторит проверку для получения дополнительных данных с помощью sock.recv(). Это будет зависать, поскольку больше не поступает данных. Если длина part меньше, чем длина RECV_BUFFER, тогда код может безопасно вырваться из цикла. – SomeGuyOnAComputer 4 December 2015 в 00:15
  • 3
    @SomeGuyOnAComputer, спасибо, исправлено. – JadedTuna 22 November 2016 в 21:21
  • 4
    @JadedTuna, похоже, не исправлена. Строка "part = sock.recv (BUFF_SIZE)" кажется, является блокирующим вызовом, поэтому выполнение зависает в этой строке после получения полного сообщения. – sh37211 7 June 2017 в 21:48
  • 5
    Этот код следует фиксировать так, как если бы len (part) & lt; BUFF_SIZE: # либо 0, либо конец разрыва данных – Hungry Mind 22 November 2017 в 13:44
Другие вопросы по тегам:

Похожие вопросы: