一、概述

UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.NET框架通过UdpClient、TcpListener 、TcpClient这几个类对Socket进行了封装,使其使用更加方便, 本文就通过这几个封装过的类讲解一下相关应用。

二、UDP基本应用

与TCP通信不同,UDP通信是不分服务端和客户端的,通信双方是对等的。为了描述方便,我们把通信双方称为发送方和接收方。

发送方:

首先创建一个UDP对象:

            string locateIP = "127.0.0.1";    //本机IP

            int locatePort = ;            //发送端口

            IPAddress locateIpAddr = IPAddress.Parse(locateIP);

            IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort);

            UdpClient  udpClient = new UdpClient(locatePoint);

发送数据:

            string remoteIP = "127.0.0.1";             //目标机器IP

            int remotePort = ;                     //接收端口          

            IPAddress remoteIpAddr = IPAddress.Parse(remoteIP);

            IPEndPoint remotePoint = new IPEndPoint(remoteIpAddr, remotePort);

            byte[] buffer = Encoding.UTF8.GetBytes(“hello”);

            udpClient.Send(buffer, buffer.Length, remotePoint);

以上就完成了一个发送任务,一个较完整的发送代码如下:

    public partial class FormServer : Form
{
private UdpClient udpClient = null; private void btnConnect_Click(object sender, EventArgs e)
{
string locateIP = "127.0.0.1";
int locatePort = ;
IPAddress locateIpAddr = IPAddress.Parse(locateIP);
IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort);
udpClient = new UdpClient(locatePoint); this.groupWork.Enabled = true;
} private void Send_Click(object sender, EventArgs e)
{
string text = this.txtSend.Text.Trim();
string remoteIP = "127.0.0.1";
int remotePort = ;
byte[] buffer = Encoding.UTF8.GetBytes(text); if (udpClient != null)
{
IPAddress remoteIp = IPAddress.Parse(remoteIP);
IPEndPoint remotePoint = new IPEndPoint(remoteIp, remotePort);
udpClient.Send(buffer, buffer.Length, remotePoint);
} Debug.WriteLine("Send OK");
}
}

接收端:

首先创建一个UDP对象:

            string locateIP = "127.0.0.1";

            int locatePort = ;

            IPAddress locateIpAddr = IPAddress.Parse(locateIP);

            IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort);

            UdpClient  udpClient = new UdpClient(locatePoint);

接收数据:

    IPEndPoint remotePoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), );

     var received = udpClient.Receive(ref remotePoint);

     string info = Encoding.UTF8.GetString(received);

     string from=$” {remotePoint.Address}:{remotePoint.Port}”;

注意两点:

1、remotePoint是获得发送方的IP信息,定义时可以输入任何合法的IP和端口信息;

2、Receive方法是阻塞方法,所以需要在新的线程内运行,程序会一直等待接收数据,当接收到一包数据时程序就返回,要持续接收数据需要重复调用Receive方法。

一个较完整的接收端代码如下:

   public partial class FormClent : Form
{
private UdpClient udpClient = null; private void btnConnect_Click(object sender, EventArgs e)
{
string locateIP = "127.0.0.1";
int locatePort = ;
IPAddress locateIpAddr = IPAddress.Parse(locateIP);
IPEndPoint locatePoint = new IPEndPoint(locateIpAddr, locatePort);
udpClient = new UdpClient(locatePoint);
IPEndPoint remotePoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), ); Task.Run(() =>
{
while (true)
{
if (udpClient != null)
{
var received = udpClient.Receive(ref remotePoint);
string info = Encoding.UTF8.GetString(received);
string from=$” {remotePoint.Address}:{remotePoint.Port}”;
}
}
});
}
}

三、丢包和乱序问题

当发送端发送一包数据时,不管对方是否接收都是发送成功的,UDP协议本身并不会对发送的可靠性进行验证。(这里的可靠性是指是否接收到,如果对方接收到数据包,其内容还是可靠的,这个在链路层进行了保证。)同时,由于网络延时等因素,先发送的包并不能确定先被接收到,所以由于这两个原因,UDP通信存在丢包和乱序的情况。

某些业务场景下,比如实时状态监控,可能对丢包和乱序情况并不敏感, 可以不用处理,但大部分情况下还是介意丢包的,简单的处理办法就是把包的头部固定长度的空间拿出来存放核对信息,比如包编号,如果有缺失,可以要求发送方重发,也可以进行排序。

四、将数据接收包装为事件

我们对UdpClent又进行一次封装,启用一个线程进行接收数据,将接收到的数据包通过事件发布出来,这样使用起来就更方便了。

namespace Communication.UDPClient
{
public class UdpStateEventArgs : EventArgs
{
public IPEndPoint remoteEndPoint;
public byte[] buffer = null;
} public delegate void UDPReceivedEventHandler(UdpStateEventArgs args); public class UDPClient
{
private UdpClient udpClient;
public event UDPReceivedEventHandler UDPMessageReceived; public UDPClient(string locateIP, int locatePort)
{
IPAddress locateIp = IPAddress.Parse(locateIP);
IPEndPoint locatePoint = new IPEndPoint(locateIp, locatePort);
udpClient = new UdpClient(locatePoint); //监听创建好后,创建一个线程,开始接收信息
Task.Run(() =>
{
while (true)
{
UdpStateEventArgs udpReceiveState = new UdpStateEventArgs(); if (udpClient != null)
{
IPEndPoint remotePoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), );
var received = udpClient.Receive(ref remotePoint);
udpReceiveState.remoteEndPoint = remotePoint;
udpReceiveState.buffer = received;
UDPMessageReceived?.Invoke(udpReceiveState);
}
else
{
break;
}
}
});
}
}
}

