2013-11-28 4 views
0

Я долго искал Google и читал связанные вопросы здесь о отправке и получении через сокеты, но не смог найти ответ на мой вопрос:Можно ли отправлять и получать пакеты через разные сокеты?

Могу ли я отправлять и принимать пакеты через разные сокеты?

Я хотел бы реализовать что-то вроде этого:

HOST='127.0.0.1' 
PORT=10000 
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) 
sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

sendSock.sendto("",(HOST,PORT)) 
response,addr = recvSock.recvfrom(65535) 

В принципе, я хотел бы послать сообщение эхо-сервер через sendSock который является сокет UDP, и получить ответ от сервера через recvSock который является сырым сокетом.

Возможно ли это? Если да, то как? код не работает.

+0

Конечно, но вам необходимо декодировать/кодировать протокол UDP – Torxed

+0

@Torxed: «Конечно»? Нет никакого способа, чтобы 'recvSock' получал данные, отправленные как ответ на его' sendSock.sendto'. Он должен делать что-то совершенно другое. – abarnert

+1

Между тем, по вашему последнему вопросу, я сказал, что вам нужно будет предоставить свою платформу и версию, если вы хотите, чтобы кто-то помог с чем-то вроде этого. Это все еще так. Любой ответ на любой такой вопрос, который не является специфичным для платформы, будет иметь ограниченную полезность; все могут сказать, что «некоторые платформы могут делать X, но они делают это по-разному, а другие не могут делать X, но могут делать Y, которые могут быть или не быть то, что вам нужно, а другие не могут делать ничего подобного , и большинство из них изменилось с годами ", что не поможет вам написать код, который вы хотите написать. – abarnert

ответ

2

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

Когда вы создали сокет UDP и отправили ему сообщение, это сделало две вещи: во-первых, он выбрал подходящий адрес источника с произвольным портом из эфемерного диапазона и привязал ваш сокет к этому адресу. (UDP-адрес - это IP-адрес хоста и порт.) Затем он отправил сообщение с этого исходного адреса на адрес назначения.

Когда удаленная сторона ответит, она отправит сообщение на этот адрес источника. Ядро получает это сообщение и видит, что оно нацелено на адрес, к которому привязан ваш UDP-сокет. Таким образом, он доставляет сообщение этому сокету.

Это значит, что recvfrom будет работать только на этом сокете, а не на другом. Просто вызов recvfrom на другом сокете, который не связан ни с чем, ничего не принесет.

Что делать, если вы можете связать два сокета по одному и тому же адресу? Ну, вы можете, используя опции сокета SO_REUSEADDR и/или SO_REUSEPORT. См. this question для некоторых (особенно для конкретной платформы) деталей, когда это разрешено с каждой опцией. Но, даже когда это разрешено, что происходит, когда сообщение получено для адреса, который связан с ним двумя сокетами? Ну, это также очень специфично для платформы. Теоретически сообщение доставляется в один из сокетов произвольно. На практике на некоторых платформах ядро ​​будет помнить, кто недавно отправил исходный адрес ответа, или на любой адрес в той же сети, что и исходный адрес, или кто связался совсем недавно или какое-то другое правило, и доставить это один. Таким образом, в зависимости от вашей платформы, которая может или не поможет вам, вы можете иметь bind один сокет, использовать его в sendto, затем bind второй сокет, затем recvfrom на втором и получить ответ. Или это не так. И если он не делает то, что вы хотите, не пытайтесь сражаться с вашим ядром; есть лучшие способы пойти, если вы хотите попасть под обложки.

Что делать, если вы связываетесь с более инклюзивным адресом? Например, вы можете привязать первый сокет к ('127.0.0.1', 12345), а затем привязать второй к ('0.0.0.0', 12345), и это разные адреса, не так ли? Ну, это в основном интерпретируется аналогично привязке их обоих к тем же сокетам - различия в платформе объясняются в том же ответе, который я связал выше, и поведение, если вам разрешено это сделать, будет таким же, как если бы вы использовал тот же адрес. И на большинстве платформ это верно, даже если вы связываете (raw) сокет с интерфейсом, а не с адресом.

Кроме того, классические сокеты BSD не могут даже получать пакеты UDP (или TCP). Если вы не создадите фильтр пакетов, чтобы перенаправить их, они всегда обрабатываются ядром и передаются в UDP или TCP-сокет (или в брандмауэр, если их нет).Raw IP Networking FAQ объясняет, что именно вы можете и не можете получить в соответствии с протоколом raw socket BSD, как это было определено первоначально. К счастью, большинство современных платформ выходит за рамки оригинального протокола сокетов BSD - к сожалению, все они делают это по-разному.

Вы можете решить обе эти проблемы, поставив розетку в режим promiscuous, что означает, что он получит все пакеты на своем интерфейсе, прежде чем они будут перенаправлены. (Способ сделать это также отличается на каждой платформе.) Вам решать, какие из них будут перенаправлены на ваш UDP-сокет и обработать их соответствующим образом. (Это не так сложно с UDP, а с TCP становится все сложнее из-за того, что пакеты TCP сохраняются и возвращаются в порядок до доставки в сокет.)

Более простым решением является использование платформы интерфейс пакетного фильтра.

Или, еще проще, используйте что-то вроде libpcap, у которого есть кросс-платформенные обертки для захвата пакетов.

Или, еще проще, использовать что-то вроде scapy, который имеет обертками Python вокруг libpcap и все остальное необходимое, или wireshark, отдельную программу, которую вы можете автоматизировать с Python. Таким образом, тривиально захватывать пакеты, которые будут доставлены в ваш сокет, и проанализировать заголовки и все остальное, что вы хотите сделать.

Или некоторые платформы обеспечивают способ получения заголовков на всех пакетах на сокетах UDP. Для этого требуется использовать recvmsg вместо recvfrom, поскольку заголовки поставляются как «вспомогательные данные», а не как часть основного буфера приема. Python 3.3 имеет recvmsg; более ранних версий нет, и вам придется использовать ctypes или стороннюю оболочку (или создать собственную оболочку).

+0

спасибо, поэтому я понимаю, что с помощью setsockopt и SO_REUSEADDR/PORT - мое решение, но оно не работает. Сделал новый вопрос относительно этого ... надеюсь, на этот раз я закончу с этим :) – Jjang

+0

@Jjang: Как я объяснил в ответе, SO_REUSEADDR не будет работать на большинстве платформ, потому что (по разным причинам) пакеты будут в конечном итоге доставляется в неправильное гнездо. – abarnert

Смежные вопросы