Как правильно пересылать TCP-трафик между сокетами? - PullRequest
4 голосов
/ 09 апреля 2010

Я пытаюсь написать некоторый код Python, который установит невидимое реле между двумя сокетами TCP. Моя текущая техника состоит в том, чтобы настроить два потока, каждый из которых считывает и затем записывает 1 КБ данных за раз в определенном направлении (то есть 1 поток для A - B, 1 поток для B - A).

Это работает для некоторых приложений и протоколов, но не является надежным - иногда отдельные приложения будут работать по-разному при работе через этот ретранслятор на основе Python. Некоторые даже терпят крах.

Я думаю, это потому, что когда я заканчиваю чтение в сокете A, выполняющаяся там программа считает, что его данные уже поступили в B, тогда как на самом деле я - коварный человек посередине - еще не отправил его B. В ситуации, когда B не готов принять данные (из-за чего send() блокируется некоторое время), мы сейчас находимся в состоянии, когда A считает, что он успешно отправил данные в B, но я все еще держу данные, ожидающие вызова send(). Я думаю, что это является причиной различий в поведении, которое я обнаружил в некоторых приложениях при использовании моего текущего кода ретрансляции. Я что-то пропустил, или это звучит правильно?

Если так, то мой реальный вопрос: есть ли способ обойти эту проблему? Можно ли читать из сокета A только тогда, когда мы знаем, что B готов к приему данных? Или есть другой метод, который я могу использовать для установления действительно «невидимого» двустороннего реле между [уже открытыми и установленными] сокетами TCP?

Ответы [ 3 ]

5 голосов
/ 09 апреля 2010

Можно ли читать только с розетка A, когда мы знаем, что B готов получать данные?

Конечно: используйте select.select на обоих сокетах A и B (если он возвращает сообщение о том, что только один из них готов, используйте его на другом), и только читайте из A и записывайте в B когда ты знаешь, что они оба готовы. E.g.:

import select

def fromAtoB(A, B):
    r, w = select.select([A], [B], [])
    if not r: select.select([A], [], [])
    elif not w: select.select([], [B], [])
    B.sendall(A.recv(4096))
1 голос
/ 09 апреля 2010

Возможно приложение, которое вы используете, плохо написано.

Например, если я позвоню recv(fd, buf, 4096, 0);, мне не обещают 4096 байт. Система делает все возможное, чтобы обеспечить ее.

Если 1k не кратен размеру вашего приложения recv или send, и приложение разбито, то группировка данных, отправленных в блоки по 1k, приведет к поломке приложения.

1 голос
/ 09 апреля 2010

Не думаю, что это может быть вашей проблемой.

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

Даже без вашего ретранслятора в середине, единственный способ для A "считать свои данные уже прибывшими в B" - это получить ответ от B, говорящий "да, я получил его".

...