2013-11-28 164 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),這些地址是不同的,對吧?那麼,這基本上解釋爲將它們綁定到相同的套接字上 - 特定於平臺的差異在上面鏈接的同一個答案中解釋,如果允許這樣做,行爲將與您的行爲相同使用相同的地址。在大多數平臺上,即使將(原始)套接字綁定到接口而不是地址,情況也是如此。

在此之上,經典BSD原始套接字甚至不能接收UDP(或TCP)的分組。除非你構建一個數據包過濾器來重新路由它們,否則它們總是由內核處理並傳送到UDP或TCP套接字(或者是防火牆,如果沒有的話)。Raw IP Networking FAQ根據最初定義的BSD原始套接字協議準確解釋您可以接受和不能接收的內容。幸運的是,大多數現代平臺都超出了原來的BSD套接字協議 - 不幸的是,它們都以不同的方式來完成。

您可以通過將套接字置於混合模式模式來解決這兩個問題,這意味着它將在路由前獲取其接口上的所有數據包。 (在每個平臺上,這樣做的方式也不同)。然後由您來決定哪些將被路由到您的UDP套接字並進行適當的處​​理。 (這對於UDP來說並不難;對於TCP來說,它變得更加複雜,因爲TCP數據包在被傳送到套接字之前被存儲並放回原位。)

更簡單的解決方案是使用您的平臺包過濾器接口。

或者,更容易使用類似libpcap的東西,它具有用於數據包捕獲的跨平臺包裝。

或者,更容易,使用類似scapy,其中有Python包裝圍繞libpcap和其他一切必要的,或wireshark,一個獨立的程序,你可以從Python的自動化。通過這種方式可以輕鬆捕獲將要傳送到套接字的數據包,並解析標頭以及您想要執行的其他任何事情。

或者,某些平臺提供了一種獲取UDP套接字上所有數據包標頭的方法。這要求使用recvmsg而不是recvfrom,因爲標題是作爲「輔助數據」傳送的,而不是作爲主接收緩衝區的一部分傳送。 Python 3.3有recvmsg;早期版本不會,你將不得不使用​​或一些第三方包裝(或建立自己的包裝)。

+0

謝謝,所以我明白使用setsockopt和SO_REUSEADDR/PORT是我的解決方案,但它不工作。提出了一個新的問題...希望這一次我會完成這個:) – Jjang

+0

@Jjang:正如我在答案中解釋,SO_REUSEADDR將無法在大多數平臺上工作,因爲(由於各種不同的原因)數據包將無論如何,最終都會被傳遞到錯誤的插槽。 – abarnert

相關問題