一般来说,在Socket传输的实际应用中使用TCP较多,这里的UDP只是简单的实现了两个用户之间的通信
1、首先,我们需要清楚什么是UDP:
UDP —用户数据报协议,是一个无连接的简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
2、UDP的特点:
UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。 UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。 UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。
3、UDP的适用情况:
UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如:
- 语音广播
- 视频
- TFTP(简单文件传送)
- SNMP(简单网络管理协议)
- RIP(路由信息协议,如报告股票市场,航空信息)
- DNS(域名解释)
注重速度流畅
UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
4、UDP传输代码:
流程: 1、初始化Socket环境 --> 2、创建客户端Socket --> 3、Send和Receive消息 --> 4、关闭Socket
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Threading;
public class UDPSocket
{
//委托
public delegate void ReceiveDelegate(byte[] buffer, int realSize, string ip, int port);
//代理
ReceiveDelegate receiveCallBack;
public Socket myUDP;
/// <summary>
/// 传递一个端口号
/// </summary>
/// <param name="point"></param>
public UDPSocket(int point, int bufferSize, ReceiveDelegate back)
{
receiveCallBack = back;
Initial(point, bufferSize);
}
/// <summary>
/// 初始化Socket(套接字 描述符)
/// </summary>
/// <param name="point"></param>
public void Initial(int point, int size)
{
receiveBuffer = new byte[size];
//AddressFamily: 地址协议族:ip4 ip6
//ProtocolType:UDP TCP
//SocketType:TCP :数据流方式Stream UDP:数据报Dgram
myUDP = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//显卡的地址
//绑定网卡:IPAddress.Any:自动获取一个比较闲的网卡
//端口号:不是1024都可以
IPEndPoint tmpIPPoint = new IPEndPoint(IPAddress.Any, point);
//绑定IP地址和端口号
myUDP.Bind(tmpIPPoint);
Thread tmpThread = new Thread(ReceiveThread);
tmpThread.Start();
}
bool isRun = true;
public void ReceiveThread()
{
while (isRun)
{
if (myUDP != null && myUDP.Available < 1)
{
Thread.Sleep(100);
}
ReceiveBytes();
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="buffer">发送的byte[]</param>
/// <param name="ip">IP地址</param>
/// <param name="port">端口号</param>
/// Send:发送TDP
/// SendTo:发送UDP
public void SendBytes(byte[] buffer, string ip, int port)
{
IPEndPoint tmpEnd = new IPEndPoint(IPAddress.Parse(ip), port);
int realSend = myUDP.SendTo(buffer, buffer.Length, SocketFlags.None, tmpEnd);
}
byte[] receiveBuffer;
//存接收方的IP地址、端口号
IPEndPoint receivePoint = new IPEndPoint(IPAddress.Any, 0);
/// <summary>
/// 接收消息
/// </summary>
/// ReceiveFrom:SnedTo
/// Receive:Send
public void ReceiveBytes()
{
EndPoint tpEnd = receivePoint;
//ref:会修改IP地址和端口号
//真正接收的字节数 阻塞
int realReceive = myUDP.ReceiveFrom(receiveBuffer, ref tpEnd);
receiveCallBack(receiveBuffer, realReceive, receivePoint.Address.ToString(), receivePoint.Port);
}
/// <summary>
/// 关闭(释放)
/// </summary>
public void Dispose()
{
isRun = false;
if (myUDP != null)
{
myUDP.Close();
}
}
}
调用UDPSocket实现不同端口的信息传递
using System;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
public class UDPQQTalk : MonoBehaviour
{
[SerializeField] private InputField mine;
[SerializeField] private Transform parent;
[SerializeField] private GameObject prefab;
[SerializeField] private ScrollRect scroll;
[SerializeField] private RectTransform viewPort;
[Header("发送的端口")]
[SerializeField] private int sendPort;
[Header("接收的端口")]
[SerializeField] private int receivePort;
private UDPSocket sendSocket;
private byte[] reBuffer;
private bool isReceive;
void SendBuffer(byte[] buffer, int realSize, string ip, int port)
{
lock (this)
{
reBuffer = new byte[realSize];
Buffer.BlockCopy(buffer, 0, reBuffer, 0, realSize);
isReceive = true;
}
}
private void Start()
{
sendSocket = new UDPSocket(sendPort, 1024, new UDPSocket.ReceiveDelegate(SendBuffer));
mine.onEndEdit.AddListener(_ => MineTalk());
}
private void Update()
{
if (isReceive)
{
isReceive = false;
lock (this)
{
Debug.Log("接收消息:" + sendPort);
//进行UI的操作
Text text = Instantiate(prefab, parent).GetComponent<Text>();
string tp = Encoding.Default.GetString(reBuffer);
text.text = receivePort.ToString() + ":\n" + tp;
Invoke("DoMove", 0.1f);
}
}
}
/// <summary>
/// 发送方
/// </summary>
public void MineTalk()
{
if (!string.IsNullOrEmpty(mine.text))
{
//UI操作
Text text = Instantiate(prefab, parent).GetComponent<Text>();
text.text = sendPort.ToString() + ":\n" + mine.text;
//发送消息给接收端口
byte[] buffer = Encoding.Default.GetBytes(mine.text);
sendSocket.SendBytes(buffer, "127.0.0.1", receivePort);
mine.text = string.Empty;
Invoke("DoMove", 0.1f);
}
}
/// <summary>
/// 移动Content到最高处
/// </summary>
private void DoMove()
{
if (parent.GetComponent<RectTransform>().rect.height > viewPort.rect.height)
{
float height = parent.GetComponent<RectTransform>().rect.height - viewPort.rect.height;
parent.DOLocalMoveY(height, 0.3f);
}
}
}
效果呈现: