C#基于Socket實(shí)現(xiàn)簡(jiǎn)單聊天室功能
因?yàn)檫@段時(shí)間在學(xué)習(xí)Socket,所以就試著寫了一個(gè)簡(jiǎn)單的聊天室。主要分為服務(wù)器端和多個(gè)客戶端。利用服務(wù)器端作數(shù)據(jù)中轉(zhuǎn)站,實(shí)現(xiàn)消息群發(fā)。
1、服務(wù)器端有兩個(gè)類:
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
namespace 聊天室_Socket_TCP_服務(wù)器端
{
? ? 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);//將每個(gè)新創(chuàng)建的連接通信放于client類做通信
? ? ? ? ? ? ? ? ? ? clients.Add(client);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }
? ? }
}using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace 聊天室_Socket_TCP_服務(wù)器端
{
? ? /// <summary>
? ? /// 利用該類和客戶端做通信
? ? /// </summary>
? ? class Client
? ? {
? ? ? ? public Socket clientSocket;
? ? ? ? private Thread mesManageTherad;?
? ? ? ? private byte[] bufffer=new byte[20];
? ? ? ? public Client(Socket soc)
? ? ? ? {
? ? ? ? ? ? clientSocket = soc;
? ? ? ? ? ? //由于消息是不斷發(fā)送的,需要多次進(jìn)行處理。這里開一個(gè)線程,專門用來(lái)處理消息。
? ? ? ? ? ? mesManageTherad = new Thread(MessageSendFromClient);
? ? ? ? ? ? mesManageTherad.Start();
? ? ? ? }
? ? ? ? private void MessageSendFromClient()
? ? ? ? {
? ? ? ? ? ? //開啟的線程一直檢測(cè)客戶端客戶端發(fā)過(guò)來(lái)的消息
? ? ? ? ? ? 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ā)送過(guò)來(lái)的消息:"+mes);
? ? ? ? ? ? ? ? ? ? //廣播消息出去給每個(gè)客戶端
? ? ? ? ? ? ? ? ? ? Program.CastMessageTOAllConnetedClients(mes);//對(duì)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;
? ? ? ? ? ? }
? ? ? ? }
? ? }
}服務(wù)器端邏輯:
這里的服務(wù)器主要負(fù)責(zé)建立連接,接受客戶端消息,廣播客戶端發(fā)來(lái)的消息。
服務(wù)器通過(guò)socket對(duì)象綁定服務(wù)器IP和相應(yīng)端口號(hào)(端口號(hào)自己開,沒有被其他軟件占用就好),通過(guò)Listen監(jiān)聽和服務(wù)器socket對(duì)象的Accept方法捕捉連接到服務(wù)器的客戶端socket,將捕捉到的客戶端socket放入List集合中方便統(tǒng)一管理和后面的消息群發(fā)。
關(guān)于捕捉到的客戶端socket的邏輯處理放在了Client類中統(tǒng)一管理。
Client類將收到客戶端的消息進(jìn)行接受,在Client中開啟一個(gè)線程用于不斷得檢測(cè)是否有新消息從客戶端發(fā)送過(guò)來(lái),若有消息發(fā)送過(guò)來(lái)則通過(guò)CastMessageTOAllConnetedClients方法(對(duì)socket對(duì)象的Send方法的封裝)發(fā)送給每一個(gè)客戶端。
2.客戶端
客戶端是在Unity中使用NGUI插件簡(jiǎn)單開發(fā)的一個(gè)聊天界面。把腳本掛在NGUI控件上即可??蛻舳酥饕?fù)責(zé)顯示消息,發(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 = "";//默認(rèn)為空串
? ? // Use this for initialization
? ? void Start () {
? ? ? ? ConnetToSever(_ipAdress, _port);//與服務(wù)器建立連接
? ? }
? ? // 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);
? ? ? ? //因?yàn)槭且恢痹跍?zhǔn)備接受的狀態(tài),所以開啟一個(gè)線程來(lái)負(fù)責(zé)處理接受消息
? ? ? ? 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 = "輸入框?yàn)榭眨?;
? ? ? ? }
? ? ? ? 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:不要在單獨(dú)的線程里面操作unity組件
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }
? ? }
}在客戶端中同樣有一個(gè)socket對(duì)象,這個(gè)對(duì)象通過(guò)ConnetToSever方法連接到服務(wù)器。在這里,假如某個(gè)客戶端通過(guò)輸入框輸入文本被客戶端腳本捕捉,它將以流的方式發(fā)送到服務(wù)器,服務(wù)器接受到文本,并在服務(wù)器端將文本群發(fā)至每個(gè)客戶端。服務(wù)器開設(shè)了一個(gè)線程專門用于捕捉客戶端發(fā)來(lái)的消息,當(dāng)然,客戶端也有相應(yīng)的線程時(shí)刻捕捉服務(wù)器發(fā)來(lái)的消息。消息被客戶端捕捉到了,就可以顯示在各自的客戶端界面上了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C# lambda表達(dá)式應(yīng)用如何找出元素在list中的索引
這篇文章主要介紹了C# lambda表達(dá)式應(yīng)用如何找出元素在list中的索引的相關(guān)資料,需要的朋友可以參考下2018-01-01
C# 啟用事務(wù)提交多條帶參數(shù)的SQL語(yǔ)句實(shí)例代碼
這篇文章主要介紹了C# 啟用事務(wù)提交多條帶參數(shù)的SQL語(yǔ)句實(shí)例代碼,需要的朋友可以參考下2018-02-02
C#實(shí)現(xiàn)進(jìn)程管理的啟動(dòng)和停止實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)進(jìn)程管理的啟動(dòng)和停止方法,以操作記事本程序?yàn)槔?實(shí)例分析了C#針對(duì)進(jìn)程操作的基本技巧,需要的朋友可以參考下2015-07-07
c#之用戶定義的數(shù)據(jù)類型轉(zhuǎn)換介紹
c#允許定義自己的數(shù)據(jù)類型,這意味著需要某些工具支持在自己的數(shù)據(jù)類型間進(jìn)行數(shù)據(jù)轉(zhuǎn)換。方法是把數(shù)據(jù)類型轉(zhuǎn)換定義為相關(guān)類的一個(gè)成員運(yùn)算符,數(shù)據(jù)類型轉(zhuǎn)換必須聲明是隱式或者顯式,以說(shuō)明怎么使用它2014-01-01
C#中用foreach語(yǔ)句遍歷數(shù)組及將數(shù)組作為參數(shù)的用法
這篇文章主要介紹了C#中用foreach語(yǔ)句遍歷數(shù)組及將數(shù)組作為參數(shù)的用法,C#的數(shù)組可作為實(shí)參傳遞給方法形參,需要的朋友可以參考下2016-01-01

