完成一個聊天室,首先需要有服務端,然後客戶端接入服務端,才能看到顯示的數據。
因爲有多個客戶端要接入到服務端,所以要服務端開啓多線程。
首先來搭建一個服務端,這裏我使用的是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;
}
}
最後運行的效果圖:
感謝閱讀。