夏媛 發表於 2020-5-27 14:33:00

C#网络编程入门之UDP

<h3>一、概述</h3>
<p>UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.NET框架通过<span lang="EN-US">UdpClient、<span lang="EN-US">TcpListener 、<span lang="EN-US">TcpClient这几个类对Socket进行了封装,使其使用更加方便,</span></span>&nbsp;本文就通过这几个封装过的类讲解一下相关应用。</span></p>
<p>&nbsp;</p>
<p><strong>二、UDP基本应用</strong></p>
<p>与TCP通信不同,UDP通信是不分服务端和客户端的,通信双方是对等的。为了描述方便,我们把通信双方称为发送方和接收方。</p>
<p><strong>发送方:</strong></p>
<p>首先创建一个UDP对象:</p>
<div class="cnblogs_code">
<pre>            <span style="color: rgba(0, 0, 255, 1)">string</span> locateIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>;    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">本机IP</span>

            <span style="color: rgba(0, 0, 255, 1)">int</span> locatePort = <span style="color: rgba(128, 0, 128, 1)">9001</span>;            <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">发送端口</span>
<span style="color: rgba(0, 0, 0, 1)">
            IPAddress locateIpAddr </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(locateIP);

            IPEndPoint locatePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(locateIpAddr, locatePort);

            UdpClientudpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> UdpClient(locatePoint);</pre>
</div>
<p>发送数据:</p>
<div class="cnblogs_code">
<pre>            <span style="color: rgba(0, 0, 255, 1)">string</span> remoteIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>;             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">目标机器IP</span>

            <span style="color: rgba(0, 0, 255, 1)">int</span> remotePort = <span style="color: rgba(128, 0, 128, 1)">9002</span>;                     <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">接收端口          </span>
<span style="color: rgba(0, 0, 0, 1)">
            IPAddress remoteIpAddr </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(remoteIP);

            IPEndPoint remotePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(remoteIpAddr, remotePort);

            </span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] buffer =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetBytes(“hello”);

            udpClient.Send(buffer, buffer.Length, remotePoint);</span></pre>
</div>
<p>以上就完成了一个发送任务,一个较完整的发送代码如下:&nbsp;</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="" id="code_img_closed_25e59a69-3f6a-4fac-a513-0276ae89dd59" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="" id="code_img_opened_25e59a69-3f6a-4fac-a513-0276ae89dd59" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_25e59a69-3f6a-4fac-a513-0276ae89dd59" class="cnblogs_code_hide">
<pre>    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FormServer : Form
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> UdpClient udpClient = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnConnect_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> locateIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">int</span> locatePort = <span style="color: rgba(128, 0, 128, 1)">9001</span><span style="color: rgba(0, 0, 0, 1)">;
            IPAddress locateIpAddr </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(locateIP);
            IPEndPoint locatePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(locateIpAddr, locatePort);
            udpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UdpClient(locatePoint);

            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.groupWork.Enabled = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Send_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> text = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtSend.Text.Trim();
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> remoteIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">int</span> remotePort = <span style="color: rgba(128, 0, 128, 1)">9002</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] buffer =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetBytes(text);

            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (udpClient != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                IPAddress remoteIp </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(remoteIP);
                IPEndPoint remotePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(remoteIp, remotePort);
                udpClient.Send(buffer, buffer.Length, remotePoint);
            }

            Debug.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Send OK</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    }</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>&nbsp;</p>
<p><strong>接收端:</strong>&nbsp;</p>
<p>首先创建一个UDP对象:</p>
<div class="cnblogs_code">
<pre>            <span style="color: rgba(0, 0, 255, 1)">string</span> locateIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;

            </span><span style="color: rgba(0, 0, 255, 1)">int</span> locatePort = <span style="color: rgba(128, 0, 128, 1)">9002</span><span style="color: rgba(0, 0, 0, 1)">;

            IPAddress locateIpAddr </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(locateIP);

            IPEndPoint locatePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(locateIpAddr, locatePort);

            UdpClientudpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> UdpClient(locatePoint);</pre>
</div>
<p>接收数据:&nbsp;</p>
<div class="cnblogs_code">
<pre>    IPEndPoint remotePoint = <span style="color: rgba(0, 0, 255, 1)">new</span> IPEndPoint(IPAddress.Parse(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1.1.1.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>), <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);

   </span><span style="color: rgba(0, 0, 255, 1)">var</span> received = udpClient.Receive(<span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> remotePoint);

   </span><span style="color: rgba(0, 0, 255, 1)">string</span> info =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetString(received);

   </span><span style="color: rgba(0, 0, 255, 1)">string</span> <span style="color: rgba(0, 0, 255, 1)">from</span>=$” {remotePoint.Address}:{remotePoint.Port}”;</pre>
