C# UDP 基本使用

UDP基本介绍:

UDP 是一种无连接协议,客户端和服务器之间不需要建立连接即可发送数据。它的核心功能是快速传输数据包。

更多特性,参考

arduino 复制代码
https://www.runoob.com/np/udp-protocol.html

一、UDP 点对点通讯:

虽然UDP是无连接的,但是也可以手动绑定本地IP。(在UdpClient构造里,或使用)

绑定后,udp将以此本地IP及端口通讯。

例如:

ini 复制代码
            // 绑定本地端口
            UdpClient udpClientA = new UdpClient(321);

            //绑定本地IP端点
            IPAddress ip_127_0_0_5 = IPAddress.Parse("127.0.0.5");
            IPEndPoint iPEndPoint_B = new IPEndPoint(ip_127_0_0_5, 321);
            UdpClient udpClientB = new UdpClient(iPEndPoint_B);

            //不绑定端口
            UdpClient udpClientC = new UdpClient();

A指定了端口321,B指定了IP及端口 127.0.0.5:321,C不指定;

然后通过UdpClient.Send方法进行消息发送,代码如下:

ini 复制代码
  Byte[] sendBytes_A_P2P = Encoding.ASCII.GetBytes("A say Hi?");
  Byte[] sendBytes_B_P2P = Encoding.ASCII.GetBytes("B say Hi?");
  Byte[] sendBytes_C_P2P = Encoding.ASCII.GetBytes("C say Hi?");

  IPEndPoint remoteIPEndP_IP3 = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 555);
  IPEndPoint remoteIPEndIP_IP1 = new IPEndPoint(
      IPAddress.Parse("192.168.153.1"),
      666
  );
  #region P2P发送
  Console.WriteLine("点对点收发");
  try
  {
      udpClientA.Send(sendBytes_A_P2P);
  }
  catch (Exception e)
  {
      Console.WriteLine(e.Message);
      Console.WriteLine();
  }

  udpClientA.Send(sendBytes_A_P2P, remoteIPEndP_IP3);
  Console.WriteLine();
  Console.WriteLine("A say Hi to 127.0.0.1:555");
  udpClientA.Send(sendBytes_A_P2P, remoteIPEndIP_IP1);
  Console.WriteLine();
  Console.WriteLine("A say Hi to 192.168.153.1:666");

  udpClientB.Send(sendBytes_B_P2P, remoteIPEndP_IP3);
  Console.WriteLine();
  Console.WriteLine("B say Hi to 127.0.0.1:555");
  try
  {
      udpClientB.Send(sendBytes_B_P2P, remoteIPEndIP_IP1);
      Console.WriteLine();
      Console.WriteLine("B say Hi to 192.168.153.1:666");
  }
  catch (Exception e)
  {
      Console.WriteLine();
      Console.WriteLine($"{e.Message},IPEndPoint:{remoteIPEndIP_IP1}");
  }

  udpClientC.Send(sendBytes_C_P2P, remoteIPEndP_IP3);
  Console.WriteLine();
  Console.WriteLine("C say Hi to 127.0.0.1:555");
  udpClientC.Send(sendBytes_C_P2P, remoteIPEndIP_IP1);
  Console.WriteLine();
  Console.WriteLine("C say Hi to 192.168.153.1:666");


  #endregion

其中UdpClient.Send方法有多个重载,

当使用了UdpClient.Connect 方法

建立远程连接后,可以不带IPEndPoint参数,

ini 复制代码
udpClientA.Send(sendBytes_A_P2P);

但要是没有使用Connect就调用此函数就会出现异常:

The operation is not allowed on non-connected sockets.

注意:

1、使用UdpClient.Connect 方法

建立连接后,就只能接受此远程IP节点的信息。而且还不可以在Send时候再指定远程节点。

2、在构造函数里指定绑定本地IP后,就只能在此本地IP网段通讯,UDPClinet不会再根据远程节点切换IP。

如上述中的B,绑定本地127.0.0.5,此时向其他网段通讯。

arduino 复制代码
                try
                {
                    udpClientB.Send(sendBytes_B_P2P, remoteIPEndIP_IP1);
                    Console.WriteLine();
                    Console.WriteLine("B say Hi to 192.168.153.1:666");
                }
                catch (Exception e)
                {
                    Console.WriteLine();
                    Console.WriteLine($"{e.Message},IPEndPoint:{remoteIPEndIP_IP1}");
                }

出现异常,向一个无法连接的网络尝试了一个套接字操作。,IPEndPoint:192.168.153.1:666。

通讯结果如下:

A因为绑定了端口,所以发送的时候会使用固定端口321 。 B绑定了端口及IP,无法与其他网段通讯。C由于没有手动指定,会由基础服务提供合适的IP及端口。

二、UDP广播:

将主机号,设置成255,就是此网段的广播地址。全网段广播地址为255.255.255.255.

如下:

