Unity C#编写Socket服务器客户端功能代码及源文件


前言

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";
    bool IsConnect = false;
    #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
        {
            IsConnect = false;
			Debug.Log("-----尝试连接----" + ifRight);
			clientTcp.Connect(ipep);
			ifRight = true;
			IsConnect=true;
			Debug.Log("-----连接成功----" + ifRight);
        }
        catch (SocketException ex)
        {
        	 if (IsConnect) 
 			 {
     			return;
 			 }
            Debug.Log("--阻塞后连接失败失败失败----" + ifRight);
            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();


            }
        }
    }
}

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Drawing.Drawing2D;

namespace PreviewDemo.Scripts
{
    internal class TcpClientConnect : SingleTon<TcpClientConnect>
    {
        public delegate void ReceiveServerInfo(string serverinfo);
        public event ReceiveServerInfo ReceiveServerInfoEvent;


        string serverIP = "";
        string serverPort = "";

        bool timerEnable = false;
        bool ifRight = false;
        bool ifConnecting = false;//是否正在连接

        Thread clientThread;
        TcpClient clientTcp;
        NetworkStream ns;
        StreamReader sr;
        StreamWriter sw;
        List<string> sendMessageList = new List<string>();
        public void TcpClientConnect_Load()
        {
            this.serverIP = "127.0.0.1";
            // this.serverIP = "192.168.13.122";
            //this.serverPort = "6000";

            Connect();
            timerEnable = true;
        }
        public void Connect()
        {
         //   ifConnecting = true;//正在连接

            clientThread = new Thread(new ThreadStart(ConnectToServer));
            clientThread.Start();
        }
        private void ConnectToServer()
        {

            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(serverIP), 6000);
            clientTcp = new TcpClient();

            //将套接字与远程服务器地址相连
            try
            {
                clientTcp.Connect(ipep);
              //  ifConnecting = false;
                ifRight = true;


                ns = clientTcp.GetStream();
                sr = new StreamReader(ns);
                sw = new StreamWriter(ns);

                SendToServer("WinformExe#Init#E\r\n");
                //CommonInfo.dicParas.Clear();
            }
            catch (SocketException ex)
            {
                ifRight = false;
              //  CanData.isSend = false;
              //  ifConnecting = false;
                return;
            }
            catch (StackOverflowException ex)
            {
                //堆栈溢出
            }
            catch (Exception ex)
            {

            }
            //while (sendMessageList.Count > 0)
            //{
            //    sw.WriteLine(sendMessageList[0]);
            //    sw.Flush();
            //    sendMessageList.RemoveAt(0);
            //    if (sendMessageList.Count > 0)
            //        Thread.Sleep(100);
            //}
            string data = string.Empty;
            while (ifRight)
            {
                //接收服务器信息
                try
                {
                    
                        data = sr.ReadLine();
                        if (data!=null&&data.Length > 0)
                        {
                            Console.WriteLine("接收的数据是:" + data);
                            string[] dataStr = Regex.Split(data, "\r\n");
                        for (int i = 0; i < dataStr.Length; i++)
                        {
                            if (dataStr[i].Length > 0)//Server#V1!0,V2!1#E
                            {
                                string[] getData = dataStr[i].Split('#');
                                if (getData.Length == 3 && getData[0].Equals("Server") && getData[2].Equals("E"))
                                {
                                    ReceiveServerInfoEvent?.Invoke(getData[1]);
                                }

                            }

                        }
                    }//线程锁

                }
                catch (Exception ex)
                {
                    ifRight = false;
                    ifConnecting = false;
                    return;
                }
            }
            ifRight = false;
        }


        public void TcpClientConnect_Tick()
        {
            if (!timerEnable)
            {
                return;
            }

            if ((!ifRight || !clientTcp.Connected) && !ifConnecting)
            {
                this.serverIP = "127.0.0.1"; 
                Connect();
            }
        }

        public void SendToServer(string message)
        {

            sendMessageList.Add(message);
            if (sw == null)
                return;

            Console.WriteLine("发送的数据是:" + message);
            //向服务器发送信息
            try
            {
                while (sendMessageList.Count > 0)
                {
                    sw.WriteLine(sendMessageList[0]);
                    sw.Flush();
                    sendMessageList.RemoveAt(0);
                    if (sendMessageList.Count > 0)
                        Thread.Sleep(100);
                }
            }
            catch (Exception ex)
            {
                ifRight = false;
                sw.Close();
                sw = null;
            }
        }
    }
}

5.WindowsForms版本Socket

winform版本可以做一个简单的服务器后台运行处理数据,与Unity能够连接相互通信。

6.WindowsForms项目源文件

https://download.youkuaiyun.com/download/ForKnowledgeMe/89569929?spm=1001.2014.3001.5503
如果对您能够起到帮助的话,希望大家能够多多支持,点点关注,十分感谢!

开启服务
连接成功

总结

以上就是所有的内容,具体的使用方式和方法作用都注释出来了,有使用错误或者不对的地方还请大家多多指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰尼杰尼丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值