1. 程式人生 > >C# Socket網路程式設計之客戶端群發訊息

C# Socket網路程式設計之客戶端群發訊息

接上一片部落格,接著socket的學習。此次寫的是伺服器端被多個客戶端連線,並且一個客戶端發訊息,其他連線的客戶端都可接收到。
伺服器端設計思路:
1.要有一個執行緒監聽埠,當有客戶端連線上時,就要產生一個socket物件負責和這個客戶端通訊,此時需要開啟一個執行緒處理與這個客戶端的通訊。
2.轉發給其他客戶端時,需要知道所有連線客戶端的資訊。所以建立一個集合,儲存連線客戶端資訊
3.建立一個客戶端集合類,能夠新增客戶端資訊,同時當一個客戶端離開後能夠從集合中移除資訊
4.建立一個接受客戶端訊息的類,能夠接受產生的socket的例項物件
ClientGeneric類:
class ClientGeneric
{
   private Dictionary<string,Socket> clientDic=new Dictionary<string,Socket>();
   private static ClientGeneric cg=new ClientGeneric();//單例模式,保證各個執行緒訪問同一個物件
   public static ClientGeneric Instance()
   {
    return cg;
   }
   //將連線的客戶端資訊和產生的socket物件加入集合中
   public void AddClient(string key,Socket socket)
   {
      clientDic.Add(key,socket);
   }
   //當一個客戶端離開後移除集合中客戶端資訊與產生的socket物件
   public void RemoveClient(string key)
   {
      clientDic.Remove(key);
   }
   //群聊是傳送訊息,遍歷集合中客戶端資訊,為每一個客戶端傳送訊息
   public void SendMsg(string msg)
   {
     byte[] msgBytes=Encoding.ASCII.GetBytes(msg.ToCharArray());
     foreach(var kvp in clientDic)
     {
     kvp.Value.Send(msgBytes);
     }
   }
}

伺服器端接受客戶端會話處理類ServerSession:
class ServerSession
{
   private Socket socket;
   public ServerSession(Socket socket)
   {
      this.socket=socket;
   }
   public void GetMsg()
   {
      string remoteEndPoint=socket.RemoteEndPiont.ToString();//獲取連線客戶端的資訊
      ClientGeneric.Instance().AddClient(remoteEndPiont,socket);
      string msg=string.Empty;
      byte[] bytes=new byte[1024*1024];
      int len=0;
      while(true)//迴圈獲取客戶端發來的訊息
      {
        len=socket.Receive(bytes);
        msg=Encoding.UTF8.GetString(bytes,0,len);
        if(msg.Equals("End"))
        {
           ClientGeneric.Instance().Remove(remoteEndPoint);//從集合中移除斷開的客戶端資訊
           Thread.CurrentThread.Abort();//停止當前監聽的執行緒
           socket.Close();
        }
        //將收到的資訊進行轉發給其他客戶端
        ClientGeneric.Instance().SendMsg(msg);
        
      }

   }
}

伺服器監聽類:
public partial class ServerForm
{
        Socket socket_Server = null; //定義一個套接字介面物件,並初始化值為空
        Thread myThread = null; //定義一個執行緒物件,並初始化值為空        
        Socket socket_Connet = null; //用於與客戶端連線       
        string RemoteEndPoint; //客戶端的網路結點
        private void buttonStartListen_Click(object sender, EventArgs e)
        {
            socket_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //new一個Socket物件,注意這裡用的是流式Socket(針對於面向連線的TCP服務應用)而不是資料報式Socket(針對於面向無連線的UDP服務應用)。
            string IP = textBoxIP.Text.Trim();
            IPAddress ServerIP = IPAddress.Parse(IP); //提取IP地址
            int port = int.Parse(textBoxPort.Text.Trim()); //port是埠號
            IPEndPoint point = new IPEndPoint(ServerIP, port); //point為網路結點物件
            socket_Server.Bind(point); //將結點繫結到套接字上
            socket_Server.Listen(10); //設定連線佇列的最大長度,可根據伺服器的效能,可以設定更大程度。
            textBoxListenMsg.AppendText("監聽成功!\t\n");
            
            myThread = new Thread(Listen_Disp);
            myThread.IsBackground = true; //設定是否為後臺執行緒,設定後臺執行緒是為了使當關閉視窗時所有其他執行緒關閉
            myThread.Start();

        }