</div>
<p>注意两点:</p>
<p>1、remotePoint是获得发送方的IP信息,定义时可以输入任何合法的IP和端口信息;</p>
<p>2、Receive方法是阻塞方法,所以需要在新的线程内运行,程序会一直等待接收数据,当接收到一包数据时程序就返回,要持续接收数据需要重复调用Receive方法。</p>
<p>一个较完整的接收端代码如下:&nbsp;</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="" id="code_img_closed_90bf5ed4-49d2-4f40-90cd-8b3cbc3cb104" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="" id="code_img_opened_90bf5ed4-49d2-4f40-90cd-8b3cbc3cb104" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_90bf5ed4-49d2-4f40-90cd-8b3cbc3cb104" class="cnblogs_code_hide">
<pre>   <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FormClent : Form
   {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> UdpClient udpClient = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnConnect_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> locateIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">int</span> locatePort = <span style="color: rgba(128, 0, 128, 1)">9002</span><span style="color: rgba(0, 0, 0, 1)">;
            IPAddress locateIpAddr </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(locateIP);
            IPEndPoint locatePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(locateIpAddr, locatePort);
            udpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UdpClient(locatePoint);
            IPEndPoint remotePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> IPEndPoint(IPAddress.Parse(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1.1.1.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>), <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);

            Task.Run(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                  </span><span style="color: rgba(0, 0, 255, 1)">if</span> (udpClient != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                  {
                        </span><span style="color: rgba(0, 0, 255, 1)">var</span> received = udpClient.Receive(<span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> remotePoint);
                        </span><span style="color: rgba(0, 0, 255, 1)">string</span> info =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetString(received);
                        </span><span style="color: rgba(0, 0, 255, 1)">string</span> <span style="color: rgba(0, 0, 255, 1)">from</span>=<span style="color: rgba(0, 0, 0, 1)">$” {remotePoint.Address}:{remotePoint.Port}”;
                  }
                }
            });
      }
   }</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>&nbsp;</p>
<h4>三、丢包和乱序问题</h4>
<p>&nbsp; &nbsp;当发送端发送一包数据时,不管对方是否接收都是发送成功的,UDP协议本身并不会对发送的可靠性进行验证。(这里的可靠性是指是否接收到,如果对方接收到数据包,其内容还是可靠的,这个在链路层进行了保证。)同时,由于网络延时等因素,先发送的包并不能确定先被接收到,所以由于这两个原因,UDP通信存在丢包和乱序的情况。</p>
<p>&nbsp; &nbsp;某些业务场景下,比如实时状态监控,可能对丢包和乱序情况并不敏感, 可以不用处理,但大部分情况下还是介意丢包的,简单的处理办法就是把包的头部固定长度的空间拿出来存放核对信息,比如包编号,如果有缺失,可以要求发送方重发,也可以进行排序。</p>
<p>&nbsp;</p>
<p><strong><span style="font-size: 1em">四、将数据接收包装为事件</span></strong></p>
<p>&nbsp;我们对UdpClent又进行一次封装,启用一个线程进行接收数据,将接收到的数据包通过事件发布出来,这样使用起来就更方便了。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> Communication.UDPClient
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> UdpStateEventArgs : EventArgs
    {      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint remoteEndPoint;      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>[] buffer = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">delegate</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> UDPReceivedEventHandler(UdpStateEventArgs args);

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> UDPClient
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> UdpClient udpClient;
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">event</span><span style="color: rgba(0, 0, 0, 1)"> UDPReceivedEventHandler UDPMessageReceived;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> UDPClient(<span style="color: rgba(0, 0, 255, 1)">string</span> locateIP, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> locatePort)
      {
            IPAddress locateIp </span>=<span style="color: rgba(0, 0, 0, 1)"> IPAddress.Parse(locateIP);
            IPEndPoint locatePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IPEndPoint(locateIp, locatePort);
            udpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UdpClient(locatePoint);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">监听创建好后,创建一个线程,开始接收信息</span>
            Task.Run(() =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                  UdpStateEventArgs udpReceiveState </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UdpStateEventArgs();

                  </span><span style="color: rgba(0, 0, 255, 1)">if</span> (udpClient != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                  {
                        IPEndPoint remotePoint </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> IPEndPoint(IPAddress.Parse(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1.1.1.1</span><span style="color: rgba(128, 0, 0, 1)">"</span>), <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);
                        </span><span style="color: rgba(0, 0, 255, 1)">var</span> received = udpClient.Receive(<span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> remotePoint);
                        udpReceiveState.remoteEndPoint </span>=<span style="color: rgba(0, 0, 0, 1)"> remotePoint;
                        udpReceiveState.buffer </span>=<span style="color: rgba(0, 0, 0, 1)"> received;
                        UDPMessageReceived</span>?<span style="color: rgba(0, 0, 0, 1)">.Invoke(udpReceiveState);
                  }
                  </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
                  {
                        </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
                  }
                }
            });
      }
    }
}</span></pre>
</div>
<p>具体使用办法:</p>
<div class="cnblogs_code">
<pre>      <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnConnect_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> locateIP = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">int</span> locatePort = <span style="color: rgba(128, 0, 128, 1)">9002</span><span style="color: rgba(0, 0, 0, 1)">;         
            UDPClient udpClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UDPClient(locateIP, locatePort);
            udpClient.UDPMessageReceived </span>+=<span style="color: rgba(0, 0, 0, 1)"> UdpClient_UDPMessageReceived;         
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> UdpClient_UDPMessageReceived(UdpStateEventArgs args)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> remotePoint =<span style="color: rgba(0, 0, 0, 1)"> args.remoteEndPoint;
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> info =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetString(args.buffer);
      }</span></pre>
</div>
<p>&nbsp;限于篇幅,我们只封装了数据接收,实际使用时需要把发送功能也封装进去,使这个类同时具备发送和接收功能,发送功能的封装比较简单就不贴代码了。</p>
<p>&nbsp;</p>
<h3>传送门:</h3>
<p>C#网络编程入门系列包括三篇文章:</p>
<p>(一)C#网络编程入门之UDP</p>
<p>(二)C#网络编程入门之TCP</p>
<p>(三)C#网络编程入门之HTTP</p>

</div>
<div id="MySignature" role="contentinfo">
    <hr>
签名区:<br>
如果您觉得这篇博客对您有帮助或启发,请点击右侧【推荐】支持,谢谢!
<hr><br><br>
来源:https://www.cnblogs.com/seabluescn/p/12972417.html
頁: [1]
查看完整版本: C#网络编程入门之UDP