Socket的雙網卡收發(C#)


最近的一個項目中需要同時使用兩塊網卡收發UDP組播數據包,並且要求使用Socket的方式接收和發送網絡數據包(我不會告訴你們我之前是直接使用SharpPcap來實現的)。在C#中Socket接觸的比較早,但是用的不多,特別是在實現本次上網卡的收發過程中也是遇到了不少麻煩。其中最最頭疼的就是不能同時接收兩張網卡的數據,雖然這個問題不是致命的(大不了用SharpPcap唄!!),但是最為一個21世紀有志青年,怎么能干出這種半吊子的事情呢!於是,這兩天我陷入了這個問題無法自拔,當然,最后還是解決了!哈~哈~哈哈哈~~

就在解決問題的那瞬間,眼前忽然出現了一個身影——“啊~~勤勞的(碼)農夫啊~~,請問你丟的是這把金斧頭呢,還是這…….”,“金斧頭!金斧頭!”(嘿嘿,把它賣了就能炒股票!賺大錢!出任CEO!迎娶白富美!走向人生巔峰!)

啊~~~呸!呸!呸!其實我想說,良心發現的我還是覺得如果有那么一群人還在糾結這個問題的,那么看看這里,也許能有幫助。(真的!絕對不是來裝X的)

問題概述

言歸正傳,在我的應用中,我主要是想利用C#的Socket來接收組播的UDP數據包。當然,發送也需要,但不是我最關心的問題。因此,我首先想到的就是UdpClient這個類。這個類對Socket進行了很好的封裝,用起來更加簡單。那么問題來了,我始終只能收到一個網卡上的數據,這是很頭疼的。我檢查了網絡,檢查了數據包發現都沒問題,但就是只能收到其中一個網卡的數據。

下面是我初始化Socket的代碼,該代碼為CapDevice類中的一段:

// 定義IPEndPoint
private IPEndPoint localEP = null;
private IPEndPoint remoteEP = null;

// 定義UDP發送和接收Socket
//private Socket udpReceive = null;
private UdpClient udpReceive = null;
private UdpClient udpSend = null;

// 本機節點
localEP = new IPEndPoint(device, LOCAL_PORT);

// 遠程節點
remoteEP = new IPEndPoint(IPAddress.Parse(MASTER_IP), DES_PORT);

// 實例化
udpReceive = new UdpClient(AddressFamily.InterNetwork);
udpReceive.Client.ReceiveBufferSize = 320000;
udpReceive.Client.Bind(localEP);
udpReceive.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

udpSend = new UdpClient();

// 發送和接收加入組播組
udpReceive.JoinMulticastGroup(IPAddress.Parse(MASTER_TX_GROUP)); 
udpSend.JoinMulticastGroup(IPAddress.Parse(MASTER_RX_GROUP));

// 打開發送標志位
isOpen = true;

// 打開接收線程
cap_thread = new Thread(new ThreadStart(ReceiveLoop));
cap_thread.Start();
cap_thread.Priority = ThreadPriority.Highest;
cap_thread.IsBackground = true;

在使用過程中,我分別實例化了兩個CapDevice對象,並且將所需IP和端口信息通過參數傳入。

device為本機網卡的IP地址;

LOCAL_PORT為本機偵聽端口;

MASTER_IP為待接收的遠程設備的IP地址;

DES_PORT為待接收的遠程設備的端口;

MASTER_TX_GROUP為接收數據綁定的組;

MASTER_RX_GROUP為發送數據綁定的組;

本人使用台式機開發,主板自帶一張千兆網卡,然后外接了一張PCI接口的千兆網卡。在我調試和尋找問題的過程中遇到這樣的情況:

1. 兩張網卡的IP分別為192.168.30.33和192.168.30.34,33的為主板上的網卡,34的是外接的網卡。按照上述方式進行配置后我發現我永遠只能收到33的網卡上的數據,除非我拔掉33的網卡上的網線,並且重新打開接收,此時34的網卡上才會有數據。

2. 我嘗試使用同步、異步的方法,並且嘗試使用Socket類而不是UdpClient類,但是都沒有成功。

因此,我懷疑,即使我的Socket偵聽的IP是192.168.30.34,實際Windows還是沒有對34這張網卡的數據進行偵聽。

問題解決

我花了很多時間去尋找答案,但是結果還是不太對。最終我找到一個講述跟我一樣問題的網站:

http://stackoverflow.com/questions/15265620/udp-read-data-from-all-network-interfaces

提出問題的人也遇到了同樣的問題,並且進行了很多嘗試。最終他得到的結論是:“把同步接收改成異步接收就行啦!”

我試了一下,發現問題不在那里,還是沒有。但是我發現一個地方,那就是加入組的時候他們使用的JoinMulticastGroup函數有兩個參數,然后我這么改了:

udpReceive.JoinMulticastGroup(IPAddress.Parse(MASTER_TX_GROUP), localEP.Address);

恩,其實看了這么多,我就想說這個,真不好意思,浪費大家這么多時間看廢話,嘻嘻!!

第二個參數是本地地址,真正能夠讓我的第二張網卡也能夠接收數據的也是這個參數,看來第二個參數才是通知系統將IP與本地網卡建立聯系。測試了一下,異步接收和同步接收都沒有問題。

總結

問題總結一句話,加入組播組需要使用帶本地IP地址的重載函數。當然,寫這么多就是想把問題描述清楚一點,同樣的問題按照這里所述就能得到解決。如果是其它原因導致Socket通信失敗,按照本文所述就不一定能解決咯。

下面是初始化代碼(區別就在高亮部分代碼):

// 定義IPEndPoint
private IPEndPoint localEP = null;
private IPEndPoint remoteEP = null;

// 定義UDP發送和接收Socket
//private Socket udpReceive = null;
private UdpClient udpReceive = null;
private UdpClient udpSend = null;

// 本機節點
localEP = new IPEndPoint(device, LOCAL_PORT);

// 遠程節點
remoteEP = new IPEndPoint(IPAddress.Parse(MASTER_IP), DES_PORT);

// 實例化
udpReceive = new UdpClient(AddressFamily.InterNetwork);
udpReceive.Client.ReceiveBufferSize = 320000;
udpReceive.Client.Bind(localEP);
udpReceive.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

udpSend = new UdpClient();

// 發送和接收加入組播組
udpReceive.JoinMulticastGroup(IPAddress.Parse(MASTER_TX_GROUP), localEP.Address); 
udpSend.JoinMulticastGroup(IPAddress.Parse(MASTER_RX_GROUP));

// 打開發送標志位
isOpen = true;

// 打開接收線程
cap_thread = new Thread(new ThreadStart(ReceiveLoop));
cap_thread.Start();
cap_thread.Priority = ThreadPriority.Highest;
cap_thread.IsBackground = true;

這里順便把我同步接收線程函數也貼出來吧:

private void ReceiveLoop()
{

    byte[] rcvData;

    while (isOpen)
    {
        rcvData = udpReceive.Receive(ref remoteEP);
        // 解析數據
         UploadEndecoder.CapturePkgM(rcvData);
    }

    udpReceive.Close();
    udpSend.Close();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM