zoukankan      html  css  js  c++  java
  • unity下tcp协议socket异步通信,服务端和客户端代码实现

    unity下tcp协议socket异步通信,服务端和客户端代码实现

    .Net API Socket类

    https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.socket?view=netframework-4.7.2

     

    服务端部分:

    服务端的流程是:绑定IP和端口-》监听-》接收连接,有客户端连上后,服务端对该客户端进行接收信息、发送信息操作。

    下面开始服务端代码的实现:

    先定义一个端口号,和一个Socket(服务端)。

     

        public int port = 9090;
    
        private Socket serverSocket;

      

    下面定义一个函数,初始化Socket。 

        private void Init()
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPHostEntry iPHostEntry = Dns.GetHostEntry("localhost");
            IPEndPoint iPEndPoint = new IPEndPoint(iPHostEntry.AddressList[1], port);
    
            serverSocket.Bind(iPEndPoint);
            serverSocket.Listen(5);
        }

     

    其中AddressFamily的字段含义,可参考.NET的API https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.addressfamily?view=netframework-4.7.2

    其中SocketType的字段含义,可参考.NET的API https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.sockettype?view=netframework-4.7.2

    其中ProtocolType的字段含义,可参考.NET的API https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.protocoltype?view=netframework-4.7.2

    Listen的参数,数字表示接受连接的上限数量。

    之后就可以进行接收操作了,接收连接的函数如下。 

        private void Accept()
        {
            try
            {
                serverSocket.BeginAccept(new AsyncCallback(Accept_Callback), serverSocket);
            }
            catch (Exception e)
            {
                print(e.Message);
            }
        }

     这是一个异步的接收,其中的Accept_Callback是一个委托,下面会写到,理解起来就是当有客户端连接到服务端时,会自动调用Accept_Callback函数。

      

    实现Accept_Callback的代码之前,先定义一个类,后面传递参数时需要用到,后面再进行说明。

    public class StateObject
    {
        public byte[] buffer;
        public Socket socket;
    
        public StateObject(int size, Socket socket)
        {
            buffer = new byte[size];
            this.socket = socket;
        }
    }

      

    定义可接收的数据的大小,以及一个用来储存客户端Socket的List。

        private const int BUFFER_SIZE = 128;
        private List<Socket> clientSockets;

    Accept_Callback函数实现如下。

        private void Accept_Callback(IAsyncResult ar)
        {
            Socket socket = serverSocket.EndAccept(ar);
    
            print(socket.LocalEndPoint.ToString() + " Connected");
    
            if (!clientSockets.Contains(socket))
            {
                clientSockets.Add(socket);
    
                StateObject stateObject = new StateObject(BUFFER_SIZE, socket);
    
                try
                {
                    socket.BeginReceive(stateObject.buffer, 0, BUFFER_SIZE, 0, new AsyncCallback(Receive_Callback), stateObject);
                }
                catch (Exception e)
                {
                    print(e.Message);
                }
            }
    
            Accept();
        }

    每接收到一个连接后,进行EndAccept,以获取该客户端的socket。

    BeginReceive的参数定义可查看API文档,这个地方的参数看文档都能理解,就是最后一个参数要注意,这个地方传递,会在传递给下文Receive_Callback中,这个地方传送的都会被转换为Object,然后在回调函数中可以通过T t=(T)ar.AsyncState这样的形式转换回来。

    下面是异步接收的回调函数。

        private void Receive_Callback(IAsyncResult ar)
        {
            StateObject stateObject = (StateObject)ar.AsyncState;
    
            int read = stateObject.socket.EndReceive(ar);
    
            if (read > 0)
            {
                print(Encoding.ASCII.GetString(stateObject.buffer));
    
                try
                {
                    stateObject.socket.BeginReceive(stateObject.buffer, 0, BUFFER_SIZE, 0, new AsyncCallback(Receive_Callback), stateObject);
                }
                catch (Exception e)
                {
                    print(e.Message);
                }
            }
        }

    这个地方要记得EndReceive,不然会接收到很多空字符串。

    下面是发送给所有客户端的函数,用了同步,如果要使用异步,使用BeginSend即可,用法和上面类似。

     public void Send(string message)
        {
            byte[] msg = Encoding.ASCII.GetBytes(message);
            foreach (var item in clientSockets)
            {
                if (item.Connected)
                {
                    try
                    {
                        item.Send(msg, msg.Length, 0);
                    }
                    catch (Exception e)
                    {
                        print(e.Message);
                    }
                }
            }
        }

    最后要记得要关闭Socket,不然即使你unity不运行了,那个socket还在后面运行。

        private void OnDisable()
        {
            if (serverSocket.Connected)
            {
                try
                {
                    serverSocket.Shutdown(SocketShutdown.Both);
                    serverSocket.Close();
                }
                catch (Exception e)
                {
                    print(e.Message);
                }
            }
        }

    下面是服务端的完整代码。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using UnityEngine;
    
    public class StateObject
    {
        public byte[] buffer;
        public Socket socket;
    
        public StateObject(int size, Socket socket)
        {
            buffer = new byte[size];
            this.socket = socket;
        }
    }
    
    public class AsynchronousSocket : MonoBehaviour
    {
        private static AsynchronousSocket _singleton;
        public static AsynchronousSocket Singleton
        {
            get
            {
                if (_singleton == null)
                {
                    _singleton = FindObjectOfType<AsynchronousSocket>();
                }
                return _singleton;
            }
        }
    
        private const int BUFFER_SIZE = 128;
    
        public int port = 9090;
    
        private Socket serverSocket;
    
        private List<Socket> clientSockets;
    
        void Start()
        {
            clientSockets = new List<Socket>();
    
            Init();
            Accept();
        }
    
        private void Init()
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPHostEntry iPHostEntry = Dns.GetHostEntry("localhost");
            IPEndPoint iPEndPoint = new IPEndPoint(iPHostEntry.AddressList[1], port);
    
            serverSocket.Bind(iPEndPoint);
            serverSocket.Listen(5);
        }
    
        private void Accept()
        {
            try
            {
                serverSocket.BeginAccept(new AsyncCallback(Accept_Callback), serverSocket);
            }
            catch (Exception e)
            {
                print(e.Message);
            }
        }
    
        private void Accept_Callback(IAsyncResult ar)
        {
            Socket socket = serverSocket.EndAccept(ar);
    
            print(socket.LocalEndPoint.ToString() + " Connected");
    
            if (!clientSockets.Contains(socket))
            {
                clientSockets.Add(socket);
    
                StateObject stateObject = new StateObject(BUFFER_SIZE, socket);
    
                try
                {
                    socket.BeginReceive(stateObject.buffer, 0, BUFFER_SIZE, 0, new AsyncCallback(Receive_Callback), stateObject);
                }
                catch (Exception e)
                {
                    print(e.Message);
                }
            }
    
            Accept();
        }
    
        private void Receive_Callback(IAsyncResult ar)
        {
            StateObject stateObject = (StateObject)ar.AsyncState;
    
            int read = stateObject.socket.EndReceive(ar);
    
            if (read > 0)
            {
                print(Encoding.ASCII.GetString(stateObject.buffer));
    
                try
                {
                    stateObject.socket.BeginReceive(stateObject.buffer, 0, BUFFER_SIZE, 0, new AsyncCallback(Receive_Callback), stateObject);
                }
                catch (Exception e)
                {
                    print(e.Message);
                }
            }
        }
    
        public void Send(string message)
        {
            byte[] msg = Encoding.ASCII.GetBytes(message);
            foreach (var item in clientSockets)
            {
                if (item.Connected)
                {
                    try
                    {
                        item.Send(msg, msg.Length, 0);
                    }
                    catch (Exception e)
                    {
                        print(e.Message);
                    }
                }
            }
        }
    
        private void OnDisable()
        {
            if (serverSocket.Connected)
            {
                try
                {
                    serverSocket.Shutdown(SocketShutdown.Both);
                    serverSocket.Close();
                }
                catch (Exception e)
                {
                    print(e.Message);
                }
            }
        }
    }

    客户端部分:

    下面是客户端的完整代码。

    using System;
    using System.IO;
    using System.Net.Sockets;
    using System.Text;
    using UnityEngine;
    
    public class SocketBehaviour : MonoBehaviour
    {
        private static SocketBehaviour _singleton;
        public static SocketBehaviour Singleton
        {
            get
            {
                if (_singleton == null)
                {
                    _singleton = FindObjectOfType<SocketBehaviour>();
                }
                return _singleton;
            }
        }
        private const int BUFFER_SIZE = 128;
    
        public string host = "127.0.0.1";
        public int port = 9090;
    
        private byte[] buffer;
    
        private Socket socket;
        // Use this for initialization
        void Start()
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
            Connect();
        }
    
        private void Connect()
        {
            try
            {
                socket.Connect(host, port);
            }
            catch (Exception e)
            {
                print(e.Message);
            }
    
            if (socket.Connected)
            {
                print("Connected");
                Receive();
            }
            else
            {
                print("Connect fail");
            }
        }
    
        private void Receive()
        {
            if (!socket.Connected)
                return;
    
            buffer = new byte[BUFFER_SIZE];
    
            try
            {
                socket.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, new AsyncCallback(Receive_Callback), socket);
            }
            catch (Exception e)
            {
                print(e.Message);
            }
        }
    
        private void Receive_Callback(IAsyncResult ar)
        {
            if (!socket.Connected)
            {
                return;
            }
    
            int read = socket.EndReceive(ar);
    
            if (read > 0)
            {
                print(Encoding.UTF8.GetString(buffer));
    
                Receive();
            }
        }
    
        public void Send(string message)
        {
            if (!socket.Connected)
                return;
    
            byte[] msg = Encoding.ASCII.GetBytes(message);
            socket.Send(msg);
        }
    
        private void OnDisable()
        {
            if (socket.Connected)
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
        }
    }

    客户端比较简单就不解释了,这里的连接和发送都用的同步,用异步的话,参考API文档,还是比较好实现的。

    欢迎交流,转载注明出处:)

     

    public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socket_flags, AsyncCallback callback, object state);

  • 相关阅读:
    python---自定义分页类
    python---正则中的(?P<name>group)
    学习windows编程 day6 之模拟记事本
    学习windows编程 day5 之按键消息
    some websit
    android/iphone/windows/linux声波通讯库
    无线点餐系统
    android实现弧形进度表盘效果
    与Sevice实现双向通信
    android code bbs for developer
  • 原文地址:https://www.cnblogs.com/JinT-Hwang/p/10114027.html
Copyright © 2011-2022 走看看