文章目录
前言
unity中搭建简单的通讯功能一般采取Socket连接方式,下面是自己经常使用的功能代码,比较稳定可靠,代码贴出来一是为了分享,二是自己使用时也能够方便查找,忘记时可以看一下;
PS:抱着求是的态度把winform版本自己测试了一下,功能可用,源文件放文章最后了;
一、Socket介绍
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口
二、Unity使用步骤
1.创建单例模板
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingleTon<T> : MonoBehaviour where T : SingleTon<T>
{
private static T instance;
static readonly object mylock = new object();
public static T GetInstance
{
get
{
if (instance == null)
{
lock (mylock)
{
instance = FindObjectOfType(typeof(T)) as T;
if (instance == null)
{
GameObject obj = new GameObject();
obj.hideFlags = HideFlags.HideAndDontSave;
instance = (T)obj.AddComponent(typeof(T));
}
}
}
return instance;
}
}
protected virtual void Awake()
{
if (instance == null)
{
instance = (T)this;
}
}
protected virtual void OnDestroy()
{
if (instance == this)
{
instance = null;
}
}
}
2.编辑NetworkStream服务端代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using UnityEngine;
/// <summary>
/// SocketServer
/// 接收发送消息都采取以下格式:"消息头#" + "消息" + "#E\r\n",用来规避消息粘包问题
/// </summary>
public class SocketServerManager : SingleTon<NetworkManager>
{
/// <summary>
/// 连接客户端集合
/// </summary>
Dictionary<string, SocketClient> Clients = new Dictionary<string, SocketClient>();
SocketClient _socketClient;
TcpListener server;
public int GetClientCount
{
get { return Clients.Count; }
}
private void Start()
{
StartListener();
}
public void StartListener()
{
server = new TcpListener(IPAddress.Any, 6000);
server.Start();
server.BeginAcceptTcpClient(OnConnect, null);//开始监听
}
private void OnConnect(IAsyncResult ar)
{
try
{
TcpClient curClient = server.EndAcceptTcpClient(ar);
_socketClient = new SocketClient(curClient);//有连接就初始化一个
Debug.Log("客户端" + curClient.Client.RemoteEndPoint.ToString() + "已经连接");
//开启接收
_socketClient.ReceiveMsg();
server.BeginAcceptTcpClient(OnConnect, null);//开始监听
}
catch (Exception)
{
}
}
public void SendToClients(string _str)//消息发送
{
string sendMsg = _str;
string[] keys = new string[Clients.Count];
Clients.Keys.CopyTo(keys, 0);
foreach (string key in keys)
{
try
{
Clients[key].SendMsg(sendMsg);
}
catch (Exception ex)
{
Debug.Log("客户端" + key + "出现异常," + ex.Message);
if (Clients.ContainsKey(key))
{
CloseClientConnect(Clients[key]);
}
}
}
}
/// <summary>
/// 制定客户端发送消息
/// </summary> NetworkManager.GetInstance.SendToClient("Client0", "消息头#" + "消息" + "#E\r\n");
/// <param name="_clientName"></param>
/// <param name="_data"></param>
public void SendToClient(string _clientName, string _data)
{
Debug.Log("<color=green>" + "发送信息=============" + _data + "</color>");
if (Clients.ContainsKey(_clientName))
{
Clients[_clientName].SendMsg(_data);
}
}
/// <summary>
/// 根据客户端名称发送消息
/// </summary> SocketServerManager.GetInstance.SendToClient("消息头#" + "消息" + "#E\r\n", "Client0", "Client1", "Client2");
/// <param name="_data">消息</param>
/// <param name="_clientNames">客户端名称</param>
public void SendToClient(string _data, params string[] _clientNames)
{
for (int i = 0; i < _clientNames.Length; i++)
{
if (Clients.ContainsKey(_clientNames[i]))
{
Clients[_clientNames[i]].SendMsg(_data);
}
}
}
public void CloseClientConnect(SocketClient client)//关闭连接
{
if (client.IfConnect)//如果是连接状态,再执行移除
{
client.CloseClient();
client.IfConnect = false;
//从字典中移除
Clients.Remove(client.MenuNos);
Console.WriteLine("移除了客户端" + client.MenuNos);
}
}
public void InitClient(SocketClient client)//连接初始化
{
if (Clients.ContainsKey(client.MenuNos))
{
Clients[client.MenuNos] = client;
}
else
{
Clients.Add(client.MenuNos, client);
}
}
protected override void OnDestroy()
{
base.OnDestroy();
string[] keys = new string[Clients.Count];
Clients.Keys.CopyTo(keys, 0);
foreach (string key in keys)
{
CloseClientConnect(Clients[key]);
}
if (server != null)
{
server.Stop();
}
}
}
public class SocketClient
{
#region 字段
private const int bufferSize = 8192;
byte[] buffer = new byte[bufferSize];
bool ifConnect;
string serverIP;
int serverPort;
string menuNos;
NetworkStream stream;
TcpClient client;
StreamReader sr;
StreamWriter sw;
#endregion
#region 属性
public NetworkStream Stream
{
get { return stream; }
set { stream = value; }
}
public TcpClient Client
{
get { return client; }
set { client = value; }
}
public bool IfConnect { get { return ifConnect; } set { ifConnect = value; } }
public string ServerIP { get { return serverIP; } set { serverIP = value; } }
public int ServerPort { get { return serverPort; } set { serverPort = value; } }
public string MenuNos { get { return menuNos; } set { menuNos = value; } }
#endregion
public SocketClient(TcpClient client)
{
if (client != null)
{
this.client = client;
stream = client.GetStream();
sw = new StreamWriter(stream);
sr = new StreamReader(stream);
ifConnect = true;
}
}
/// <summary>
/// 开启异步接收消息
/// </summary>
public void ReceiveMsg()
{
if (client != null)
{
stream.BeginRead(buffer, 0, bufferSize, OnReceive, null);//开启一步接收
}
}
/// <summary>
/// 接收到消息的回调,用于处理消息
/// </summary>
/// <param name="ar"></param>
private void OnReceive(IAsyncResult ar)
{
try
{
int count = stream.EndRead(ar);
try
{
string msg = Encoding.UTF8.GetString(buffer, 0, count);//接收到消息
string[] dataArr = Regex.Split(msg, "\r\n");
Debug.Log("接收到Client客户端消息=====" + msg);
for (int i = 0; i < dataArr.Length; i++)
{
if (dataArr[i].Equals(""))
{
continue;
}
string[] msgArr = dataArr[i].Split('#');
if (msgArr.Length != 3)//验证消息
{
continue;
}
if (msgArr[1].Equals("Init"))//验证初始化
{
this.MenuNos = msgArr[0];
SocketServerManager.GetInstance.InitClient(this);
}
else if (msgArr[1].Equals("ApplicationQuit"))
{
SocketServerManager.GetInstance.CloseClientConnect(this);
}
else
{
//将消息进一步解析
if (this.MenuNos.Equals("Clitnt0"))//来自于客户端消息处理
{
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("消息处理失败" + ex.StackTrace);
}
stream.BeginRead(buffer, 0, bufferSize, OnReceive, null);
}
catch (Exception ex)
{
Console.WriteLine(ex);
//消息接收失败,说明连接异常
// Console.WriteLine("消息接收异常,请检查客户端端{0}的连接",client.Client.RemoteEndPoint.ToString());
Console.WriteLine("接收消息发生错误:" + this.MenuNos);
//NetworkManager.Instance.CloseClientConnect(this);
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="msg">消息内容</param>
public void SendMsg(string msg)
{
byte[] data = Encoding.UTF8.GetBytes(msg);
stream.Write(data, 0, data.Length);
stream.Flush();
}
/// <summary>
/// 断开连接
/// </summary>
public void CloseClient()
{
if (client != null)
{
try
{
if (sr != null)
{
sr.Close();
}
if (sw != null)
{
sw.Close();
}
stream.Close();
client.Close();
client.Client.Close();
ifConnect = false;
}
catch
{
Console.WriteLine("关闭");
return;
}
}
ifConnect = false;
}
}
3.编辑NetworkStream客户端代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
public class SocketClient : SingleTon<SocketClient>
{
//定义稳委托事件用来处理消息,其他脚本可调用 SocketHelperClient.GetInstance.CommonCallBack += MsgReceive;
public delegate void CommonThreadCallBackDelegate(string type);
public CommonThreadCallBackDelegate CommonCallBack;
TcpClient clientTcp;
NetworkStream ns;
StreamReader sr;
StreamWriter sw;
bool ifRight = true;
string ipAddress = "127.0.0.1";
public string EXENAME = "Clitnt0";
#region 通用
void Start()
{
StartConnect();
StartCoroutine(HertJump());
}
private void StartConnect()
{
Loom.RunAsync(ConnectToServer);
}
//消息发送方法 : string msg = EXENAME + "#" + msg + "#E\r\n";
//SocketHelperClient.GetInstance.SendToServer(msg);
public void SendToServer(string message)
{
if (ifRight)
{
try
{
sw.WriteLine(message);
sw.Flush();
}
catch
{
ifRight = false;
}
}
}
IEnumerator HertJump()//检测
{
while (true)
{
yield return new WaitForSeconds(3f);
// Debug.Log("-----3s后检测----");
if (!ifRight)
{
if (sw != null)
{
sw.Close();
sw = null;
}
StartConnect();
}
}
}
private void ConnectToServer()//连接服务器
{
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ipAddress), 6000);
clientTcp = new TcpClient();
try
{
clientTcp.Connect(ipep);
ifRight = true;
}
catch (SocketException ex)
{
Debug.Log("-----连接失败----");
ifRight = false;
return;
}
catch (StackOverflowException ex)
{
}
ns = clientTcp.GetStream();//数据流,读写
sr = new StreamReader(ns);
sw = new StreamWriter(ns);
Loom.QueueOnMainThread(() =>
{
SendToServer(EXENAME + "#Init#E");
});
while (ifRight)
{
try
{
byte[] bytes = Encoding.UTF8.GetBytes(sr.ReadLine());
// Debug.LogError(Encoding.UTF8.GetString(bytes).ToString());
if (bytes.Length > 0)
{
Loom.QueueOnMainThread(() =>
{
UpdCommonInfo(Encoding.UTF8.GetString(bytes).ToString());
});
}
}
catch
{
ifRight = false;
}
}
}
private void UpdCommonInfo(string _data)
{
// Debug.LogError(_data);
string[] datas = Regex.Split(_data, "\r\n");
for (int i = 0; i < datas.Length; i++)
{
if (datas[i].Equals(""))
{
continue;
}
string[] data1 = datas[i].Split('#');
if (data1.Length != 3)
{
continue;
}
CommonCallBack(data1[1]);
}
}
protected override void OnDestroy()
{
base.OnDestroy();
SendToServer(EXENAME + "#ApplicationQuit#E\r\n");
try
{
if (sr != null)
{
sr.Close();
sw.Close();
ns.Close();
}
clientTcp.Close();
}
catch
{
}
Loom.CloseThread();
}
#endregion
}
4.其他所需要的代码,放入Unity场景中
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
public class Loom : MonoBehaviour
{
public static int maxThreads = 8;
static int numThreads;
private static Thread[] curThreads = new Thread[maxThreads];
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
static bool initialized;
static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action) //主线程队列
{
QueueOnMainThread(action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
public static Thread RunAsync(Action a) //异步运行
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
int tIndex = -1;
for (int i = 0; i < curThreads.Length; i++)
{
if (curThreads[i] == null || !curThreads[i].IsAlive)
{
tIndex = i;
break;
}
}
curThreads[tIndex] = new Thread(new ThreadStart(() => RunAction(a, tIndex)));
curThreads[tIndex].Start();
//ThreadPool.QueueUserWorkItem(RunAction, a);
return curThreads[tIndex];
}
private static void RunAction(object action, int tIndex)
{
try
{
((Action)action)();
}
catch (ThreadInterruptedException e)
{
Debug.Log("newThread cannot go to sleep - " +
"interrupted by main thread.");
}
finally
{
// if(curThreads[tIndex]!=null)
// {
// curThreads[tIndex].Abort();
// curThreads[tIndex].Join();
// curThreads[tIndex]=null;
// }
Interlocked.Decrement(ref numThreads);
//curThreads.Remove(thread1);
}
}
void OnDisable()
{
// CommonInfo.ifClosing = true;
//CloseThread();
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start()
{
}
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
void Update()
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
foreach (var a in _currentActions)
{
a();
}
lock (_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
foreach (var item in _currentDelayed)
_delayed.Remove(item);
}
foreach (var delayed in _currentDelayed)
{
delayed.action();
}
}
public static void CloseThread()
{
for (int i = 0; i < curThreads.Length; i++)
{
if (curThreads[i] != null && curThreads[i].IsAlive)
{
//curThreads[i].Interrupt();
curThreads[i].Abort();
curThreads[i].Join();
}
}
}
}
5.WindowsForms版本Socket
winform版本可以做一个简单的服务器后台运行处理数据,与Unity能够连接相互通信。
6.WindowsForms项目源文件
https://download.csdn.net/download/ForKnowledgeMe/89569929?spm=1001.2014.3001.5503
如果对您能够起到帮助的话,我们达成一个君子协定,希望大家能够多多支持,点点关注,十分感谢!
总结
以上就是所有的内容,具体的使用方式和方法作用都注释出来了,有使用错误或者不对的地方还请大家多多指正。