C#基於unity製作聊天室(TCP協議)

完成一個聊天室,首先需要有服務端,然後客戶端接入服務端,才能看到顯示的數據。

因爲有多個客戶端要接入到服務端,所以要服務端開啓多線程。

首先來搭建一個服務端,這裏我使用的是TCP協議。

這裏我新建了一個C#項目,名爲“聊天室-服務端”,其中有兩個.cs類,一個爲Client.cs,另一個爲Program.cs.

首先這裏列出Client.cs類,該類用於描述客戶端,因爲有多個客戶端接入,所以我們將每個客戶端抽象爲一個類:

Client.cs

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace 聊天室_服務端
{
    internal class Client
    {
        private readonly Thread Accept;
        private readonly Socket ClientSocket;
        private readonly byte[] data = new byte[1024];//數據存儲區
        private string Message;

        public Client(Socket socket)
        {
            ClientSocket = socket;
            Accept = new Thread(ReceiveMessage);//每一個客戶端生成時,都需要調用他的ReceiveMessage函數用於接受數據,於是開啓一個新的線程
            Console.WriteLine("一個客戶端連接過來了");
            Accept.Start();//必須用Start啓動線程,否則線程不會啓動
        }

        public bool connected => ClientSocket.Connected;

        public void ReceiveMessage()//接受來自服務端數據的函數
        {
            try
            {
                while (true)
                {
                    //在接收數據之前,判斷Socket連接是否斷開
                    if (ClientSocket.Poll(10, SelectMode.SelectRead) || !ClientSocket.Connected)//這個判斷是否連接的方式非常重要!!!!
                    {
                        ClientSocket.Close(); //關閉掉當前連接
                        Console.WriteLine("一個客戶端斷開了連接");
                        break;
                    }

                    var length = ClientSocket.Receive(data);//使用data數組(字節流)接受數據,返回的length就是接收到的數據佔據現有數組的長度
                    Message = Encoding.UTF8.GetString(data, 0, length);//getString(數據數組,開始位置,結束位置)用於獲取有效的信息
                    Program.Boradcast(Message);//這裏調用了Program類中的Boradcast函數,目的是進行廣播到所有的客戶端
                    Console.WriteLine("收到了來自用戶的消息:" + Message);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        public void SendMessage(string Message)//客戶端向外發送數據的函數
        {
            ClientSocket.Send(Encoding.UTF8.GetBytes(Message));
        }
    }
}

這裏有一個要點:

那就是判斷客戶端是否仍然與服務端保持連接:

if (ClientSocket.Poll(10, SelectMode.SelectRead) || !ClientSocket.Connected)

第一個Poll函數纔是最重要的,Connect可能並沒有連接上,但是過程還在執行,最好的方式是兩個一起使用。Poll中的參數10表示發射一個10us的信號,看是否有迴應,這纔是確認是否保持連接的正確方式。

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 聊天室_服務端
{
    class Program
    {
        private static List<Client> ClientList = new List<Client>();
        private static List<Client> notClientList = new List<Client>();
        public static void Boradcast(string Message)
        {
            foreach(var client in ClientList)
            {
                if(client.connected)
                {
                    client.SendMessage(Message);
                }
                else
                {
                    notClientList.Add(client);
                }
            }
            foreach(var client in notClientList)
            {
                ClientList.Remove(client);
            }
        }
        static void Main(string[] args)
        {
            Socket TcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//這句話是申請一個新的套接字,作用在InterNet網絡,也就是因特網,類型是流式套接字,還有數據報類型等等,使用的協議是TCP協議
            TcpServer.Bind(new IPEndPoint(IPAddress.Parse("xx.xx.xx.xx"), 7788));//這一句話用於將套接字綁定在“當前主機IP的第7788號端口之上”這裏的ip需要自己查閱
            TcpServer.Listen(100);
            Console.WriteLine("服務器已開啓");
            while (true)//服務端一旦開啓就會一直運行下去
            {
                Socket socket = TcpServer.Accept();//Accept用於等待客戶端接入,如果沒有客戶端接入就會一直等待
                Client client = new Client(socket);
                ClientList.Add(client);//用一個客戶端的列表,來表示所有的客戶端
            }
        }
    }
}

注意:上面的代碼,需要使用你的本機ip地址,具體的操作方法:

按下windows+R,輸入cmd

輸入ipconfig

一般情況下,這其中ipv4地址即爲你的本機地址。我這裏爲了防止ip泄露,所以打碼了。

運行結果如下:

此時的服務器正在等待客戶端的接入。

 

爲了區別客戶端與服務器,這裏最好新建另外一個unity項目,我將其命名爲“聊天室-客戶端”,先使用unity的NGUI控件佈置好佈局:

然後新建一個類ChatManager.cs用於管理這個聊天框。

ChatManager.cs

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

public class ChatManager : MonoBehaviour {
	public string ipadress= "xx.xx.xx.xx";//你上次用的什麼ip地址來建立的服務端,這裏就要寫什麼ip地址
	public int Port=7788;//因爲上次用的端口號是7788,所以這裏也用7788
	private Socket ClientSocket;
//下面這三個均爲unity的窗口控件
	public UIInput textInput; 
	public UILabel ChatLabel;
	public UIInput NameInput;

	private string Name="用戶";//用戶暱稱
	private byte[]data=new byte[1024];
	private string Message;
	Thread getMessage;
	// Use this for initialization
	void Start () {
		Connect ();
	
	}
	
	// Update is called once per frame
	void Update () {
		if (Message != null && Message != "") {
			ChatLabel.text += "\n" + Message;
			Message="";
		}
	
	}
	public void ReceiveMessage()
	{
		while (true)
		{
			if (ClientSocket.Connected==false||ClientSocket.Poll(10,SelectMode.SelectRead)) 
			{
				break;
			}
			else
			{
				int length=ClientSocket.Receive(data);
				Message=Encoding.UTF8.GetString(data,0,length);
			}
			
		}	

	}

	public void Connect()
	{
		ClientSocket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		ClientSocket.Connect (new IPEndPoint (IPAddress.Parse (ipadress), Port));
		getMessage = new Thread (ReceiveMessage);
		getMessage.Start ();
	}
	public void SendMesage(string Message){
		byte[] data = Encoding.UTF8.GetBytes (Message);
		ClientSocket.Send (data);
	}
	public void OnSendButtonClick()
	{
		string value = textInput.value;
		SendMesage (Name+":"+value);
		textInput.value = "";
	}

	public void OnDestroy()..程序退出時候,自動調用OnDestroy
	{
		ClientSocket.Shutdown (SocketShutdown.Both);//兩邊的連接均斷開
		ClientSocket.Close ();
		//getMessage.Abort ();
	}
	public void getNameClick()
	{
		Name = NameInput.value;
	}
}

最後運行的效果圖:

感謝閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章