美文网首页网络编程Unity知识归纳
Unity中的聊天功能(异步Socket)

Unity中的聊天功能(异步Socket)

作者: IongX | 来源:发表于2018-01-18 11:36 被阅读52次

    实现聊天功能分为两部分:服务端,客户端。话不多说,上代码。

    1. 服务端 (用VS写的控制台程序),主要实现异步通信,及连接池

    1.1 ConnectClient (客户端连接类)
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Net;
    using System.Net.Sockets;
    
    namespace TestSocketServer
    {
        /// <summary>
        /// 客户端连接对象类
        /// </summary>
        class ConnectClient
        {
            //缓冲区大小+
            public const int BUFFER_SIZE = 1024;
            public Socket socket;
            //是否使用
            public bool isUse = false; 
            //缓冲区
            public byte[] readBuff = new byte[BUFFER_SIZE];
            //数据大小
            public int bufferCount;
    
            //构造
            public ConnectClient()
            {
                readBuff = new byte[BUFFER_SIZE];
            }
    
            /// <summary>
            /// 初始化数据
            /// </summary>
            /// <param name="soc"></param>
            public void Init(Socket soc)
            {
                this.socket = soc;
                isUse = true;
                bufferCount = 0;
            }
            
            /// <summary>
            /// 缓冲区剩余字节空间
            /// </summary>
            /// <returns></returns>
            public int BufferRemain()
            {
                return BUFFER_SIZE - bufferCount;
            }
    
            /// <summary>
            /// 获取socket连接地址
            /// </summary>
            /// <returns></returns>
            public string Address()
            {
                if (!isUse)
                {
                    return null;
                }
                else
                {
                    return socket.RemoteEndPoint.ToString();
                }
    
            }
    
            /// <summary>
            /// 关闭连接
            /// </summary>
            public void Close()
            {
                if (!isUse)
                {
                    return;
                }
                else
                {
                    Console.WriteLine(Address() + " [ 断开连接 ]");
                    socket.Close();
                    isUse = false;
                }
            }
        }
    }
    
    
    1.2 SocketServer (服务端控制类)
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Net;
    using System.Net.Sockets;
    
    namespace TestSocketServer
    {
        /// <summary>
        /// 服务器类
        /// </summary>
        class SocketServer
        {
            //监听socket对象
            public Socket listenSocket;
            //客户端连接池
            public ConnectClient[] clientList;
            //容纳客户端数量
            public int maxClient = 50;
    
            /// <summary>
            /// 从连接池中 获取客户端连接对象 ,如果列表中以满 则获取失败
            /// </summary>
            /// <returns></returns>
            public int GetIndex()
            {
                //如果连接池为空 则新建连接池 返回第一个连接对象
                if (clientList == null)
                {
                    clientList = new ConnectClient[maxClient];
                    clientList[0] = new ConnectClient();
                    return 0;
                }
                else
                {
                    //遍历连接池 , 返回未使用连接对象的索引
                    for (int i = 0; i < clientList.Length; i++)
                    {
                        if (clientList[i] == null)
                        {
                            clientList[i] = new ConnectClient();
                            return i;
                        }
                        else if (clientList[i].isUse == false)
                        {
                            return i;
                        }
                    }
                    //如果都有对象且在使用中,则返回-1. 代表获取失败
                    return -1;
                }
            }
    
            //接收回调
            private void AcceptCallBack(IAsyncResult ar)
            {
                try
                {
                    Socket socket = listenSocket.EndAccept(ar);
                    int index = GetIndex();
                    if (index< 0)
                    {
                        socket.Close();
                        Console.WriteLine("服务器连接已满,请稍候再试");
                    }
                    else
                    {
                        ConnectClient client = clientList[index];
                        client.Init(socket);
                        client.socket.BeginReceive(client.readBuff, client.bufferCount, client.BufferRemain(), SocketFlags.None, ReceiveCallBack,client);
                        Console.WriteLine("客户端连接成功 = " + client.Address());
                    }
                    //重新开始异步接收请求
                    listenSocket.BeginAccept(AcceptCallBack, null);
                }
                catch (Exception e)
                {
                    Console.WriteLine("客户端请求异常! Exception = " + e.Message);
                }
    
            }
            //返回回调
            private void ReceiveCallBack(IAsyncResult ar)
            {
                ConnectClient client = (ConnectClient)ar.AsyncState;
                try
                {
                    int count = client.socket.EndReceive(ar);
                    //断开连接
                    if (count <= 0)
                    {
                        Console.WriteLine("断开连接  = " + client.Address());
                        client.Close();
                    }
                    else
                    {
                        string receiveString = System.Text.Encoding.UTF8.GetString(client.readBuff, 0, count);
                        Console.WriteLine("接收 " + client.Address() + "    的数据 =  " + receiveString);
                        byte[] sendBytes = System.Text.Encoding.UTF8.GetBytes(client.Address() + " :   " + receiveString);
    
                        //广播信息
                        for (int i = 0; i < clientList.Length; i++)
                        {
                            if (clientList[i] == null)
                            {
                                continue;
                            }
    
                            if (clientList[i].isUse == false)
                            {
                                continue;
                            }
                            clientList[i].socket.Send(sendBytes);
                            Console.WriteLine("广播 " + client.Address() + " 的数据 给 " + clientList[i].Address());
                        }
                    }
                    //继续接收数据
                    client.socket.BeginReceive(client.readBuff, client.bufferCount, client.BufferRemain(), SocketFlags.None, ReceiveCallBack, client);
                }
                catch (Exception e)
                {
                    Console.WriteLine("[接收数据异常]  client = " + client.Address());
                    Console.WriteLine(" Execption = " + e.Message);
                    client.Close();
                }
            }
    
            /// <summary>
            /// 开启服务
            /// </summary>
            /// <param name="host">主机地址</param>
            /// <param name="port">端口</param>
            /// <param name="maxClient">容纳客户端数量 (默认50)</param>
            public void Start(string host , int port , int maxClient = 50)
            {
                //初始化连接池
                this.maxClient = maxClient;
                clientList = new ConnectClient[this.maxClient];
    
                listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress ipa = IPAddress.Parse(host);
                IPEndPoint ipe = new IPEndPoint(ipa, port);
                listenSocket.Bind(ipe);
                listenSocket.Listen(maxClient);
                //开启异步接收连接
                listenSocket.BeginAccept(AcceptCallBack, null);
    
                Console.WriteLine("服务器启动成功!");
            }
        }
    }
    
    

    启动服务端:

     static void Main(string[] args)
            {
                Console.WriteLine("请输入服务端IP地址:");
                string host = Console.ReadLine();
                Console.WriteLine("请输入服务端端口号:");
                string port = Console.ReadLine();
    
                SocketServer server = new SocketServer();
                //只是本机测试,可以写127.0.0.1 , 但是要让其他机器连接的话,要写实际ip地址
                //server.Start("192.168.0.171", 1234);
                server.Start(host, int.Parse(port));
                while (true)
                {
                    string write = Console.ReadLine();
                    switch (write)
                    {
                        case "quit":
                            return;
                    }
                }
            }
    
    

    2. 客户端,聊天Demo 及Socket通信。

    场景界面如下:


    图片.png

    功能控制脚本:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Net.Sockets;
    using System.Net;
    using UnityEngine.UI;
    using System;
    
    public class TestClient : MonoBehaviour {
    
        private Socket m_Socket;
    
        public InputField HostField;
        public InputField PortField;
        public InputField MessageField;
        public InputField LinkMessageField;
        public InputField ReceiveFiled;
        private byte[] readBuff = new byte[1024];
    
        private string reveString; //接收的字符数据
        private bool isReceived = false; //数据接收完成
        // Use this for initialization
        void Start ()
        {
            //设置后台运行,数据就会立马同步更新。否则其他客户端发送一条消息,服务端进行广播,但是Unity进程被挂起了,就无法实时更新
            Application.runInBackground = true;
        }
        
        public void LinkServer()
        {      
            m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                m_Socket.Connect(HostField.text, int.Parse(PortField.text));
                LinkMessageField.text = "连接成功-----" + m_Socket.LocalEndPoint.ToString();
            }
            catch (Exception)
            {
                LinkMessageField.text = "连接失败!!";
                throw;
            }
            
            m_Socket.BeginReceive(readBuff, 0, 1024, SocketFlags.None, ReceiveCallBack, null);
        }
    
    
        public void SendMessageToServer()
        {
            try
            {
                byte[] sendBytes = System.Text.Encoding.UTF8.GetBytes(MessageField.text);
                m_Socket.Send(sendBytes);
            }
            catch (Exception)
            {
                throw;
            }
    
        }
    
        //服务器返回回调
        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                int count = m_Socket.EndReceive(ar);
                reveString = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
                isReceived = true;
                //之所以不直接在这里赋值,是因为线程问题,会报错,该回调不是在unity主线程中执行的,所以赋值放在update中
                //if (ReceiveFiled.text.Length > 500)
                //{
                //    ReceiveFiled.text = "";
                //}
                //ReceiveFiled.text += reveString + '\n';
                //继续接收返回信息
                m_Socket.BeginReceive(readBuff, 0, 1024, SocketFlags.None, ReceiveCallBack, null);
            }
            catch (Exception)
            {
                reveString = m_Socket.LocalEndPoint.ToString() + "连接断开";
                isReceived = true;
                m_Socket.Close();
                throw;
            }
    
        }
    
        private void Update()
        {
            if (isReceived)
            {
                if (ReceiveFiled.text.Length > 500)
                {
                    ReceiveFiled.text = "";
                }
                ReceiveFiled.text += reveString + '\n';
                isReceived = false;
            }
        }
    
    }
    
    

    运行效果如下:


    图片.png

    最后:

    以上纯属个人总结,如有不对或者更好的方法,欢迎指正,交流。
    工程文件链接 : https://github.com/IongX/Unity_Socket

    相关文章

      网友评论

        本文标题:Unity中的聊天功能(异步Socket)

        本文链接:https://www.haomeiwen.com/subject/bpbioxtx.html