C#基于Socket實現(xiàn)簡單聊天室功能
因為這段時間在學習Socket,所以就試著寫了一個簡單的聊天室。主要分為服務器端和多個客戶端。利用服務器端作數(shù)據(jù)中轉(zhuǎn)站,實現(xiàn)消息群發(fā)。
1、服務器端有兩個類:
using System.Collections.Generic; using System.Net; using System.Net.Sockets; namespace 聊天室_Socket_TCP_服務器端 { ? ? class Program ? ? { ? ? ? ? static List<Client> clients = new List<Client>(); ? ? ? ? static List<Client> notClients = new List<Client>(); ? ? ? ? /// <summary> ? ? ? ? /// 廣播消息 ? ? ? ? /// </summary> ? ? ? ? /// <param name="message"></param> ? ? ? ? public static void CastMessageTOAllConnetedClients(string message) ? ? ? ? { ? ? ? ? ? ? foreach (var client in clients) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? if (client.Conneted) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? client.CastMessage(message); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? else ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? notClients.Add(client); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? foreach (var temp in notClients) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? clients.Remove(temp); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? static void Main(string[] args) ? ? ? ? { ? ? ? ?? ? ? ? ? ? ? Socket tcpSever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ? ? ? ? ? ? tcpSever.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8899)); ? ? ? ? ? ? tcpSever.Listen(100);//監(jiān)聽是否有客戶端發(fā)起連接 ? ? ? ? ? ? Console.WriteLine("Begin to listen..."); ? ? ? ? ? ? while (true) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? Socket clientSocket = tcpSever.Accept(); ? ? ? ? ? ? ? ? if (clientSocket!=null) ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? Console.WriteLine("A client has connneted..."); ? ? ? ? ? ? ? ? ? ? Client client = new Client(clientSocket);//將每個新創(chuàng)建的連接通信放于client類做通信 ? ? ? ? ? ? ? ? ? ? clients.Add(client); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? Console.ReadKey(); ? ? ? ? } ? ? } }
using System; using System.Net.Sockets; using System.Text; using System.Threading; namespace 聊天室_Socket_TCP_服務器端 { ? ? /// <summary> ? ? /// 利用該類和客戶端做通信 ? ? /// </summary> ? ? class Client ? ? { ? ? ? ? public Socket clientSocket; ? ? ? ? private Thread mesManageTherad;? ? ? ? ? private byte[] bufffer=new byte[20]; ? ? ? ? public Client(Socket soc) ? ? ? ? { ? ? ? ? ? ? clientSocket = soc; ? ? ? ? ? ? //由于消息是不斷發(fā)送的,需要多次進行處理。這里開一個線程,專門用來處理消息。 ? ? ? ? ? ? mesManageTherad = new Thread(MessageSendFromClient); ? ? ? ? ? ? mesManageTherad.Start(); ? ? ? ? } ? ? ? ? private void MessageSendFromClient() ? ? ? ? { ? ? ? ? ? ? //開啟的線程一直檢測客戶端客戶端發(fā)過來的消息 ? ? ? ? ? ? while (true) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? //判斷連接是否斷開, ?SelectMode.SelectRead讀狀態(tài)模式。 ? ? ? ? ? ? ? ? //poll已斷開返回true ? ? ? ? ? ? ? ? if (clientSocket.Poll(10,SelectMode.SelectRead)==true) ? ? ? ? ? ? ? ? ? ? { ? ? ? ? ? ? ? ? ? ? clientSocket.Close(); ? ? ? ? ? ? ? ? ? ? break;//終止本線程 ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? int byteNum = clientSocket.Receive(bufffer);//從客戶端接受消息 ? ? ? ? ? ? ? ? ? ? string mes = Encoding.UTF8.GetString(bufffer, 0 , byteNum); ? ? ? ? ? ? ? ? ? ? Console.WriteLine("客戶端發(fā)送過來的消息:"+mes); ? ? ? ? ? ? ? ? ? ? //廣播消息出去給每個客戶端 ? ? ? ? ? ? ? ? ? ? Program.CastMessageTOAllConnetedClients(mes);//對CastMessage的一層封裝 ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? /// <summary> ? ? ? ? /// Send messages to Clients ? ? ? ? /// </summary> ? ? ? ? public void CastMessage(string message) ? ? ? ? { ? ? ? ? ? ? byte[] data = Encoding.UTF8.GetBytes(message); ? ? ? ? ? ? clientSocket.Send(data); ? ? ? ? } ? ? ? ? /// <summary> ? ? ? ? /// 判斷是否斷開連接 ? ? ? ? /// </summary> ? ? ? ? public bool Conneted ? ? ? ? { ? ? ? ? ? ? get ? ? ? ? ? ? { ? ? ? ? ? ? ? ? return clientSocket.Connected; ? ? ? ? ? ? } ? ? ? ? } ? ? } }
服務器端邏輯:
這里的服務器主要負責建立連接,接受客戶端消息,廣播客戶端發(fā)來的消息。
服務器通過socket對象綁定服務器IP和相應端口號(端口號自己開,沒有被其他軟件占用就好),通過Listen監(jiān)聽和服務器socket對象的Accept方法捕捉連接到服務器的客戶端socket,將捕捉到的客戶端socket放入List集合中方便統(tǒng)一管理和后面的消息群發(fā)。
關于捕捉到的客戶端socket的邏輯處理放在了Client類中統(tǒng)一管理。
Client類將收到客戶端的消息進行接受,在Client中開啟一個線程用于不斷得檢測是否有新消息從客戶端發(fā)送過來,若有消息發(fā)送過來則通過CastMessageTOAllConnetedClients方法(對socket對象的Send方法的封裝)發(fā)送給每一個客戶端。
2.客戶端
客戶端是在Unity中使用NGUI插件簡單開發(fā)的一個聊天界面。把腳本掛在NGUI控件上即可。客戶端主要負責顯示消息,發(fā)送消息,接收消息。
using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; public class ChatManager : MonoBehaviour { ? ? private string _ipAdress = "192.168.1.2"; ? ? private int _port=8899; ? ? EndPoint remotPoint; ? ? Socket clientSocket; ? ? public UIInput buttonInput; ? ? private bool isCanSend=false; ? ? private string buttonMessage=null; ? ? Thread receiveThread; ? ? byte[] bufferReceive = new byte[1024]; ? ? public UILabel chatWindowLable; ? ? private string message = "";//默認為空串 ? ? // Use this for initialization ? ? void Start () { ? ? ? ? ConnetToSever(_ipAdress, _port);//與服務器建立連接 ? ? } ? ? // Update is called once per frame ? ? void Update () { ? ? ? ? if (message!=null&&message!="") ? ? ? ? { ? ? ? ? ? ? chatWindowLable.text += "\n" + message; ? ? ? ? ? ? message = "";//清空消息 ? ? ? ? } ? ? } ? ? void ConnetToSever(string ipadress,int port) ? ? { ? ? ? ? clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ? ? ? ? remotPoint = new IPEndPoint(IPAddress.Parse(ipadress),port); ? ? ? ? //建立連接 ? ? ? ? clientSocket.Connect(remotPoint); ? ? ? ? //因為是一直在準備接受的狀態(tài),所以開啟一個線程來負責處理接受消息 ? ? ? ? receiveThread = new Thread(ReceiveMessageFormSever); ? ? ? ? receiveThread.Start(); ? ? } ? ? private new void SendMessage(string message) ? ? { ? ? ? ? byte [] buffer= Encoding.UTF8.GetBytes(message); ? ? ? ? clientSocket.SendTo(buffer,remotPoint); ? ? } ? ? public void OnSendButtonClickS() ? ? { ? ? ? ? if (buttonInput.value!=null) ? ? ? ? { ? ? ? ? ? ? buttonMessage = buttonInput.value; ? ? ? ? } ? ? ? ? else ? ? ? ? { ? ? ? ? ? ? buttonMessage = "輸入框為空!"; ? ? ? ? } ? ? ? ? SendMessage(buttonMessage); ? ? ? ? buttonInput.value = ""; ? ? } ? ? private void ReceiveMessageFormSever() ? ? { ? ? ? ? while (true) ? ? ? ? { ? ? ? ? ? ? if (clientSocket.Connected) ? ? ? ? ? ? { ? ? ? ? ? ? ? ? int length = clientSocket.Receive(bufferReceive); ? ? ? ? ? ? ? ? message = Encoding.UTF8.GetString(bufferReceive, 0, length); ? ? ? ? ? ? ? ? //ps:不要在單獨的線程里面操作unity組件 ? ? ? ? ? ? } ? ? ? ? ? ? else ? ? ? ? ? ? { ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? } ? ? } }
在客戶端中同樣有一個socket對象,這個對象通過ConnetToSever方法連接到服務器。在這里,假如某個客戶端通過輸入框輸入文本被客戶端腳本捕捉,它將以流的方式發(fā)送到服務器,服務器接受到文本,并在服務器端將文本群發(fā)至每個客戶端。服務器開設了一個線程專門用于捕捉客戶端發(fā)來的消息,當然,客戶端也有相應的線程時刻捕捉服務器發(fā)來的消息。消息被客戶端捕捉到了,就可以顯示在各自的客戶端界面上了。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
C# 使用 WebBrowser 實現(xiàn) HTML 轉(zhuǎn)圖片功能的示例代碼
這篇文章主要介紹了C# 如何使用 WebBrowser 實現(xiàn) HTML 轉(zhuǎn)圖片功能,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下2020-07-07C#獲取變更過的DataTable記錄的實現(xiàn)方法
這篇文章主要介紹了C#獲取變更過的DataTable記錄的實現(xiàn)方法,對初學者很有學習借鑒價值,需要的朋友可以參考下2014-08-08c# 使用模式匹配以及 is 和 as 運算符安全地進行強制轉(zhuǎn)換
這篇文章主要介紹了c# 使用模式匹配以及 is 和 as 運算符安全地進行強制轉(zhuǎn)換,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2020-10-10