ini 复制代码
    //此网段所有IP
    IPEndPoint allIPEndPoint_IP1 = new IPEndPoint(
        IPAddress.Parse("192.168.153.255"),
        666
    );

    //默认网卡IP
    IPEndPoint allIPEndPoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 666);

    Byte[] sendBytes_A_all_IP1 = Encoding.ASCII.GetBytes(
        "A say Hi to everyone in 192.168.153 ?"
    );
    Byte[] sendBytes_C_all_IP1 = Encoding.ASCII.GetBytes(
        "C say Hi to everyone in 192.168.153 ?"
    );

    Byte[] sendBytes_A_all = Encoding.ASCII.GetBytes("A say Hi to everyone?");
    Byte[] sendBytes_C_all = Encoding.ASCII.GetBytes("C say Hi to everyone?");
    #region 广播

    Console.WriteLine("广播");

    udpClientA.Send(sendBytes_A_all_IP1, allIPEndPoint_IP1);
    Console.WriteLine();
    Console.WriteLine("A say Hi to 192.168.153:666");
    udpClientC.Send(sendBytes_C_all_IP1, allIPEndPoint_IP1);
    Console.WriteLine();
    Console.WriteLine("C say Hi to 192.168.153:666");
    //udpClientA.EnableBroadcast = true;
    //udpClientC.EnableBroadcast = true;
    var a = udpClientA.Send(sendBytes_A_all, allIPEndPoint);
    Console.WriteLine();
    Console.WriteLine("A say Hi to everyone:666");
    var b = udpClientC.Send(sendBytes_C_all, allIPEndPoint);
    Console.WriteLine();
    Console.WriteLine("C say Hi to everyone:666");

    #endregion

有些文章说需要设置

ini 复制代码
udpClientA.EnableBroadcast = true;

但是我尝试过,没什么效果。

三、组播:

组播拥有特殊的地址:

arduino 复制代码
  //组播地址
  //IP 组播通信必须依赖于 IP 多播地址,在 IPv4 中它是一个 D 类 IP 地址,范围从 224.0.0.0 到 239.255.255.255,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址3类:

  //局部链接多播地址范围在 224.0.0.0~224.0.0.255,这是为路由协议和其它用途保留的地址,路由器并不转发属于此范围的IP包;

  //预留多播地址为 224.0.1.0~238.255.255.255,可用于全球范围(如Internet)或网络协议;

  //管理权限多播地址为 239.0.0.0~239.255.255.255,可供组织内部使用,类似于私有 IP 地址,不能用于 Internet,可限制多播范围。

测试代码:

ini 复制代码
    Byte[] sendBytes_multi = Encoding.ASCII.GetBytes("Gruop 224.0.1.0 say Hi");

    IPAddress multiCastIP = IPAddress.Parse("224.0.1.0");

    #region 组播

    Console.WriteLine("组播");

    UdpClient multiRev_D = new UdpClient();

    multiRev_D.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

    multiRev_D.Client.Bind(new IPEndPoint(IPAddress.Any,777));

    multiRev_D.JoinMulticastGroup(multiCastIP);


    UdpClient multiRev_E = new UdpClient();


    multiRev_E.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

    multiRev_E.Client.Bind(new IPEndPoint(IPAddress.Any, 777));

    multiRev_E.JoinMulticastGroup(multiCastIP);

    //接受组播信息

    Task.Run(() =>
    {
        IPEndPoint multiRev_D_IPEndPoint = null;
        var bytes = multiRev_D.Receive(ref multiRev_D_IPEndPoint);
        string msg = Encoding.UTF8.GetString(bytes);
        Console.WriteLine($"multiRev_D receive: {msg} receive IPEndPoint:{multiRev_D_IPEndPoint}");
        Console.WriteLine();
        multiRev_D.DropMulticastGroup(multiCastIP);
    }); 

    Task.Run(() =>
    {
        IPEndPoint multiRev_E_IPEndPoint = null;
        var bytes = multiRev_E.Receive(ref multiRev_E_IPEndPoint);
        string msg = Encoding.UTF8.GetString(bytes);
        Console.WriteLine($"multiRev_E receive: {msg} receive IPEndPoint:{multiRev_E_IPEndPoint}");
        Console.WriteLine();
        multiRev_E.DropMulticastGroup(multiCastIP);
    });

    //发送组播信息

    IPEndPoint multiCastIPEndPoint = new IPEndPoint(multiCastIP, 777);

    UdpClient sendMultiUdp = new UdpClient();

    sendMultiUdp.Connect(multiCastIPEndPoint);

    sendMultiUdp.Send(sendBytes_multi);

使用:

arduino 复制代码
Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

是为了可以重复使用绑定端口号。

arduino 复制代码
JoinMulticastGroup //加入IP组
DropMulticastGroup //退出IP组,在不需要时记得退出,不然还是会接受到此IP组发出的消息

结果:

注意:

组播更多注意事项,参考链接:

perl 复制代码
https://blog.lindexi.com/post/C-dotnet-core-%E5%B1%80%E5%9F%9F%E7%BD%91%E7%BB%84%E6%92%AD%E6%96%B9%E6%B3%95.html

四、UDP接受

使用

csharp 复制代码
public byte[] Receive(ref System.Net.IPEndPoint? remoteEP);

接受,其中remoteEP参数为远端IP。

相关推荐
辜月十2 小时前
Conda配置文件.condarc
后端
今天没有盐2 小时前
Python字符串操作全解析:从基础定义到高级格式化
后端·scala·编程语言
IT 行者2 小时前
Spring Framework 6.x 异常国际化完全指南:让错误信息“说“多国语言
java·后端·spring·异常处理·problemdetail·国际化i18n
Victor3563 小时前
Hibernate(18)Hibernate的延迟加载是什么?
后端
Victor3563 小时前
Hibernate(17)什么是Hibernate的悲观锁?
后端
一只叫煤球的猫3 小时前
并行不等于更快:CompletableFuture 让你更慢的 5 个姿势
java·后端·性能优化
Codebee3 小时前
深入揭秘Ooder框架信息架构中的钩子机制:从原理到企业级实践
后端
cike_y3 小时前
Spring使用注解开发
java·后端·spring·jdk1.8
小蒜学长3 小时前
python餐厅点餐系统(代码+数据库+LW)
数据库·spring boot·后端·python