天天看點

利用Unity實作一個簡單的TCP通訊工具

在這樣的一個簡單的通訊工具中,首先要實作的是兩個子產品:

  • 伺服器端(server)
  • 用戶端(client)

其中server端,利用VS寫一個控制台程式來實作。

client端則利用Unity3D軟體來實作。

使用的語言都是C#。

1, Sever

Server端主要有兩個類,一個Program類為主類,Client類不是用戶端,是專門處理使用者端的發送資訊的類。

在Program類中,設定了一個clientList清單,用來存儲所有連接配接來的client端;基本邏輯是server在接受到client處理的資訊後,将這個資訊利用廣播的方式發送給所有的clientList的用戶端。

- Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TCP_server_communication
{
    class Program
    {
        static List<Client> clientList = new List<Client>();//建立一個clientList用來儲存所有連接配接的用戶端,便于将資訊分發給它們

        public static void BroadcastMessage(string message)
        {
            var notConnectedList = new List<Client>();
            foreach (var client in clientList)
            {
                if(client.Connected)
                {
                    client.SendMessage(message);
                }

                else
                {
                    notConnectedList.Add(client);
                }
            }

            foreach(var temp in notConnectedList)
            {
                clientList.Remove(temp);//将斷開連接配接的用戶端從clientList中删除
            }
        }
        static void Main(string[] args)
        {
            //建立一個Socket
            //第一個參數表示一個内網,第二個參數表示以流的形式進行通信,第三個參數表示使用TCP協定進行通信
            Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //綁定IP和端口号
            IPAddress ipaddress = new IPAddress(new byte[] { , , ,  });
            EndPoint point = new IPEndPoint(ipaddress, );
            tcpServer.Bind(point);

            //開始監聽,等待用戶端連接配接
            //參數表示最大的連接配接數
            tcpServer.Listen();
            Console.WriteLine("啟動伺服器成功,開始監聽...");

            //暫停目前線程,知道有一個用戶端連接配接過來,然後再執行之後的代碼
            //傳回值為一個Socket,使用這個Socket來進行之後的通信

            while (true)
            {
                Socket clientSocket = tcpServer.Accept();
                Console.WriteLine("連接配接成功!");
                Client client = new Client(clientSocket);//将與每個用戶端通信的邏輯(收發消息)放到Client類中進行處理
                clientList.Add(client);
            }

        }
    }
}
           

- Client.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace TCP_server_communication
{
    class Client
    {
        private Socket clientSocket;
        private Thread t;
        private byte[] data = new byte[];//建立一個1024大小的資料容器

        public Client(Socket s)
        {
            clientSocket = s;
            //啟動一個線程來處理用戶端的資料接收
            t = new Thread(ReceiveMessage);
            t.Start();
        }

        private void ReceiveMessage()
        {
            while (true)//用一個死循環來一直接收用戶端的資料
            {
                //在接收資料之前,判斷socket連接配接是否斷開
                if(clientSocket.Poll(, SelectMode.SelectRead))//10為等待的時間為10ms,selectmode.read表示是否可以讀取對方的消息
                {
                    clientSocket.Close();
                    Console.WriteLine("Message: 對方已離線!");
                    break;//跳出循環,終止這個線程的執行
                }
                int length = clientSocket.Receive(data);
                string message = Encoding.UTF8.GetString(data, , length);
                //接收到資料後,需要将資料分發到用戶端中。
                //即,将這個消息廣播到所有的用戶端中
                Program.BroadcastMessage(message);
                Console.WriteLine("Message: " + message);

            }
        }

        public void SendMessage(string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            clientSocket.Send(data);
        }

        public bool Connected//判斷目前client的連接配接狀态
        {
            get { return clientSocket.Connected; }   
        }

    }
}
           

2, Client

client是利用Unity來實作的。

具體的UI設計和實作是利用NGUI來實作的,不過以後UGUI是主流。

設計的具體細節在這裡便不再贅述,主要是CharManager的腳本實作:

using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System;
using System.Threading;
using UnityEngine;

public class ChartManager : MonoBehaviour
{
    public string ipaddress = "192.168.123.1";
    public int port = ;
    public UIInput textInput;//UIInput用來做輸入
    public UILabel chatLable;//UILable用來做輸出
    private Socket clientSocket;
    private Thread t;
    private byte[] data = new byte[];//資料容器
    private string message = "";//消息容器


    // Use this for initialization
    void Start()
    {
        ConnectToServer();
    }

    // Update is called once per frame
    void Update()
    {
        if(message!=null && message!="")
        {
            chatLable.text += "\n" +"message:" +  message;
            message = "";//清空消息,便于下一次的message的探測
        }
    }


    void ConnectToServer()
    {
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //與伺服器端建立連接配接
        clientSocket.Connect(new IPEndPoint(IPAddress.Parse(ipaddress), port));

        //建立一個新的線程,用于接收消息
        t = new Thread(ReceiveMessage);
        t.Start();
    }

    //循環接收消息
    void ReceiveMessage()
    {
        while(true)
        {
            if (clientSocket.Connected == false)
                break;
            int length = clientSocket.Receive(data);
            message = Encoding.UTF8.GetString(data, , length);

        }

    }

    void SendMessage(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        clientSocket.Send(data);
    }

    public void OnSendButtonClick()
    {
        string value = textInput.value;//通過value屬性來擷取一個輸入框中的值
        SendMessage(value);
        textInput.value = "";
    }

    void OnDestory()
    {

        clientSocket.Shutdown(SocketShutdown.Both);//關閉連接配接
        clientSocket.Close();
    }
}
           

需要注意的是,在build時,需要在PlayerSetting中需要設定:

Run In Background

這樣就可以保證兩個用戶端在背景時,也可以同步的接受到資訊。