效果 實現思路 使用TcpListener建一個伺服器,接收所有客戶端發送的消息,然後由伺服器再發送到其他客戶端 客戶端使用TcpClient,發消息給伺服器,接收伺服器的消息,不和其他客戶端直接交互 伺服器端 開啟一個線程,死迴圈去接收客戶端.接收到之後放到一個集合里,保存起來,以便轉發消息用.每 ...
效果
實現思路
使用TcpListener建一個伺服器,接收所有客戶端發送的消息,然後由伺服器再發送到其他客戶端
客戶端使用TcpClient,發消息給伺服器,接收伺服器的消息,不和其他客戶端直接交互
伺服器端
接收客戶端
開啟一個線程,死迴圈去接收客戶端.接收到之後放到一個集合里,保存起來,以便轉發消息用.每個客戶端都再開啟一個線程,用於接收這個客戶端發送的消息.
接收客戶端的方法AcceptTcpClient()是阻塞方法,在程式退出釋放資源時會引發異常,可以先使用Pending()方法先判斷是否有掛起的鏈接請求,有請求的話再去接收.這樣可以避免退出時引發的異常.
這裡取每隔1秒接收一次.
/// <summary> /// 接收客戶端 /// </summary> private void AcceptClient() { try { while (_isAccept) { if (_listener.Pending()) { TcpClient client = _listener.AcceptTcpClient(); IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint; _clients.Add(endpoint.ToString(), client); //添加到前端客戶端列表 lbx_Clients.Dispatcher.Invoke(() => { lbx_Clients.Items.Add(endpoint.ToString()); }); //接收消息線程 Thread reciveMessageThread = new Thread(ReciveMessage); reciveMessageThread.Start(client); } else { Thread.Sleep(1000); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } }
接收消息並轉發
也是死迴圈接收,使用Read()方法接收.如果遠程主機已關閉連接,Read()將立即返回零位元組.此時跳出迴圈,釋放資源,結束此線程.
/// <summary> /// 接收消息 /// </summary> /// <param name="obj">TcpClient</param> private void ReciveMessage(object obj) { TcpClient client = obj as TcpClient; IPEndPoint endpoint = null; NetworkStream stream = null; try { endpoint = client.Client.RemoteEndPoint as IPEndPoint; stream = client.GetStream(); while (true) { byte[] data = new byte[1024]; //如果遠程主機已關閉連接,Read將立即返回零位元組 int length = stream.Read(data, 0, data.Length); if (length > 0) { #region if string msg = Encoding.UTF8.GetString(data, 0, length); //添加到前端消息列表 lbx_Messages.Dispatcher.Invoke(() => { lbx_Messages.Items.Add(string.Format("{0}:{1}", endpoint.ToString(), msg)); }); //發送到其他客戶端 foreach (KeyValuePair<string, TcpClient> kvp in _clients) { if (kvp.Value != client) { string writeMsg = string.Format("{0}:{1}", endpoint.ToString(), msg); byte[] writeData = Encoding.UTF8.GetBytes(writeMsg); NetworkStream writeStream = kvp.Value.GetStream(); writeStream.Write(writeData, 0, writeData.Length); } } #endregion } else { //客戶端斷開連接 跳出迴圈 break; } } } catch (Exception ex) { //Read是阻塞方法 客戶端退出是會引發異常 釋放資源 結束此線程 } finally { //從前端客戶端列表移除 lbx_Clients.Dispatcher.Invoke(() => { lbx_Clients.Items.Remove(endpoint.ToString()); }); //釋放資源 stream.Dispose(); _clients.Remove(endpoint.ToString()); client.Dispose(); } }
客戶端
接收消息
開啟線程,死迴圈接收伺服器發送的消息.如果Read()返回0,說明伺服器已關閉.
/// <summary> /// 接收消息 /// </summary> private void ReciveMessage() { try { NetworkStream stream = _client.GetStream(); while (true) { byte[] data = new byte[1024]; int length = stream.Read(data, 0, data.Length); if (length > 0) { string msg = Encoding.UTF8.GetString(data, 0, length); //添加到前端消息列表 lbx_Messages.Dispatcher.Invoke(() => { lbx_Messages.Items.Add(msg); }); } else { MessageBox.Show("伺服器已關閉"); stream.Dispose(); break; } } } catch (Exception ex) { //Read是阻塞方法 程式退出釋放資源是會引發異常 不做處理 線程結束 } }
源碼下載:
伺服器端:SocketServerDemo.zip