Unity中Socket_TCP异步连接,加入断线检测以及重连功能(服务端客户端源码)

发布于:2024-10-09 ⋅ 阅读:(22) ⋅ 点赞:(0)

1、服务端

using System;
using System.Collections.Generic;
using System.Text;
#region 命名空间
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
#endregion

namespace AsynServerConsole
{
    /// <summary>
    /// Tcp协议异步通讯类(服务器端)
    /// </summary>
    public class AsynTcpServer: MonoBehaviour
    {

        //存储已连接的客户端的泛型集合
        private static Dictionary<string, Socket> socketList = new Dictionary<string, Socket>();

        private void Start()
        {
            StartListening();
        }
        float ttt;

        private void Update()
        {
            ttt += Time.deltaTime;
            if (ttt>5f)
            {
                ttt = 0f;
                 //Debug.Log("连接数量==="+ socketList.Count);

                if (socketList.Count > 0)
                {
                    foreach (var item in socketList)
                    {
                      //  Debug.Log("连接客户端====" + item.Key);
                        //  HandleClient(item.Value);
                        //AsynSend(item.Value,"心跳");
                    }
                }
                else 
                {
                
                }
               
            }
        }
        Socket TcpServer;
        #region Tcp协议异步监听
        /// <summary>
        /// Tcp协议异步监听
        /// </summary>
        public void StartListening()
        {
            //主机IP
            IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("192.168.15.72"), 8686);
            TcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            TcpServer.Bind(serverIp);
            TcpServer.Listen(100);
            Debug.Log("异步开启监听...");
            AsynAccept(TcpServer);
        }
        #endregion

        #region 异步连接客户端
        /// <summary>
        /// 异步连接客户端
        /// </summary>
        /// <param name="tcpServer"></param>
        public void AsynAccept(Socket tcpServer)
        {
            tcpServer.BeginAccept(asyncResult =>
            {
                Socket tcpClient = tcpServer.EndAccept(asyncResult);

               string str = tcpClient.RemoteEndPoint.ToString();
                socketList.Add(str, tcpClient);

                Debug.Log("收到连接=============" + tcpClient.RemoteEndPoint.ToString());
                AsynSend(tcpClient, "收到客户端连接...");//发送消息
                AsynAccept(tcpServer);//继续监听客户端
                AsynRecive(tcpClient);//继续监听客户端消息
            }, null);
        }
        #endregion

        #region 异步接受客户端消息
        /// <summary>
        /// 异步接受客户端消息
        /// </summary>
        /// <param name="tcpClient"></param>
        public void AsynRecive(Socket tcpClient)
        {
            byte[] data = new byte[1024];
            try
            {
                tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None,
                asyncResult =>
                {
                    int length = tcpClient.EndReceive(asyncResult);
                    if (length==0)
                    {
                        Debug.Log("客户端退出===="+tcpClient.RemoteEndPoint.ToString());
                        foreach (var item in socketList)
                        {
                            if (item.Key.Contains(tcpClient.RemoteEndPoint.ToString()))
                            {
                                socketList.Remove(item.Key);
                            }
                        }
                        return;
                    }
                    Debug.Log(tcpClient.RemoteEndPoint.ToString()+"收到客户端消息:{length}=====" + length + "___" + Encoding.UTF8.GetString(data)); 
                     AsynSend(tcpClient, "收到客户端消息...");
                    AsynRecive(tcpClient);
                }, null);
            }
            catch (Exception ex)
            {
                Debug.Log("异常信息:" + ex.Message);
            }
        }
        #endregion

        #region 异步发送消息
        /// <summary>
        /// 异步发送消息
        /// </summary>
        /// <param name="tcpClient">客户端套接字</param>
        /// <param name="message">发送消息</param>
        public void AsynSend(Socket tcpClient, string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            try
            {
                tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
                {
                    //完成发送消息
                    int length = tcpClient.EndSend(asyncResult);
                    Debug.Log("服务端完成发送消息=====" + length+"___" +message);
                }, null);
            }
            catch (Exception ex)
            {
                Debug.Log("发送消息异常信息=====" + ex.Message);
            }
        }
        #endregion


        private void HandleClient(Socket clientSocket)
        {
            byte[] buffer = new byte[1024];
            try
            {
                int bytesRead = clientSocket.Receive(buffer);
                if (bytesRead > 0)
                {
                    // 处理接收到的数据
                    Debug.Log("Received: {0}" + Encoding.ASCII.GetString(buffer, 0, bytesRead));
                }
                else
                {
                    // 客户端正常关闭连接
                    Debug.Log("Client closed the connection.");
                }
            }
            catch (SocketException se)
            {
                // 处理客户端断开连接的情况
                Debug.Log($"Socket error: {se.Message}");
            }
            finally
            {
                // 关闭Socket
                clientSocket.Close();
            }
        }

        private void OnDestroy()
        {
            Debug.Log(socketList.Count);
            foreach (KeyValuePair<string, Socket> item in socketList)
            {
                item.Value.Dispose();
                item.Value.Close();
            }
            if (TcpServer.Connected)
            {
                TcpServer.Shutdown(SocketShutdown.Both);
            }
           
            TcpServer.Dispose();
            TcpServer.Close();
        }


        [ContextMenu("客户端数量")]
        public void DebugClientCount() 
        {
            Debug.Log("连接数量===" + socketList.Count);
        }
    }
}