具体使用办法:

        private void btnConnect_Click(object sender, EventArgs e)
{
string locateIP = "127.0.0.1";
int locatePort = ;
UDPClient udpClient = new UDPClient(locateIP, locatePort);
udpClient.UDPMessageReceived += UdpClient_UDPMessageReceived;
} private void UdpClient_UDPMessageReceived(UdpStateEventArgs args)
{
var remotePoint = args.remoteEndPoint;
string info = Encoding.UTF8.GetString(args.buffer);
}

限于篇幅,我们只封装了数据接收,时间使用时需要把发送功能也封装进去,使这个类同时具备发送和接收功能,发送功能的封装比较简单就不贴代码了。

传送门:

C#网络编程入门系列包括三篇文章:

(一)C#网络编程入门之UDP

(二)C#网络编程入门之TCP

(三)C#网络编程入门之HTTP

C#网络编程入门之UDP的更多相关文章

  1. C#网络编程入门之TCP

    目录: C#网络编程入门系列包括三篇文章: (一)C#网络编程入门之UDP (二)C#网络编程入门之TCP (三)C#网络编程入门之HTTP 一.概述 UDP和TCP是网络通讯常用的两个传输协议,C# ...

  2. 脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?

    本文引用了“帅地”发表于公众号苦逼的码农的技术分享. 1.引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又有什么关系呢 ...

  3. 脑残式网络编程入门(五):每天都在用的Ping命令,它到底是什么?

    本文引用了公众号纯洁的微笑作者奎哥的技术文章,感谢原作者的分享. 1.前言   老于网络编程熟手来说,在测试和部署网络通信应用(比如IM聊天.实时音视频等)时,如果发现网络连接超时,第一时间想到的就是 ...

  4. 脑残式网络编程入门(四):快速理解HTTP/2的服务器推送(Server Push)

    本文原作者阮一峰,作者博客:ruanyifeng.com. 1.前言 新一代HTTP/2 协议的主要目的是为了提高网页性能(有关HTTP/2的介绍,请见<从HTTP/0.9到HTTP/2:一文读 ...

  5. 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

    本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...

  6. 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...

  7. 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

    .引言 网络编程中TCP协议的三次握手和四次挥手的问题,在面试中是最为常见的知识点之一.很多读者都知道“三次”和“四次”,但是如果问深入一点,他们往往都无法作出准确回答. 本篇文章尝试使用动画图片的方 ...

  8. [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?     http://www.52im.net/thread-1732-1-1.html   1.引言 本文接上篇<脑残式网 ...

  9. [转帖]脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

    脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手   http://www.52im.net/thread-1729-1-1.html     1.引言 网络编程中TCP协议的三次握手和 ...

随机推荐

  1. JS异步与同步

    这里展示一个操作场景:需要对数据进行异步处理,但这次操作可能会失败,所以需要定期对数据进行再次处理,直至处理成功. 实现:手动触发的处理以及定期触发的处理,是相同的,即可以抽取出来成一个公共函数,定期 ...

  2. VS Code 全部快捷键一览表(巨TM全)

    常用 General 按 Press 功能 Function Ctrl + Shift + P,F1 显示命令面板 Show Command Palette Ctrl + P 快速打开 Quick O ...

  3. CodeForces 1058C C. Vasya and Golden Ticket

    C. Vasya and Golden Ticket time limit per test1 second memory limit per test256 megabytes inputstand ...

  4. session与cookie的浅谈

    cookie的用途: 当你浏览网页时,会有一些推送消息,大多数是你最近留意过的同类东西,比如你想买桌子,上淘宝搜了一下,结果连着几天会有各种各样的桌子的链接.这是因为你浏览某个网页的时候,WEB 服务 ...

  5. Java——单双引号的区别

    单引号: 单引号包括的是单个字符,表示的是char类型.例如: char  a='1' 双引号: 双引号可以包括0个或者多个字符,表示的是String类型. 例如: String s="ab ...

  6. CSS中的间距设置与盒子模型

    CSS间距 内补白 外补白 盒子模型 CSS间距 很多时候我们为了美观,需要对内容进行留白设置,这时候就需要设置间距了. 内补白 设置元素的内间距 padding: 检索或设置对象四边的内部边距 pa ...

  7. Linux编程实现递归查找文件

    命令格式如下:SCRIPT_NAME       SEARCH_ON_DIR       SEARCH_FILENAME #!/bin/bash Find(){ files=`` for file i ...

  8. [hdu4801]搜索

    http://acm.hdu.edu.cn/showproblem.php?pid=4801 状态和生成状态的过程处理好了,这个题就是简单的搜索题了== #include <iostream&g ...

  9. [hdu3484]枚举

    题意:给两个个01矩阵,有两种操作,(1)交换两列(2)反转某一行.求能否通过若干操作使两矩阵相等 思路:(把所有对B的操作放到A上来,这一定是可以做到一样的效果的)枚举B矩阵的第一列对应A矩阵的第几 ...

  10. 世界这么大,Python 也想去看看

    把时间线拉回到 2015 年 4 月 13 日,一位河南省实验中学的心里老师在辞职信上写下了「世界那么大,我想去看看」这句话,后来爆红网络,我想这位心里老师当时写这句话的时候肯定没想到这句只有十个字的 ...