示例一:
客户端向服务端(虚拟助手)发送消息,服务端接收到消息;
客户端代码:
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine.UI;
public class UDPClient : MonoBehaviour
{
public Button btn_StartConnect;
public Button btn_Disconnect;
public Button btn_Send;
public InputField input_IP;
public InputField input_Port;
public InputField input_Data;
public Text txt_ConnectState;
UdpClient udpClient;
IPEndPoint ipEndPoint; //客户端端口
byte[] sendData = new byte[1024]; //发送的数据,必须为字节
void Awake()
{
btn_StartConnect.onClick.AddListener(ButtonStartConnect);
btn_Disconnect.onClick.AddListener(ButtonDisconnect);
btn_Send.onClick.AddListener(ButtonSend);
}
void OnApplicationQuit()
{
ButtonDisconnect();
}
///
/// 开始连接
///
public void ButtonStartConnect()
{
ipEndPoint = new IPEndPoint(IPAddress.Parse(input_IP.text), int.Parse(input_Port.text));//定义连接的服务器ip和端口
udpClient = new UdpClient();//初始化一个客户端
IPEndPoint senderReceive = new IPEndPoint(IPAddress.Any, 0);
txt_ConnectState.text = "已经建立连接" + "\n";
}
///
/// 断开连接
///
public void ButtonDisconnect()
{
txt_ConnectState.text += "断开连接";
if (udpClient != null)
udpClient.Close();
}
///
/// 客户端发送数据
///
///
void SocketSend(string sendStr)
{
//清空发送缓存
sendData = new byte[1024];
//数据类型转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给所有服务端
udpClient.Send(sendData, sendData.Length, ipEndPoint);
txt_ConnectState.text += "客户端发送了:" + sendStr + "\n";
}
///
/// 发送数据
///
public void ButtonSend()
{
if ((!string.IsNullOrEmpty(input_Data.text))&&udpClient!=null)
{
SocketSend(input_Data.text);
}
else if (string.IsNullOrEmpty(input_Data.text) && udpClient != null)
{
txt_ConnectState.text += "请输入要发送的消息" + "\n";
}
else if(udpClient==null)
{
txt_ConnectState.text += "请先建立连接" + "\n";
}
}
}
单片机多功能调试助手 下载链接:百度网盘 请输入提取码
提取码:izmn
示例二:
客户端向服务端发送一张图片,服务端接收该图片后,显示5秒,并保存在Assets文件夹下;
客户端代码:
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine.UI;
public class UDPClient : MonoBehaviour
{
public static UDPClient instance;//单例模式(当游戏中的游戏对象只有一个实例的时候,宜用之)
public Text txt_ConnectState;//连接状态
string UDPClientIP;//客户端IP
Socket socket;//Socket(套接字)
EndPoint serverEnd;//服务端
IPEndPoint ipEnd;//用网络端点表示 IP和端口
string recvStr;//接收的字符串
byte[] recvData;//字节数组:接收的数据
byte[] sendData;//字节数组:发送的数据
int recvLen = 0;//接收数据的长度
Thread connectThread;//开启一个线程
bool isClientActive = false;//客户端是否激活
bool isStartHeart = false;//是否开始心跳
int port;//端口号
bool isReSend = false;//是否让客户端重新发送数据包
string reSendStrIndex;//要重新发送的字符串
float timerRate = 5;//发送消息频率
float timerInterval = 0f;//接收服务端心跳反馈的时间间隔
public delegate void ReSendIndexDeledate(string str);//委托
public event ReSendIndexDeledate ReSendIndexEvent;//事件
void Awake()
{
instance = this;
}
void Start()
{
UDPClientIP = "127.0.0.1";//客户端IP(默认本地IP:127.0.0.1)
port = 9000;//端口
UDPClientIP = UDPClientIP.Trim();//删除字符串中所有的空格
isClientActive = true;//客户端被激活
InitSocket();
}
void Update()
{
if (isStartHeart)//是否开始心跳
{
HeartSend();
}
timerInterval += Time.deltaTime;//检测 发送心跳 与 心跳反馈回来 间隔的时间
if (timerInterval > 6f)//间隔时间大于6秒
{
print("连接异常");
txt_ConnectState.text = "连接异常";
timerInterval = 0f;
}
if (isReSend)//是否重新发送
{
print("重新发送");
txt_ConnectState.text = "重新发送";
if (ReSendIndexEvent != null)//如果重新发送的事件不为空
{
ReSendIndexEvent(reSendStrIndex);//重新发送这个字符串
reSendStrIndex = null;//置空
isReSend = false;//置为默认值
}
}
}
void OnApplicationQuit()
{
isStartHeart = false;
isClientActive = false;
SocketQuit();
Thread.Sleep(25);
}
///
/// 初始化Socket
///
private void InitSocket()
{
//定义连接的服务器ip和端口(可以是本机ip,局域网,互联网)
ipEnd = new IPEndPoint(IPAddress.Parse(UDPClientIP), port);
//定义套接字类型,在主线程中定义
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//socket.Bind(ipEnd);
//定义服务端
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
serverEnd = (EndPoint)sender;
print("等待连接");
txt_ConnectState.text="等待连接";
isStartHeart = true;//开始心跳监听
//客户端发送心跳消息后,计时器开始计时,判断5秒内是否能收到服务端的反馈
HeartSend();
//开启一个线程连接,否则主线程卡死
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
///
/// 开始向服务端发送心跳包
///
private void HeartSend()
{
timerRate += Time.deltaTime;
if (timerRate > 5f)
{
try
{
SocketSend("alive");
}
catch
{
}
timerRate = 0f;
}
}
///
/// 客户端发送给服务端数据
///
/// 字符串
public void SocketSend(string sendStr)
{
//清空发送缓存
sendData = new byte[1500];
//数据类型转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给指定服务端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, ipEnd);
}
///
/// 客户端接收服务端发来的数据
///
public void SocketReceive()
{
//进入接收循环
while (isClientActive)
{
//对接收数据缓存清零
recvData = new byte[20000];
try
{
//获取服务端数据
recvLen = socket.ReceiveFrom(recvData, ref serverEnd);
if (isClientActive == false)
{
break;
}
}
catch
{
}
//输出接收到的数据
if (recvLen > 0)
{
//接收到的信息
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
}
print("接收到服务端发来的数据:" + recvStr);
//txt_ConnectState.text = "接收到服务端发来的数据:" + recvStr;
//心跳回馈
if (recvStr == "keeping")//接收到服务端反馈的数据:keeping
{
print("连接正常");
//txt_ConnectState.text = "连接正常";
timerInterval = 0;
}
else if (recvStr != null)//如果没有收到服务端发来的keeping,且 接收的字符串中不为空
{
//重新发送
reSendStrIndex = recvStr;
isReSend = true;
}
}
}
//关闭Socket连接
public void SocketQuit()
{
//关闭这条线程
if(connectThread!=null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//关闭socket连接
if (socket != null)
socket.Close();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;
public class SendImage : MonoBehaviour
{
public Button btn_Send;
string imageString;//字符串表示图片
string path;//路径
string picStr;//图片字符串
int num;
byte[] imagebytes;//字节数组表示图片
Dictionary UDPStringDic = new Dictionary();//创建一个字典
void Awake()
{
btn_Send.onClick.AddListener(ButtonSend);//按钮事件监听
}
void Start()
{
UDPClient.instance.ReSendIndexEvent += GetReSendIndexFromUDPClient;//事件监听:客户端重新发送数据
path = Application.streamingAssetsPath + "/" + "电网.jpg";//获取文件路径
FileStream files = new FileStream(path, FileMode.Open);//文件流
imagebytes = new byte[files.Length];//字节数组:图片
files.Read(imagebytes, 0, imagebytes.Length); //读取字节数组
files.Close();//关闭文件流,释放资源
//files.Close();//再关闭文件流,释放资源
picStr = Convert.ToBase64String(imagebytes);//将数组转换为64位基数的字符串
}
void OnDisable()
{
UDPClient.instance.ReSendIndexEvent -= GetReSendIndexFromUDPClient;//移除监听
}
///
/// 按钮:发送
///
public void ButtonSend()
{
StartCoroutine(SendPicture());//开启一段协程:发送图片数据给服务端
}
///
/// 发送图片数据给服务端
///
/// 返回一个协程
IEnumerator SendPicture()
{
UDPSplit(picStr);//分割图片字符串
yield return new WaitForSeconds(0.1f);//间隔0.1秒
for (int i = 0; i < num - 1000; i++)
{
if (UDPStringDic.TryGetValue(i, out imageString))//根据指定的键 获取关键的值
/*out参数:如果在一个方法中 返回多个相同类型的值时,可以考虑返回一个数组;
但是,如果返回多个不同类型的值时,就可以考虑使用out参数。*/
{
UDPClient.instance.SocketSend(imageString);
}
}
yield return new WaitForSeconds(0.1f);//间隔0.1秒
//发送完成后 发一条信息给服务端
UDPClient.instance.SocketSend("done");
}
///
/// 客户端重新发送数据
///
///
public void GetReSendIndexFromUDPClient(string str)
{
string[] reSendNum;//字符串数组:重新发送
int newindex;
reSendNum = str.Split('_');//下划线分割出来的字符串数组
for (int i = 0; i < reSendNum.Length; i++)
{
if (int.TryParse(reSendNum[i], out newindex))
{
if (UDPStringDic.TryGetValue(newindex, out imageString))
{
UDPClient.instance.SocketSend(imageString);
}
}
}
//发送完成后 再发一条信息给服务端
UDPClient.instance.SocketSend("done");
print("重新发送完毕");
UDPClient.instance.txt_ConnectState.text = "重新发送完毕";
}
///
/// 分割数据
///
///
private void UDPSplit(string str)
{
int index = 0;//索引
int maxIndex = 1000;//最大索引
string newstr;//新字符串
int stringTag = 1000;//字符串标签号
UDPStringDic.Clear();//清空这个字典
num = (str.Length / 1000) + 1 + 1000; //将数字变成四位数的,三个字节
// print(num-1000);
for (int i = 0; i < num - 1000; i++)
{
if (maxIndex > str.Length - index)
{
maxIndex = str.Length - index;
}
newstr = "1551683020" + "_" + num + "_" + stringTag + "_" + str.Substring(index, maxIndex); //包名,包长,包的顺序号,包的内容
UDPStringDic.Add(stringTag - 1000, newstr);
stringTag++;
index += 1000;
}
}
}
服务端代码:
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System;
using System.IO;
public class UDPServer : MonoBehaviour
{
public static UDPServer instance;
public Text txt_ConnectState;//连接状态
public string recvStr; //接收的字符串
public bool isStartSend = false;//是否开始发送
Socket socket; //目标socket
EndPoint clientEnd; //客户端
IPEndPoint ipEnd; //侦听端口
byte[] recvData; //字节数组:接收的数据
byte[] sendData = new byte[1024]; //字节数组:发送的数据
int recvLen; //接收数据的长度
Thread connectThread; //连接线程
int port;//端口号
bool isSendImage;//是否发送图片
bool isServerActive = false;//是否启动服务端
string newCombineStr;//新合并的字符串
string newImageName;//图片名字
int newImageCount = 0;//包的长度
int newStrIndex = 0;//新字符串的索引
string newImageMessage;//包的内容
bool isFirst = true;//判断是否是第一次接受消息
string oldImageName;//旧图片的名字
string dicStr;
float timerInterval = 0;
bool isStartCheck = false;
Dictionary newImageDic = new Dictionary();//字典:新图片
List doneIndex = new List();//集合:已完成传输的字符串索引值
public delegate void UDPServerDeledate(Texture2D byths);//委托
public event UDPServerDeledate UDPserverEvent;//事件
void Awake()
{
instance = this;
}
void Start()
{
port = 9000;
isServerActive = true;
InitSocket(); //在这里初始化server
}
void Update()
{
timerInterval += Time.deltaTime;
if (isStartCheck)
{
if (timerInterval > 6f)
{
print("网络连接异常");
txt_ConnectState.text = "网络连接异常";
timerInterval = 0f;
}
}
if (isSendImage)
{
ParseByteArr(newCombineStr);
newCombineStr = null;
isSendImage = false;
}
}
void OnDisable()
{
isServerActive = false;
SocketQuit();
Thread.Sleep(100);
}
///
/// 初始化Socket
///
private void InitSocket()
{
//定义侦听端口
ipEnd = new IPEndPoint(IPAddress.Any, port);
//定义套接字类型
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
//服务端需要绑定ip
socket.Bind(ipEnd);
//定义客户端
IPEndPoint sender = new IPEndPoint(IPAddress.Broadcast, 0);
clientEnd = (EndPoint)sender;
print("local:等待连接数据");
txt_ConnectState.text = "等待连接数据";
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
///
/// 服务器向客户端发送消息
///
///
public void SocketSend(string sendStr)
{
//清空发送缓存
sendData = new byte[20000];
//数据类型转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给指定客户端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEnd);
}
///
/// 服务端接收来自客户端的消息
///
private void SocketReceive()
{
//进入接收循环
while (isServerActive)//激活服务端
{
//对data清零
recvData = new byte[1500];
try
{
//服务端接收从客户端发来的数据
recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
if (isServerActive == false)
{
break;
}
}
catch
{
}
if (recvLen > 0)
{
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
//输出接收到的数据
Debug.Log("Clent:__" + recvStr + "++" + recvLen);
//接收客户端心跳包,进行心跳反馈
if (recvStr == "alive")
{
HeartCheck();
}
else if (recvStr == "done")
{
//当接收到的信息为done时,判断接收到的图片包数量是否够,不够就发送未收到的包的标识号,让客户端再发送一下
CheckPackage();
}
else if (recvLen > 18) //图片包头为29个字节
{
// print("这是图片");
//合并发来的图片
ConmbineString(recvStr);
}
}
}
}
///
/// 当接收到客户端发送的done消息后,判断接收到的图片包是否完整
///
private void CheckPackage()
{
//未发送包的标识号
string reSendPackageindex;
reSendPackageindex = null;
if (doneIndex.Count <= 0)
{
print("接收成功");
for (int i = 0; i < newImageDic.Count; i++)
{
if (newImageDic.TryGetValue(i, out dicStr))
{
newCombineStr = newCombineStr + dicStr;
}
}
//置0置空
isSendImage = true;
newImageCount = 0;
newStrIndex = 0;
isFirst = true;
newImageDic.Clear();
doneIndex.Clear();
}
else
{
print("接收失败,重新请求");
txt_ConnectState.text = "接收失败,重新请求";
//判断哪些包没有收到
for (int i = 0; i < doneIndex.Count; i++)
{
reSendPackageindex = doneIndex[i] + "_" + reSendPackageindex;
}
SocketSend(reSendPackageindex);
print("请求发送未成功包");
txt_ConnectState.text = "请求发送未成功包";
}
}
///
/// 将分包发来的消息合成一个包
///
///
private void ConmbineString(string perStr)
{
//0.图片名字(21字节)--1.包的长度(1000为起始点,4字节)--2.包的下标(1000为起始点4个字节)--3.包的内容
//分割字符串 "_"
string[] strs = perStr.Split('_');
//名字
newImageName = strs[0];
newImageCount = int.Parse(strs[1]) - 1000;
newStrIndex = int.Parse(strs[2]) - 1000;
newImageMessage = strs[3];
if (isFirst)
{
oldImageName = newImageName;
isFirst = false;
newCombineStr = null;
//将将要收到的包的标识号存进集合里边,每接收到对应的数据就移除该标识号
for (int i = 0; i < newImageCount; i++)
{
doneIndex.Add(i);
}
}
if (newImageName == oldImageName)
{
// print(newImageCount);
if (!newImageDic.ContainsKey(newStrIndex))
{
//每接收到对应的数据就移除该标识号
try
{
doneIndex.Remove(newStrIndex);
}
catch
{
print("数据传输失败");
txt_ConnectState.text = "数据传输失败";
}
newImageDic.Add(newStrIndex, newImageMessage);
}
}
}
///
/// 检测心跳包
///
private void HeartCheck()
{
isStartCheck = true;
timerInterval = 0f;
SocketSend("keeping");
print("连接正常");
//txt_ConnectState.text = "连接正常";
}
///
/// 发来的字节包括:图片的字节长度(前四个字节)和图片字节
/// 得到发来的字节中图片字节长度和图片字节数组
///
private void ParseByteArr(string receStr)
{
byte[] bytes = Convert.FromBase64String(receStr);
string timestamp = GetTimeStamp().ToString();
string filename = Application.streamingAssetsPath+timestamp + ".jpg";
File.WriteAllBytes(filename, bytes);//写入所有的字节
Texture2D tex2D = new Texture2D(100, 100);//创建Texture2D贴图:宽100,高100
tex2D.LoadImage(bytes);//根据字节数组加载图片
if (UDPserverEvent != null)
{
UDPserverEvent(tex2D);//订阅这个事件
}
}
///
/// 关闭Socket连接
///
private void SocketQuit()
{
//关闭线程
if(connectThread!=null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//关闭socket
if (socket != null)
{
try
{
socket.Close();
}
catch
{
}
}
Debug.LogWarning("local:断开连接");
txt_ConnectState.text = "断开连接";
}
///
/// 获取事件跨度(用秒和毫秒来表示)
///
///
///
public static long GetTimeStamp(bool bflag = true)
{
//TimeSpan表示时间间隔;DataTime.UtcNow表示计算机上的当前日期
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
//long表示64位整数,int表示32位整数 short表示16位整数,
long ret;
if (bflag)
ret = Convert.ToInt64(ts.TotalSeconds);//用秒来表示,强转为64位整数
else
ret = Convert.ToInt64(ts.TotalMilliseconds);//用毫秒来表示,强转为64位整数
return ret;
}
}
using UnityEngine;
using UnityEngine.UI;
public class LoadImageFromClient : MonoBehaviour
{
public RawImage newImage;
void Start()
{
UDPServer.instance.UDPserverEvent += ReceiveByteFromUDPServer;//监听事件:从服务端接收字节数组
}
void OnDisable()
{
UDPServer.instance.UDPserverEvent -= ReceiveByteFromUDPServer;//移除监听
}
///
/// 发来的字节包括:图片的字节长度(前四个字节)和图片字节
/// 得到发来的字节中图片字节长度和图片字节数组
///
void ReceiveByteFromUDPServer(Texture2D newTexture)
{
newImage.gameObject.SetActive(true);
newImage.texture = newTexture;
Invoke("SetDefultImage", 5f);
}
///
/// 图片置空
///
void SetDefultImage()
{
newImage.texture = null;
}
}
示例二源码链接:https://pan.baidu.com/s/1DYG1d3DGJtOIzhQ7dqyNTA
提取码:5s9c