2、客户端

using System;
using System.Collections.Generic;
using System.Text;
#region 命名空间
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
using System.Net.Http;
#endregion

namespace AsynClientConsole
{
    /// <summary>
    /// Tcp协议异步通讯类(客户端)
    /// </summary>
    public class AsynTcpClient : MonoBehaviour
    {
        public bool GetConnect
        {
            get
            {
                return (TcpClient != null && IsSocketConnected(TcpClient));
            }
        }
        private void Start()
        {
            AsynConnect();


        }
        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.A))
            {
                AsynSend(TcpClient, "我上线了");
            }
        }
        Socket TcpClient;
        IPEndPoint serverIp;
        #region 异步连接
        /// <summary>
        /// Tcp协议异步连接服务器
        /// </summary>
        public void AsynConnect()
        {
            //主机IP
            serverIp = new IPEndPoint(IPAddress.Parse("192.168.15.72"), 8686);
            TcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //TcpClient.BeginConnect(serverIp, asyncResult =>
            //{
            //    if (!TcpClient.Connected)
            //    {
            //        Debug.Log("服务器未开启,尝试重连。。。。。。");

            //    }
            //    else
            //    {
            //        TcpClient.EndConnect(asyncResult);
            //        Debug.Log($"client-->-->{serverIp.ToString()}");
            //        AsynSend(TcpClient, "我上线了");
            //        // AsynSend(tcpClient, "第一次发送消息");
            //        //  AsynSend(tcpClient, "第二次发送消息");
            //        AsynRecive(TcpClient);
            //    }
            //}, null);


            TcpClient.BeginConnect(serverIp, ConnectAsyncResult, null);
        }

        private void ConnectAsyncResult(IAsyncResult ar)
        {
            if (!TcpClient.Connected)
            {
                Debug.Log("服务器未开启,尝试重连。。。。。。");
                TcpClient.BeginConnect(serverIp, ConnectAsyncResult, null);
            }
            else
            {
                TcpClient.EndConnect(ar);
                Debug.Log($"client-->-->{serverIp.ToString()}");
                AsynSend(TcpClient, "我上线了");
                // AsynSend(tcpClient, "第一次发送消息");
                //  AsynSend(tcpClient, "第二次发送消息");
                AsynRecive(TcpClient);
            }
        }
        #endregion





        /// <summary>
        /// 查询客户端是否仍然连接
        /// </summary>

        private static bool IsSocketConnected(Socket socket)
        {
            if (!socket.Connected)
                return false;

            // 使用 Poll 方法检查连接状态
            bool isReadable = socket.Poll(1000, SelectMode.SelectRead);
            bool isDisconnected = (socket.Available == 0 && isReadable);
            return !isDisconnected;
        }

        #region 异步接受消息
        /// <summary>
        /// 异步连接客户端回调函数
        /// </summary>
        /// <param name="tcpClient"></param>
        public void AsynRecive(Socket tcpClient)
        {
            //   TcpClient tt;

            byte[] data = new byte[1024];
            tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None, asyncResult =>
            {
                int length = tcpClient.EndReceive(asyncResult);

                Debug.Log($"client<--<--server--长度=====:{length}");
                Debug.Log($"是否连接=====:{IsSocketConnected(tcpClient)}");

                if (length <= 0 && !IsSocketConnected(tcpClient))
                {
                    tcpClient?.Close();
                    Debug.Log(TcpClient == null);
                    Debug.Log(TcpClient);
                    //  TcpClient.BeginConnect(serverIp, ConnectAsyncResult, null);
                    AsynConnect();
                    return;
                }

                Debug.Log($"client<--<--server:{length + "____" + Encoding.UTF8.GetString(data)}");


                AsynRecive(tcpClient);
            }, null);
        }
        #endregion

        #region 异步发送消息
        /// <summary>
        /// 异步发送消息
        /// </summary>
        /// <param name="tcpClient">客户端套接字</param>
        /// <param name="message">发送消息</param>
        public void AsynSend(Socket tcpClient, string message)
        {
            if (!GetConnect)
            {
                Debug.Log("------未连接-----");
                return;
            }
            byte[] data = Encoding.UTF8.GetBytes(message);
            tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
            {
                //完成发送消息
                int length = tcpClient.EndSend(asyncResult);
                Debug.Log($"client-->-->server:{message}");
            }, null);
        }
        #endregion

        [ContextMenu("是否连接")]
        public void DebugClientCount()
        {
            Debug.Log($"是否连接=====:{IsSocketConnected(TcpClient)}");
        }
        private void OnApplicationQuit()
        {
            TcpClient.Close();
        }
    }
}