        void Listen_Disp()
        {
            while (true)
            {
                socket_Connet = socket_Server.Accept();
                //當收到一個客戶端連線,就產生一個socket負責與其通訊,並開啟一個執行緒管理這個客戶端
                ServerSession ss = new ServerSession(socket_Connet);
                Thread t= new Thread(ss.GetMsg);
                t.Start();
                t.IsBackground = true;

                RemoteEndPoint = socket_Connet.RemoteEndPoint.ToString(); //客戶端網路結點號
                textBoxListenMsg.AppendText("成功與" + RemoteEndPoint + "客戶端建立連線!\t\n"); //顯示與客戶端連線情況               
                OnlineList_Disp(RemoteEndPoint); //顯示線上客戶端                

            }
        }

       
        void OnlineList_Disp(string Info)
        {
            listBoxOnlineList.Items.Add(Info); //線上列表中顯示連線的客戶端套接字
        }
}

客戶端接收發送訊息:
 public partial class Form1 : Form
    {
        Socket socket = null; //定義一個套接字,初始化為空
        Thread thread = null; //定義一個執行緒  
        IPEndPoint point = null;

        public Form1()
        {
            InitializeComponent();
            RichTextBox.CheckForIllegalCrossThreadCalls = false; //允許跨執行緒訪問
        }

        private void buttonConnectToServer_Click(object sender, EventArgs e)
        {
            string IP = textBoxIP.Text.Trim();
            int port = int.Parse(textBoxPort.Text.Trim());
            IPAddress ip = IPAddress.Parse(IP);
            point = new IPEndPoint(ip, port); //網路結點物件
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化套接字
            socket.Connect(point);//連線給定的IP地址和埠號
            thread = new Thread(ShowMsg); //開啟一個監聽執行緒,監聽伺服器端發來的資料
            thread.IsBackground = true; //後臺執行緒
            thread.Start();
            OnlineList_Disp(textBoxIP.Text.Trim());
        }

        void OnlineList_Disp(string Info)
        {
            listBoxOnlineList.Items.Add(Info); //線上列表中顯示連線的客戶端套接字
        }
        void ShowMsg()
        {
            byte[] bytes = new byte[1024 * 1024 * 3]; //設定位元組流陣列為3M
            int length = 0;
            string Msg = string.Empty;
            while (true)
            {
                length = socket.Receive(bytes);
                Msg = System.Text.Encoding.UTF8.GetString(bytes, 0, length); //注意要把位元組轉化為字串
                richTextBoxReceiveMsg.AppendText("伺服器端:"+Msg+"\t\n"); //顯示接收的資訊             

            }
        }
        void Receive_Show(string Info)
        {
            richTextBoxReceiveMsg.AppendText(Info + "\t\n");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            string sendMsg = richTextBoxMsg.Text.Trim(); //要傳送的資訊
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); //將要傳送的資訊轉化為位元組陣列,因為Socket傳送資料時是以位元組的形式傳送的
            socket.Send(bytes); //傳送資料
            richTextBoxReceiveMsg.AppendText("客戶端:"+sendMsg+"\t\n");
            richTextBoxMsg.Text = "";

        }

        private void button_Interput_Click(object sender, EventArgs e)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes("End"); //將要傳送的資訊轉化為位元組陣列,因為Socket傳送資料時是以位元組的形式傳送的
            socket.Send(bytes); //傳送資料
            thread.Abort();//將監聽執行緒停止,不在接聽伺服器端發來的資料
            socket.Close();
        }


    }