效果
实现思路
使用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(); } } } 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) { ]; //如果远程主机已关闭连接,Read将立即返回零字节 , data.Length); ) { #region if , 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, , 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) { ]; , data.Length); ) { , length); //添加到前端消息列表 lbx_Messages.Dispatcher.Invoke(() => { lbx_Messages.Items.Add(msg); }); } else { MessageBox.Show("服务器已关闭"); stream.Dispose(); break; } } } catch (Exception ex) { //Read是阻塞方法 程序退出释放资源是会引发异常 不做处理 线程结束 } }
源码下载:
服务器端:SocketServerDemo.zip