En respuesta a las necesidades de los aficionados, se hace un ejemplo de comunicación entre el servidor y el cliente.Los requisitos son relativamente simples.Usamos el protocolo Socket TCP para construir y usamos directamente el método de mensaje de longitud fija.
1. Construcción del servidor:
Abra Visual Studio, Archivo/Nuevo/Proyecto y cree una aplicación de consola:
Cree una nueva clase de servidor y una clase de cliente:
el código se muestra a continuación:
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);
}
}
}
Para el análisis de datos, usamos la herramienta LitJson.dll aquí. Si no tiene la herramienta, puede comunicarse conmigo para enviar una copia y abrir View/Solution Explorer:
Haga clic con el botón derecho en Solución/Agregar/Referencias del proyecto:
Haga clic en Examinar, busque la herramienta LitJson y haga clic en Aceptar para consultar:
Con LitJson, podemos analizar los datos, pero aún no hemos definido ninguna estructura de datos. Los datos que queremos transmitir incluyen imágenes y caracteres, por lo que aquí se define la siguiente estructura de datos:
[Serializable]
public class SimpleData
{
/// <summary>
/// 图片数据
/// </summary>
public string pic;
/// <summary>
/// 字符内容
/// </summary>
public string content;
}
Después de introducir el espacio de nombres LitJson, analice los datos:
//拆包:解析数据
private void Unpack(string data)
{
SimpleData simpleData = JsonMapper.ToObject<SimpleData>(data);
Console.WriteLine(simpleData.pic);
Console.WriteLine(simpleData.content);
}
Ahora ejecuta nuestro servidor:
En segundo lugar, la construcción del cliente de Unity:
Cree una clase de Cliente, herede de MonoBehaviour y defina una estructura de datos consistente con el servidor:
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;
}
Cree un objeto vacío y monte el script del Cliente para él:
Ejecute el programa Unity, regrese a la ventana de la consola del servidor y podrá ver que nos hemos conectado con éxito al servidor:
Encontramos una imagen, enviamos la imagen y los datos del personaje a la prueba del servidor, la colocamos en el directorio de Activos y leemos los datos de esta imagen a través del código:
Código de ejemplo, cuélguelo en el mismo objeto que el script del Cliente:
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);
}
}
}
Ejecute el programa, haga clic en el botón enviar datos y regrese a la consola del servidor para ver que hemos recibido los datos:
Lo anterior es un ejemplo de envío de datos del cliente al servidor. A continuación, tratamos de enviar datos del servidor al cliente:
El servidor coloca la imagen en la solución como se muestra en la figura y leemos los datos de la imagen a través del código:
Enviamos datos al cliente cuando el cliente accede, por lo que lo escribiremos en el constructor del Cliente por el momento:
/// <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);
}
En el cliente, hemos almacenado los datos enviados por el servidor en la cola, por lo que los datos se sacan de la cola a su vez:
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);
}
}
Bienvenidos a la cuenta pública "Programador Salvaje Contemporáneo"