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);

原文地址:https://www.cnblogs.com/JinT-Hwang/p/10114027.html