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";
    #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
如果对您能够起到帮助的话,我们达成一个君子协定,希望大家能够多多支持,点点关注,十分感谢!

开启服务
连接成功

总结

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

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Unity编写Socket通信,您可以使用C#中的Socket类。以下是一个简单的示例代码,用于在Unity中建立TCP连接并发送和接收数据: ```csharp using System; using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine; public class SocketClient : MonoBehaviour { private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); private byte[] _receiveBuffer = new byte[1024]; private void Start() { _clientSocket.BeginConnect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080), ConnectCallback, null); } private void ConnectCallback(IAsyncResult ar) { _clientSocket.EndConnect(ar); Debug.Log("Connected to server!"); // Send test message SendData("Hello from Unity!"); // Start receiving data _clientSocket.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, SocketFlags.None, ReceiveCallback, null); } private void ReceiveCallback(IAsyncResult ar) { int bytesRead = _clientSocket.EndReceive(ar); if (bytesRead > 0) { string receivedData = Encoding.ASCII.GetString(_receiveBuffer, 0, bytesRead); Debug.Log("Received data: " + receivedData); // Continue receiving data _clientSocket.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, SocketFlags.None, ReceiveCallback, null); } else { Debug.Log("Disconnected from server!"); } } private void SendData(string data) { byte[] dataBytes = Encoding.ASCII.GetBytes(data); _clientSocket.BeginSend(dataBytes, 0, dataBytes.Length, SocketFlags.None, SendCallback, null); } private void SendCallback(IAsyncResult ar) { _clientSocket.EndSend(ar); Debug.Log("Data sent to server!"); } private void OnDestroy() { _clientSocket.Close(); } } ``` 在上面的代码中,我们首先创建了一个名为“SocketClient”的MonoBehaviour类,该类使用Socket类进行TCP通信。在Start方法中,我们开始连接到服务器,并在连接成功后发送一条测试消息。然后,我们开始接收来自服务器的数据,并将其输出到Unity控制台。最后,在OnDestroy方法中,我们关闭了客户套接字。 请注意,上述代码中的IP地址和口号应该替换为您自己的服务器地址和口号。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值