首頁 > 軟體

C#基於Socket實現簡單聊天室功能

2022-02-12 16:00:33

因為這段時間在學習Socket,所以就試著寫了一個簡單的聊天室。主要分為伺服器端和多個使用者端。利用伺服器端作資料中轉站,實現訊息群發。

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);//監聽是否有使用者端發起連線
            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);//將每個新建立的連線通訊放於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;
            //由於訊息是不斷傳送的,需要多次進行處理。這裡開一個執行緒,專門用來處理訊息。
            mesManageTherad = new Thread(MessageSendFromClient);
            mesManageTherad.Start();
        }

        private void MessageSendFromClient()
        {
            //開啟的執行緒一直檢測使用者端使用者端發過來的訊息
            while (true)
            {
                //判斷連線是否斷開,  SelectMode.SelectRead讀狀態模式。
                //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("使用者端傳送過來的訊息:"+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;
            }
        }

    }
}

伺服器端邏輯:

這裡的伺服器主要負責建立連線,接受使用者端訊息,廣播使用者端發來的訊息。
伺服器通過socket物件繫結伺服器IP和相應埠號(埠號自己開,沒有被其他軟體佔用就好),通過Listen監聽和伺服器socket物件的Accept方法捕捉連線到伺服器的使用者端socket,將捕捉到的使用者端socket放入List集合中方便統一管理和後面的訊息群發。
關於捕捉到的使用者端socket的邏輯處理放在了Client類中統一管理。
Client類將收到使用者端的訊息進行接受,在Client中開啟一個執行緒用於不斷得檢測是否有新訊息從使用者端傳送過來,若有訊息傳送過來則通過CastMessageTOAllConnetedClients方法(對socket物件的Send方法的封裝)傳送給每一個使用者端。

2.使用者端

使用者端是在Unity中使用NGUI外掛簡單開發的一個聊天介面。把指令碼掛在NGUI控制元件上即可。使用者端主要負責顯示訊息,傳送訊息,接收訊息。

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);
        //因為是一直在準備接受的狀態,所以開啟一個執行緒來負責處理接受訊息
        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方法連線到伺服器。在這裡,假如某個使用者端通過輸入框輸入文字被使用者端指令碼捕捉,它將以流的方式傳送到伺服器,伺服器接受到文字,並在伺服器端將文字群發至每個使用者端。伺服器開設了一個執行緒專門用於捕捉使用者端發來的訊息,當然,使用者端也有相應的執行緒時刻捕捉伺服器發來的訊息。訊息被使用者端捕捉到了,就可以顯示在各自的使用者端介面上了。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com