UDP(User Data Protocol,用户数据报协议)
(1) UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
(2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
(3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
(4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
(5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。
(6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
TCP与UDP的区别:
1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;
5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
废话不多说,请看demo。
Server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace udpServer { class Program { public static byte[] buffer; public static Socket server; public static EndPoint clientEP; static void Main(string[] args) { server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10086); //服务端需要绑定ip server.Bind(endPoint); //其实无需初始化,ReceiveFrom后会由ref赋值,但是该api不允许传空对象 clientEP = (EndPoint)endPoint; Console.WriteLine("开启服务器!"); //开启一个线程连接,必须的,否则主线程卡死 Thread Thread = new Thread(new ThreadStart(SocketReceive)); Thread.Start(); } private static void SocketReceive() { //进入接收循环 while (true) { //对data清零 buffer = new byte[1024]; //获取客户端,获取客户端数据,用引用给客户端赋值 int recvLen = server.ReceiveFrom(buffer, ref clientEP); Console.WriteLine("message from: " + clientEP.ToString()); //打印客户端信息 //输出接收到的数据 string recvStr = UTF8Encoding.UTF8.GetString(buffer, 0, recvLen); Console.WriteLine("我是服务器,接收到客户端的数据" + recvStr); //将接收到的数据经过处理再发送出去 string sendStr = "From Server: " + recvStr; SocketSend(sendStr); } } static void SocketSend(string sendStr) { //清空发送缓存 byte[] sendData = new byte[1024]; //数据类型转换 sendData = UTF8Encoding.UTF8.GetBytes(sendStr); //发送给指定客户端 server.SendTo(sendData, sendData.Length, SocketFlags.None, clientEP); } } } |
Client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | using UnityEngine; using System.Collections; //引入库 using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; public class Client : MonoBehaviour { //服务端终结点 IPEndPoint ipEnd; //客户端socket Socket socket; //通过reveiveFrom函数,初始化的服务端终结点 EndPoint serverEnd; // Use this for initialization void Start() { InitSocket(); } void Update() { if (Input.GetKeyDown(KeyCode.A)) { SocketSend("hello world"); } } //初始化 void InitSocket() { //定义连接的服务器ip和端口,可以是本机ip,局域网,互联网 ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10086); //定义套接字类型,在主线程中定义 socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //定义服务端endpoint IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0); serverEnd = (EndPoint)sender; print("waiting for sending UDP dgram"); //建立初始连接,这句非常重要,第一次连接初始化了serverEnd后面才能收到消息 SocketSend("hello world"); //开启一个线程连接,必须的,否则主线程卡死 Thread connectThread = new Thread(new ThreadStart(SocketReceive)); connectThread.Start(); } void SocketSend(string sendStr) { //清空发送缓存 byte[] sendData = new byte[1024]; //数据类型转换 sendData = UTF8Encoding.UTF8.GetBytes(sendStr); //发送给指定服务端 socket.SendTo(sendData, sendData.Length, SocketFlags.None, ipEnd); } //服务器接收 void SocketReceive() { //进入接收循环 while (true) { //对data清零 byte[] recvData = new byte[1024]; //获取客户端,获取服务端端数据,用引用给服务端赋值,实际上服务端已经定义好并不需要赋值 int recvLen = socket.ReceiveFrom(recvData, ref serverEnd); print("message from: " + serverEnd.ToString()); //打印服务端信息 //输出接收到的数据 string recvStr = UTF8Encoding.UTF8.GetString(recvData, 0, recvLen); print("我是客户端,接收到服务器的数据" + recvStr); } } } |
运行效果