1. 程式人生 > >Socket程式設計之TcpClient、TcpListener在Unity3D中的應用

Socket程式設計之TcpClient、TcpListener在Unity3D中的應用

經過一段時間的學習,對於TCP/IP協議有了一定的認識,大概知道了TCP協議通訊的原理,接下來主要實現用TcpClient、TcpListener兩個封裝類和stream來實現一個小的demo:用vs建一個工程作為伺服器,然後用Unity引擎作為客戶端,實現客戶端和伺服器,客戶端和客戶端之間的通訊。

由於入坑試驗多次,這裡強調一下通訊時候的資料轉換,當在客戶端傳送資訊的時候,會將使用者輸入的字串轉為byte型陣列(要通過NetworkStream stream這個物件寫入)

程式碼塊一:

 void SendMessage(string myWrite) {
        byte[] data = Encoding.UTF8.GetBytes(myWrite);
        stream.Write(data, 0, data.Length);
  }

伺服器端通過流stream來讀取資料,並且將資料重新轉換為字串:

程式碼塊二:

byte[] data = new byte[1024];

int length = stream.Read(data, 0, data.Length);

string message = Encoding.UTF8.GetString(data, 0, length);

轉換為字串後就可以在伺服器端列印輸出了,然後需要再次將字串資料轉為byte[]陣列,重新傳回客戶端

程式碼塊三:(注意!!!在伺服器端一定要將客戶端讀取的byte陣列資料先轉為字串,再將字串重新轉為byte[]陣列寫入到客戶端,至於原因,前面踩過坑,發現,如果直接將客戶端讀取的byte陣列寫入到客戶端,在Unity的UGUI的文字框顯示會出現問題,UGUI只會顯示使用者輸入的第一條資料。總之,這一過程必不可少

public void SendMessage(string message) {
            byte[] data = Encoding.UTF8.GetBytes(message);
            stream.Write(data, 0, data.Length);
 }

然後在客戶端讀取伺服器寫入過來的資料,將其用byte陣列存放,並轉為string字串

程式碼塊四:

byte[] data = new byte[1024];
int length = stream.Read(data, 0, data.Length);
message = Encoding.UTF8.GetString(data, 0, length);

最後將字串通過Unity引擎的UGUI和基本程式碼顯示到Unity場景中

 

完整程式碼如下

伺服器端(用vs建立一個工程)由一個主方法和自定義類組成:

額外新增名稱空間:using System.Net;   using System.Net.Sockets;  using System.Threading;

主方法如下:

 class Program
    {
        public static List<ClientStream> ls = new List<ClientStream>();
        public static void BroadcastMessage(string message)
        {
            List<ClientStream> nols = new List<ClientStream>();
            foreach (var client in ls)
            {
                if (client.client.Connected)
                {
                    client.SendMessage(message);
                }
                else
                {
                    nols.Add(client);
                }
            }
            foreach (var i in nols)
            {
                ls.Remove(i);
            }
        }

        static void Main(string[] args)
        {
            //1.建立一個TCPListener物件,例項化綁定了IP和埠號
            TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788));
            //2.開始偵聽
            listener.Start();
            Console.WriteLine("已開啟伺服器...");

            TcpClient client;
            NetworkStream stream;

            byte[] data = new byte[1024]; //data作為一個容器用來接收
            //讀取流裡面的資料,存放到data的0索引位置,每次最大讀取1024個位元組
            while (true)
            {
                //3.等待客戶端連線
                client = listener.AcceptTcpClient();
                Console.WriteLine("一個客戶端已連線");
                //4.取得客戶端傳送過來的資料
                stream = client.GetStream();

                ClientStream clientStream = new ClientStream(stream, client);
                ls.Add(clientStream);
            }

            stream.Close();
            client.Close();
            listener.Stop();
            Console.ReadKey();
        }
    }

自定義類如下:

 class ClientStream
    {
        private Thread t;
        private NetworkStream stream;
        public TcpClient client;

        public ClientStream(NetworkStream stream, TcpClient client) {
            this.stream = stream;
            this.client = client;
            t = new Thread(ReceiveMessage);
            t.Start();
        }

        private void ReceiveMessage() {
            while (true)
            {
                if (client.Connected == false)
                {
                    Console.WriteLine("客戶端斷開連線");
                    stream.Close();
                    break; //跳出迴圈,終止執行緒的執行
                }
                byte[] data = new byte[1024];
                int length = stream.Read(data, 0, data.Length);
                string message = Encoding.UTF8.GetString(data, 0, length);
                Console.WriteLine("收到了訊息:" + message);

                Program.BroadcastMessage(message);
            }
        }

        public void SendMessage(string message) {
            byte[] data = Encoding.UTF8.GetBytes(message);
            stream.Write(data, 0, data.Length);
        }
    }

客戶端指令碼(建立一個Unity專案,需額外新增名稱空間using System.Net;  using System.Net.Sockets;  using System.Text;
using System.Threading;):

public class ClientTest : MonoBehaviour {

    public Text textContainer;
    public InputField text;

    private string address = "192.168.1.101";
    private int port = 7788;
    private TcpClient client;
    private NetworkStream stream;
    private Thread t;

    private string message;

    // Use this for initialization
    void Start () {
        ConnectToServer();
    }
    
    // Update is called once per frame
    void Update () {
        if (Input.GetKeyDown(KeyCode.Return))
        {
            OnSendButtonClick();
        }
        if (message != "" && message != null)
        {
            textContainer.text += "\n" + message;
            Debug.Log("message2:" + message);
            Debug.Log("文字框2:" + textContainer.text);
            message = "";
        }
    }

    //客戶端的連線
    void ConnectToServer() {
        client = new TcpClient(address, port);
        Debug.Log("client.Available:" + client.Available);
        //if (client.Available == 0)
        //    Debug.Log("伺服器不存在");
        stream = client.GetStream();
        Debug.Log("客戶端連線");

        t = new Thread(ReceiveMessage);
        t.Start();
    }

    //客戶端傳送訊息
    void SendMessage(string myWrite) {
        byte[] data = Encoding.UTF8.GetBytes(myWrite);
        stream.Write(data, 0, data.Length);
    }

    //客戶端接收訊息
    void ReceiveMessage() {
        while (true)
        {
            if (client.Connected == false)
            {
                break;
            }
            byte[] data = new byte[1024];
            int length = stream.Read(data, 0, data.Length);
            message = Encoding.UTF8.GetString(data, 0, length);
            Debug.Log("從伺服器受到了:"+message);
        }
    }

    //按鈕偵聽函式
    public void OnSendButtonClick() {
        if (text.text != null && text.text != "" && client.Connected) {
            string myWrite= text.text;
            SendMessage(myWrite);
            text.text = "";
        }
    }

    void OnDestroy() {
        stream.Close();
        client.Close();
    }
}

最後先編譯vs這邊的程式,即啟動伺服器,

 

 

 

然後執行Unity,即啟動客戶端,

也可以將Unity專案打包釋出PC端,這樣就可以有多個客戶端了

 

 

 

 

 以上只是一個小的demo,望各位大佬不吝賜教!!!