• 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();
    }
  • 相关阅读:
    关于抑或
    【vue】条件渲染 v-if v-else
    【vue】vue的目录结构、项目流程、vue-router
    【vue】在vue中引入iview
    【vue】vue如何创建一个项目
    【jquery】jquery怎么实现点击一个按钮控制一个div的显示和隐藏
    【angularjs】ng-model controller中取不到值(input)
    打印机增强软件pdfpro
    vagrant 安装ubuntu12.04 64 bit
    debian 7 stable 不能编译android源码
  • 原文地址:https://www.cnblogs.com/sleepwalker/p/4483277.html
Copyright © 2020-2023  润新知