程序簡介
好久沒寫博客了,最近時間比較充足.於是便打算把之前的聊天程序功能改進下,增加了一個服務端給客戶端群發信息的功能.
原理
首先我們需要獲取連接客戶端的IP和Port,並添加到客戶端列表里作為每個客戶端的唯一標識.然后通過相應客戶端的Socket.Send()方法將信息發送出去.
服務端給客戶端的信息群發與服務端給單個客戶端的信息發送原理是一樣的,通過遍歷客戶端列表里的所有客戶端標識,然后把信息一個個發送出去.
界面設計 - 客戶端
界面設計 - 服務端
代碼實施 - 客戶端
客戶端沒有做什么功能改進,這里就不貼代碼了,需要看的學友可以到隨筆后面下載源代碼.
代碼實施 - 服務端
這里將手動輸入服務端IPv4地址改為了程序自動獲取
/// <summary> /// 獲取本地IPv4地址 /// </summary> /// <returns>本地IPv4地址</returns> public IPAddress GetLocalIPv4Address() { IPAddress localIPv4 = null; //獲取本機所有的IP地址列表 IPAddress[] ipAddressList = Dns.GetHostAddresses(Dns.GetHostName()); foreach (IPAddress ipAddress in ipAddressList) { //判斷是否是IPv4地址 if (ipAddress.AddressFamily == AddressFamily.InterNetwork) //AddressFamily.InterNetwork表示IPv4 { localIPv4 = ipAddress; } else continue; } return localIPv4; }
為了方便后期給所有訪問的客戶端群發信息,我們需要用過通過監聽客戶端來獲取所有訪問客戶端的IP地址和端口號,並組成每個訪問客戶端的唯一標識clientName 用於顯示在客戶端列表上;客戶端唯一標識還有個作用就是服務端可以選擇性的給單獨某個客戶端發送信息.
//用於保存所有通信客戶端的Socket Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>(); //創建與客戶端建立連接的套接字 Socket socConnection = null; string clientName = null; //創建訪問客戶端的名字 IPAddress clientIP; //訪問客戶端的IP int clientPort; //訪問客戶端的端口號 /// <summary> /// 持續不斷監聽客戶端發來的請求, 用於不斷獲取客戶端發送過來的連續數據信息 /// </summary> private void WatchConnecting() { while (true) { try { socConnection = socketWatch.Accept(); } catch (Exception ex) { txtMsg.AppendText(ex.Message); //提示套接字監聽異常 break; } //獲取訪問客戶端的IP clientIP = (socConnection.RemoteEndPoint as IPEndPoint).Address; //獲取訪問客戶端的Port clientPort = (socConnection.RemoteEndPoint as IPEndPoint).Port; //創建訪問客戶端的唯一標識 由IP和端口號組成 clientName = "IP: " + clientIP +" Port: "+ clientPort; lstClients.Items.Add(clientName); //在客戶端列表添加該訪問客戶端的唯一標識 dicSocket.Add(clientName, socConnection); //將客戶端名字和套接字添加到添加到數據字典中 //創建通信線程 ParameterizedThreadStart pts = new ParameterizedThreadStart(ServerRecMsg); Thread thread = new Thread(pts); thread.IsBackground = true; //啟動線程 thread.Start(socConnection); txtMsg.AppendText("IP: " + clientIP + " Port: " + clientPort + " 的客戶端與您連接成功,現在你們可以開始通信了...\r\n"); } }
服務端向客戶端發送信息,在沒有選擇具體某個客戶端的情況下,默認群發. 如果選擇了具體某個客戶端,則單獨向該客戶端發送信息.
/// <summary> /// 發送信息到客戶端的方法 /// </summary> /// <param name="sendMsg">發送的字符串信息</param> private void ServerSendMsg(string sendMsg) { sendMsg = txtSendMsg.Text.Trim(); //將輸入的字符串轉換成 機器可以識別的字節數組 byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendMsg); //向客戶端列表選中的客戶端發送信息 if (!string.IsNullOrEmpty(lstClients.Text.Trim())) { //獲得相應的套接字 並將字節數組信息發送出去 dicSocket[lstClients.Text.Trim()].Send(arrSendMsg); //通過Socket的send方法將字節數組發送出去 txtMsg.AppendText("您在 " + GetCurrentTime() + " 向 IP: " + clientIP + " Port: " + clientPort + " 的客戶端發送了:\r\n" + sendMsg + "\r\n"); } else //如果未選擇任何客戶端 則默認為群發信息 { //遍歷所有的客戶端 for (int i = 0; i < lstClients.Items.Count; i++) { dicSocket[lstClients.Items[i].ToString()].Send(arrSendMsg); } txtMsg.AppendText("您在 " + GetCurrentTime() + " 群發了信息:\r\n" + sendMsg + " \r\n"); } }
運行程序
1.首先啟動服務端並連接各個客戶端
2.服務端向所有客戶端群發信息
3.向指定的客戶端發送信息
這樣,服務端向客戶端群發信息的功能就做好了.如果大家感興趣,可以結合第2篇-文件發送 來做個服務端向客戶端群發文件的功能 :)
附上源代碼
服務端ChatServer3.zip 客戶端 ChatClient3.zip