unity下tcp協議socket異步通訊,服務端和客戶端代碼實現

unity下tcp協議socket異步通訊,服務端和客戶端代碼實現api

.Net API Socket類異步

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

 

服務端部分:tcp

服務端的流程是:綁定IP和端口-》監聽-》接收鏈接,有客戶端連上後,服務端對該客戶端進行接收信息、發送信息操做。函數

下面開始服務端代碼的實現:this

先定義一個端口號,和一個Socket(服務端)。spa

 

    public int port = 9090;

    private Socket serverSocket;

  

下面定義一個函數,初始化Socket。 .net

    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.2code

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

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

相關文章
相關標籤/搜索