Unity【Socket TCP】- 服务端与客户端通讯的简单示例
应粉丝需求做一个服务端与客户端通讯的示例,需求比较简单,我们使用Socket TCP协议去构建,直接使用固定长度信息法。
一、服务端搭建:
打开Visual Studio,文件/新建/项目,创建一个控制台应用:
新建Server类与Client类:
代码如下:
-
using System.Net;
-
using System.Net.Sockets;
-
-
namespace CoderZ
-
{
-
public class Server
-
{
-
//端口
-
private const int port = 8008;
-
//客户端列表
-
private List<Client> clients = new List<Client>();
-
-
private static void Main(string[] args)
-
{
-
Console.WriteLine("服务端启动...");
-
Server server = new Server();
-
server.Init();
-
}
-
-
//服务端初始化
-
private void Init()
-
{
-
TcpListener listener = new TcpListener(IPAddress.Any, port);
-
listener.Start();
-
try
-
{
-
while (true)
-
{
-
Console.WriteLine("等待客户端接入...");
-
TcpClient client = listener.AcceptTcpClient();
-
Client clientInstance = new Client(client, this);
-
clients.Add(clientInstance);
-
Console.WriteLine($"{client.Client.RemoteEndPoint}接入.");
-
}
-
}
-
catch(Exception error)
-
{
-
throw new Exception(error.ToString());
-
}
-
}
-
-
/// <summary>
-
/// 广播:向所有客户端发送数据
-
/// </summary>
-
/// <param name="data"></param>
-
public void Broadcast(string data)
-
{
-
for (int i = 0; i < clients.Count; i++)
-
{
-
clients[i].Send(data);
-
}
-
}
-
/// <summary>
-
/// 移除客户端
-
/// </summary>
-
/// <param name="client"></param>
-
public void Remove(Client client)
-
{
-
if (clients.Contains(client))
-
{
-
clients.Remove(client);
-
}
-
}
-
}
-
}
-
using System.Text;
-
using System.Net.Sockets;
-
-
namespace CoderZ
-
{
-
public class Client
-
{
-
private Server server;
-
private TcpClient tcpClient;
-
private NetworkStream stream;
-
-
/// <summary>
-
/// 构造函数
-
/// </summary>
-
/// <param name="tcpClient"></param>
-
/// <param name="server"></param>
-
public Client(TcpClient tcpClient, Server server)
-
{
-
this.server = server;
-
this.tcpClient = tcpClient;
-
//启动线程 读取数据
-
Thread thread = new Thread(TcpClientThread);
-
thread.Start();
-
}
-
-
private void TcpClientThread()
-
{
-
stream = tcpClient.GetStream();
-
//使用固定长度
-
byte[] buffer = new byte[1024];
-
-
try
-
{
-
while (true)
-
{
-
int length = stream.Read(buffer, 0, buffer.Length);
-
if (length != 0)
-
{
-
string data = Encoding.UTF8.GetString(buffer, 0, length);
-
//解包
-
Unpack(data);
-
}
-
}
-
}
-
catch(Exception error)
-
{
-
Console.WriteLine(error.ToString());
-
}
-
finally
-
{
-
server.Remove(this);
-
}
-
}
-
//拆包:解析数据
-
private void Unpack(string data)
-
{
-
-
}
-
/// <summary>
-
/// 发送数据
-
/// </summary>
-
/// <param name="data"></param>
-
public void Send(string data)
-
{
-
byte[] buffer = Encoding.UTF8.GetBytes(data);
-
stream.Write(buffer, 0, buffer.Length);
-
}
-
}
-
}
数据的解析我们这里使用LitJson.dll工具,没有该工具的可以联系我发一份,打开视图/解决方案资源管理器:
右键解决方案/添加/项目引用:
点击浏览,找到LitJson工具,点击确定进行引用:
有了LitJson后我们便可以进行数据的解析,但是我们还没有定义任何数据结构,我们想要传输的数据包括图片和字符,因此这里定义如下数据结构:
-
[Serializable]
-
public class SimpleData
-
{
-
/// <summary>
-
/// 图片数据
-
/// </summary>
-
public string pic;
-
/// <summary>
-
/// 字符内容
-
/// </summary>
-
public string content;
-
}
引入LitJson命名空间后,解析数据:
-
//拆包:解析数据
-
private void Unpack(string data)
-
{
-
SimpleData simpleData = JsonMapper.ToObject<SimpleData>(data);
-
Console.WriteLine(simpleData.pic);
-
Console.WriteLine(simpleData.content);
-
}
此时运行我们的服务端:
二、Unity客户端搭建:
创建Client类,继承自MonoBehaviour,同时定义与服务端一致的数据结构:
-
using System;
-
using System.Text;
-
using UnityEngine;
-
using System.Threading;
-
using System.Net.Sockets;
-
using System.Collections.Generic;
-
-
public class Client : MonoBehaviour
-
{
-
private string ipAddress;
-
private int port;
-
private bool isConnected;
-
private Thread connectThread;
-
private Thread readDataThread;
-
private TcpClient tcpClient;
-
private NetworkStream stream;
-
//将数据存于队列 依次取出
-
private Queue<string> queue = new Queue<string>();
-
-
private void Start()
-
{
-
connectThread = new Thread(ConnectThead);
-
connectThread.Start();
-
}
-
//连接线程
-
private void ConnectThead()
-
{
-
tcpClient = new TcpClient();
-
tcpClient.BeginConnect(ipAddress, port, ConnectThreadCallBack, tcpClient);
-
float waitTime = 0f;
-
while (!isConnected)
-
{
-
Thread.Sleep(500);
-
waitTime += Time.deltaTime;
-
if (waitTime > 3f)
-
{
-
waitTime = 0f;
-
throw new Exception("连接超时");
-
}
-
}
-
}
-
-
private void ConnectThreadCallBack(IAsyncResult result)
-
{
-
tcpClient = result.AsyncState as TcpClient;
-
if (tcpClient.Connected)
-
{
-
isConnected = true;
-
tcpClient.EndConnect(result);
-
stream = tcpClient.GetStream();
-
readDataThread = new Thread(ReadDataThread);
-
readDataThread.Start();
-
}
-
}
-
//读取数据线程
-
private void ReadDataThread()
-
{
-
try
-
{
-
while (isConnected)
-
{
-
byte[] buffer = new byte[1024];
-
int length = stream.Read(buffer, 0, buffer.Length);
-
string data = Encoding.UTF8.GetString(buffer, 0, length);
-
queue.Enqueue(data);
-
}
-
}
-
catch(Exception error)
-
{
-
throw new Exception(error.ToString());
-
}
-
}
-
//程序退出时关闭线程
-
private void OnApplicationQuit()
-
{
-
stream?.Close();
-
connectThread?.Abort();
-
readDataThread?.Abort();
-
}
-
-
/// <summary>
-
/// 发送数据
-
/// </summary>
-
/// <param name="content"></param>
-
public void SendData(string content)
-
{
-
byte[] buffer = Encoding.UTF8.GetBytes(content);
-
stream.Write(buffer, 0, buffer.Length);
-
}
-
}
-
-
[Serializable]
-
public class SimpleData
-
{
-
/// <summary>
-
/// 图片数据
-
/// </summary>
-
public string pic;
-
/// <summary>
-
/// 字符内容
-
/// </summary>
-
public string content;
-
}
创建一个空物体为其挂载Client脚本:
运行Unity程序,回到服务端控制台窗口,可以看到我们已经成功与服务端连接:
我们找一张图片,将图片和字符数据发送给服务端测试,将它放到Assets目录中,我们通过代码读取这张图片的数据:
示例代码,将其与Client脚本挂在同一物体上:
-
using System;
-
using System.IO;
-
using UnityEngine;
-
using LitJson;
-
-
public class Foo : MonoBehaviour
-
{
-
private void OnGUI()
-
{
-
if (GUILayout.Button("发送数据", GUILayout.Width(200f), GUILayout.Height(50f)))
-
{
-
var bytes = File.ReadAllBytes(Application.dataPath + "/pic.jpg");
-
SimpleData simpleData = new SimpleData()
-
{
-
pic = Convert.ToString(bytes),
-
content = "这是一张汽车图片"
-
};
-
//使用LitJson序列化
-
string data = JsonMapper.ToJson(simpleData);
-
GetComponent<Client>().SendData(data);
-
}
-
}
-
}
运行程序点击发送数据按钮,回到服务端控制台查看可以看见我们已经接收到数据:
上面是客户端发送数据到服务端的示例,下面我们尝试从服务端发送数据到客户端:
服务端将图片放于解决方案中如图所示位置,我们通过代码读取图片数据:
我们在客户端接入的时候将数据发送给客户端,因此就暂且将其写在Client构造函数里:
-
/// <summary>
-
/// 构造函数
-
/// </summary>
-
/// <param name="tcpClient"></param>
-
/// <param name="server"></param>
-
public Client(TcpClient tcpClient, Server server)
-
{
-
this.server = server;
-
this.tcpClient = tcpClient;
-
//启动线程 读取数据
-
Thread thread = new Thread(TcpClientThread);
-
thread.Start();
-
-
byte[] bytes = File.ReadAllBytes("pic.jpg");
-
SimpleData simpleData = new SimpleData()
-
{
-
pic = Convert.ToBase64String(bytes),
-
content = "这是一张图片"
-
};
-
string data = JsonMapper.ToJson(simpleData);
-
Send(data);
-
}
客户端中我们已经将服务端发送的数据存于队列中,因此从队列中将数据依次取出:
-
private void Update()
-
{
-
if (queue.Count > 0)
-
{
-
string data = queue.Dequeue();
-
//使用LitJson反序列化
-
SimpleData simpleData = JsonMapper.ToObject<SimpleData>(data);
-
byte[] bytes = Convert.FromBase64String(simpleData.pic);
-
//将图片存到Assets目录
-
File.WriteAllBytes(Application.dataPath + "/test.jpg", bytes);
-
//打印字符内容
-
Debug.Log(simpleData.content);
-
}
-
}
文章来源: coderz.blog.csdn.net,作者:CoderZ1010,版权归原作者所有,如需转载,请联系作者。
原文链接:coderz.blog.csdn.net/article/details/123750597
- 点赞
- 收藏
- 关注作者
评论(0)