C#网络编程入门之UDP
一、概述
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#网络编程入门之UDP的更多相关文章
- C#网络编程入门之TCP
目录: C#网络编程入门系列包括三篇文章: (一)C#网络编程入门之UDP (二)C#网络编程入门之TCP (三)C#网络编程入门之HTTP 一.概述 UDP和TCP是网络通讯常用的两个传输协议,C# ...
- 脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?
本文引用了“帅地”发表于公众号苦逼的码农的技术分享. 1.引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又有什么关系呢 ...
- 脑残式网络编程入门(五):每天都在用的Ping命令,它到底是什么?
本文引用了公众号纯洁的微笑作者奎哥的技术文章,感谢原作者的分享. 1.前言 老于网络编程熟手来说,在测试和部署网络通信应用(比如IM聊天.实时音视频等)时,如果发现网络连接超时,第一时间想到的就是 ...
- 脑残式网络编程入门(四):快速理解HTTP/2的服务器推送(Server Push)
本文原作者阮一峰,作者博客:ruanyifeng.com. 1.前言 新一代HTTP/2 协议的主要目的是为了提高网页性能(有关HTTP/2的介绍,请见<从HTTP/0.9到HTTP/2:一文读 ...
- 脑残式网络编程入门(三):HTTP协议必知必会的一些知识
本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...
- 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?
1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...
- 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手
.引言 网络编程中TCP协议的三次握手和四次挥手的问题,在面试中是最为常见的知识点之一.很多读者都知道“三次”和“四次”,但是如果问深入一点,他们往往都无法作出准确回答. 本篇文章尝试使用动画图片的方 ...
- [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?
脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么? http://www.52im.net/thread-1732-1-1.html 1.引言 本文接上篇<脑残式网 ...
- [转帖]脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手
脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手 http://www.52im.net/thread-1729-1-1.html 1.引言 网络编程中TCP协议的三次握手和 ...
随机推荐
- C++--浅谈开发系统的经验
最近写了不少类了,从垃圾代码爬坑,虽然还是很垃圾,但是照葫芦画瓢,有几分神韵.在这里总结一下,写类的经验教训. 第一步 分析: 当拿到一个要求时,要先去考虑怎样一个类到底该实现什么样的功能,有什么样的 ...
- 超轻量级网络SqueezeNet网络解读
SqueezeNet网络模型非常小,但分类精度接近AlexNet. 这里复习一下卷积层参数的计算 输入通道ci,核尺寸k,输出通道co,参数个数为: 以AlexNet第一个卷积为例,参数量达到:3*1 ...
- sqlserver2005定期备份和清除
1.打开管理->维护计划 2.右键点击新建维护计划 3.给新的维护计划自定义命名 4.可以看左下角的维护方式 5.拖动“备份数据库”到右边 6.选中,编辑备份方式 7.选择备份方式,所有数据库, ...
- B. Heaters 思维题 贪心 区间覆盖
B. Heaters 这个题目虽然只有1500的分数,但是我还是感觉挺思维的,我今天没有写出来,然后看了一下题解 很少做这种区间覆盖的题目,也不是很擅长,接下来讲讲我看完题解后的思路. 题目大意是:给 ...
- 【Hadoop离线基础总结】HDFS的API操作
HDFS的API操作 创建maven工程并导入jar包 注意 由于cdh版本的所有的软件涉及版权的问题,所以并没有将所有的jar包托管到maven仓库当中去,而是托管在了CDH自己的服务器上面,所以我 ...
- input唤起键盘影响移动端底部fixed定位
主要代码如下: public docmHeight = document.documentElement.clientHeight || document.body.clientHeight; // ...
- Git上传文件、文件夹到github
上传一个文件: $ git add test.txt 上传多个文件: $ git add test.txt demo.txt 同理: 上传一个文件夹: $ git add learngit 上传多个文 ...
- 这或许是最详细的JUC多线程并发总结
多线程进阶---JUC并发编程 完整代码传送门,见文章末尾 1.Lock锁(重点) 传统 Synchronizd package com.godfrey.demo01; /** * descripti ...
- PMS学习
一,PMS的adb相关重要指令 1,adb shell dumpsys package(dump所有的系统内apk信息) 2,adb shell dumpsys package “com.androi ...
- 2018-06-18 sublime代码编辑器
sublime代码编辑器:主流前端开发编辑器,体积较小,运行速度快,文本功能强大,支持VI扩展,linux下面有个自带的文本编辑神器,名字叫做:vi ,熟练vi模式可以大量减少使用鼠标的次数,从而增加 ...