委托实现TCP异步聊天
User:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.IO; namespace AsyncTcpServer { class User { public TcpClient client { get; private set; } public BinaryReader br { get; private set; } public BinaryWriter bw { get; private set; } public string userName { get; set; } public User(TcpClient client) { this.client = client; NetworkStream networkStream = client.GetStream(); br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); } public void Close() { br.Close(); bw.Close(); client.Close(); } } }
Server:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.Threading; namespace AsyncTcpServer { public partial class FormServer : Form { /// <summary> /// 保存连接的所有用户 /// </summary> private List<User> userList = new List<User>(); /// <summary> /// 使用的本机IP地址 /// </summary> IPAddress localAddress = IPAddress.Parse("192.168.1.102"); /// <summary> /// 监听端口 /// </summary> private const int port = 8889; private TcpListener myListener; /// <summary> /// 是否正常退出所有接收线程 /// </summary> bool isExit = false; public FormServer() { InitializeComponent(); lst_Status.HorizontalScrollbar = true; btn_Stop.Enabled = false; } private void btn_Start_Click(object sender, EventArgs e) { myListener = new TcpListener(localAddress, port); myListener.Start(); AddItemToListBox(string.Format("开始在{0}:{1}监听客户端", localAddress, port)); Thread myThread = new Thread(ListenClientConnect); myThread.Start(); btn_Start.Enabled = false; btn_Stop.Enabled = true; } /// <summary> /// 监听客户端请求 /// </summary> private void ListenClientConnect() { TcpClient newClient = null; while (true) { ListenClientDelegate d = new ListenClientDelegate(ListenClient); IAsyncResult result = d.BeginInvoke(out newClient, null, null); //使用轮询方式来判断异步操作是否完成 while (result.IsCompleted == false) { if (isExit) break; Thread.Sleep(250); } //获取Begin 方法的返回值和所有输入/输出参数 d.EndInvoke(out newClient, result); if (newClient != null) { //每接受一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息 User user = new User(newClient); Thread threadReceive = new Thread(ReceiveData); threadReceive.Start(user); userList.Add(user); AddItemToListBox(string.Format("[{0}]进入", newClient.Client.RemoteEndPoint)); AddItemToListBox(string.Format("当前连接用户数:{0}", userList.Count)); } else { break; } } } private void ReceiveData(object userState) { User user = (User)userState; TcpClient client = user.client; while (!isExit) { string receiveString = null; ReceiveMessageDelegate d = new ReceiveMessageDelegate(ReceiveMessage); IAsyncResult result = d.BeginInvoke(user, out receiveString, null, null); //使用轮询方式来判断异步操作是否完成 while (!result.IsCompleted) { if (isExit) break; Thread.Sleep(250); } //获取Begin方法的返回值和所有输入/输出参数 d.EndInvoke(out receiveString, result); if (receiveString == null) { if (!isExit) { AddItemToListBox(string.Format("与{0}失去联系,已终止接收该用户信息", client.Client.RemoteEndPoint)); RemoveUser(user); } break; } AddItemToListBox(string.Format("来自[{0}]:{1}", user.client.Client.RemoteEndPoint, receiveString)); string[] splitString = receiveString.Split(','); switch (splitString[0]) { case "Login": user.userName = splitString[1]; AsyncSendToAllClient(user, receiveString); break; case "Logout": AsyncSendToAllClient(user, receiveString); RemoveUser(user); return; case "Talk": string talkString = receiveString.Substring(splitString[0].Length + splitString[1].Length + 2); AddItemToListBox(string.Format("{0}对{1}说:{2}", user.userName, splitString[1], talkString)); foreach (User target in userList) { if (target.userName == splitString[1]) { AsyncSendToClient(target, "talk," + user.userName + "," + talkString); break; } } break; default: AddItemToListBox("什么意思啊:" + receiveString); break; } } } /// <summary> /// 异步发送信息给所有客户 /// </summary> /// <param name="user"></param> /// <param name="message"></param> private void AsyncSendToAllClient(User user, string message) { string command = message.Split(',')[0].ToLower(); if (command == "login") { for (int i = 0; i < userList.Count; i++) { AsyncSendToClient(userList[i], message); if (userList[i].userName != user.userName) AsyncSendToClient(user, "login," + userList[i].userName); } } else if (command == "logout") { for (int i = 0; i < userList.Count; i++) { if (userList[i].userName != user.userName) AsyncSendToClient(userList[i], message); } } } /// <summary> /// 异步发送message给user /// </summary> /// <param name="user"></param> /// <param name="message"></param> private void AsyncSendToClient(User user, string message) { SendToClientDelegate d = new SendToClientDelegate(SendToClient); IAsyncResult result = d.BeginInvoke(user, message, null, null); while (result.IsCompleted == false) { if (isExit) break; Thread.Sleep(250); } d.EndInvoke(result); } private delegate void SendToClientDelegate(User user, string message); /// <summary> /// 发送message给user /// </summary> /// <param name="user"></param> /// <param name="message"></param> private void SendToClient(User user, string message) { try { //将字符串写入网络流,此方法会自动附加字符串长度前缀 user.bw.Write(message); user.bw.Flush(); AddItemToListBox(string.Format("向[{0}]发送:{1}", user.userName, message)); } catch { AddItemToListBox(string.Format("向[{0}]发送信息失败", user.userName)); } } /// <summary> /// 移除用户 /// </summary> /// <param name="user"></param> private void RemoveUser(User user) { userList.Remove(user); user.Close(); AddItemToListBox(string.Format("当前连接用户数:{0}", userList.Count)); } delegate void ReceiveMessageDelegate(User user, out string receiveMessage); /// <summary> /// 接收客户端发来的信息 /// </summary> /// <param name="user"></param> /// <param name="receiveMessage"></param> private void ReceiveMessage(User user, out string receiveMessage) { try { receiveMessage = user.br.ReadString(); } catch (Exception ex) { AddItemToListBox(ex.Message); receiveMessage = null; } } private delegate void ListenClientDelegate(out TcpClient client); /// <summary> /// 接受挂起的客户端连接请求 /// </summary> /// <param name="newClient"></param> private void ListenClient(out TcpClient newClient) { try { newClient = myListener.AcceptTcpClient(); } catch { newClient = null; } } delegate void AddItemToListBoxDelegate(string str); /// <summary> /// 在ListBox中追加状态信息 /// </summary> /// <param name="str">要追加的信息</param> private void AddItemToListBox(string str) { if (lst_Status.InvokeRequired) { AddItemToListBoxDelegate d = AddItemToListBox; lst_Status.Invoke(d, str); } else { lst_Status.Items.Add(str); lst_Status.SelectedIndex = lst_Status.Items.Count - 1; lst_Status.ClearSelected(); } } private void btn_Stop_Click(object sender, EventArgs e) { AddItemToListBox("开始停止服务,并依次使用户退出!"); isExit = true; for (int i = userList.Count - 1; i >= 0; i--) { RemoveUser(userList[i]); } //通过停止监听让myListener.AcceptTcpClient()产生异常退出监听线程 myListener.Stop(); btn_Start.Enabled = true; btn_Stop.Enabled = false; } private void FormServer_FormClosing(object sender, FormClosingEventArgs e) { if (myListener != null) { btn_Stop.PerformClick(); } } } }
Client:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using System.IO; using System.Net; using System.Threading; namespace AsyncTcpClient { public partial class FormClient : Form { //是否正常退出 private bool isExit = false; private TcpClient client; private BinaryReader br; private BinaryWriter bw; BackgroundWorker connectWork = new BackgroundWorker(); private string serverIP = "192.168.1.102"; public FormClient() { InitializeComponent(); this.StartPosition = FormStartPosition.CenterScreen; Random r = new Random((int)DateTime.Now.Ticks); txt_UserName.Text = "user" + r.Next(100, 999); lst_OnlineUser.HorizontalScrollbar = true; connectWork.DoWork += new DoWorkEventHandler(connectWork_DoWork); connectWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler(connectWork_RunWorkerCompleted); } /// <summary> /// 异步方式与服务器进行连接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void connectWork_DoWork(object sender, DoWorkEventArgs e) { client = new TcpClient(); IAsyncResult result = client.BeginConnect(serverIP, 8889, null, null); while (!result.IsCompleted) { Thread.Sleep(100); AddStatus("."); } try { client.EndConnect(result); e.Result = "success"; } catch (Exception ex) { e.Result = ex.Message; return; } } /// <summary> /// 异步方式与服务器完成连接操作后的处理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void connectWork_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Result.ToString() == "success") { AddStatus("连接成功"); //获取网络流 NetworkStream networkStream = client.GetStream(); //将网络流作为二进制读写对象 br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); AsyncSendMessage("Login," + txt_UserName.Text); Thread threadReceive = new Thread(new ThreadStart(ReceiveData)); threadReceive.IsBackground = true; threadReceive.Start(); } else { AddStatus("连接失败:" + e.Result); btn_Login.Enabled = true; } } private void btn_Login_Click(object sender, EventArgs e) { btn_Login.Enabled = false; AddStatus("开始连接."); connectWork.RunWorkerAsync(); } /// <summary> /// 处理接收的服务器收据 /// </summary> private void ReceiveData() { string receiveString = null; while (!isExit) { ReceiveMessageDelegate d = new ReceiveMessageDelegate(receiveMessage); IAsyncResult result = d.BeginInvoke(out receiveString, null, null); //使用轮询方式来盘点异步操作是否完成 while (!result.IsCompleted) { if (isExit) break; Thread.Sleep(250); } //获取Begin方法的返回值所有输入/输出参数 d.EndInvoke(out receiveString, result); if(receiveString == null) { if(!isExit) MessageBox.Show("与服务器失去联系"); break; } string[] splitString = receiveString.Split(','); string command = splitString[0].ToLower(); switch (command) { case "login": //格式: login,用户名 AddOnline(splitString[1]); break; case "logout": //格式: logout,用户名 RemoveUserName(splitString[1]); break; case "talk": //格式: talk,用户名,对话信息 AddTalkMessage(splitString[1] + ":\r\n"); AddTalkMessage(receiveString.Substring(splitString[0].Length + splitString[1].Length + 2)); break; } } Application.Exit(); } /// <summary> /// 发送信息状态的数据结构 /// </summary> private struct SendMessageStates { public SendMessageDelegate d; public IAsyncResult result; } /// <summary> /// 异步向服务器发送数据 /// </summary> /// <param name="message"></param> private void AsyncSendMessage(string message) { SendMessageDelegate d = new SendMessageDelegate(SendMessage); IAsyncResult result = d.BeginInvoke(message, null, null); while (!result.IsCompleted) { if (isExit) return; Thread.Sleep(50); } SendMessageStates states = new SendMessageStates(); states.d = d; states.result = result; Thread t = new Thread(FinishAsyncSendMessage); t.IsBackground = true; t.Start(states); } /// <summary> /// 处理接收的服务端数据 /// </summary> /// <param name="obj"></param> private void FinishAsyncSendMessage(object obj) { SendMessageStates states = (SendMessageStates)obj; states.d.EndInvoke(states.result); } delegate void SendMessageDelegate(string message); /// <summary> /// 向服务端发送数据 /// </summary> /// <param name="message"></param> private void SendMessage(string message) { try { bw.Write(message); bw.Flush(); } catch { AddStatus("发送失败"); } } private void btn_SendeMessage_Click(object sender, EventArgs e) { if (lst_OnlineUser.SelectedIndex != -1) { AsyncSendMessage("Talk," + lst_OnlineUser.SelectedItem + "," + rtf_SendMessage.Text + "\r\n"); rtf_SendMessage.Clear(); } else MessageBox.Show("请先在[当前在线]中选择一个对话者"); } delegate void ConnectServerDelegate(); /// <summary> /// 连接服务器 /// </summary> private void ConnectServer() { client = new TcpClient(serverIP, 8889); } delegate void ReceiveMessageDelegate(out string receiveMessage); /// <summary> /// 读取服务器发过来的信息 /// </summary> /// <param name="receiveMessage"></param> private void receiveMessage(out string receiveMessage) { receiveMessage = null; try { receiveMessage = br.ReadString(); } catch (Exception ex) { AddStatus(ex.Message); } } private delegate void AddTalkMessageDelegate(string message); /// <summary> /// 向 rtf 中添加聊天记录 /// </summary> /// <param name="message"></param> private void AddTalkMessage(string message) { if (rtf_MessageInfo.InvokeRequired) { AddTalkMessageDelegate d = new AddTalkMessageDelegate(AddTalkMessage); rtf_MessageInfo.Invoke(d, new object[] { message }); } else { rtf_MessageInfo.AppendText(message); rtf_MessageInfo.ScrollToCaret(); } } private delegate void AddStatusDelegate(string message); /// <summary> /// 向 rtf 中添加状态信息 /// </summary> /// <param name="message"></param> private void AddStatus(string message) { if (rtf_StatusInfo.InvokeRequired) { AddStatusDelegate d = new AddStatusDelegate(AddStatus); rtf_StatusInfo.Invoke(d, new object[] { message }); } else { rtf_StatusInfo.AppendText(message); } } private delegate void AddOnlineDelegate(string message); /// <summary> /// 向 lst_Online 添加在线用户 /// </summary> /// <param name="message"></param> private void AddOnline(string message) { if (lst_OnlineUser.InvokeRequired) { AddOnlineDelegate d = new AddOnlineDelegate(AddOnline); lst_OnlineUser.Invoke(d, new object[] { message }); } else { lst_OnlineUser.Items.Add(message); lst_OnlineUser.SelectedIndex = lst_OnlineUser.Items.Count - 1; lst_OnlineUser.ClearSelected(); } } private delegate void RemoveUserNameDelegate(string userName); /// <summary> /// 从 listBoxOnline 删除离线用户 /// </summary> /// <param name="userName"></param> private void RemoveUserName(string userName) { if (lst_OnlineUser.InvokeRequired) { RemoveUserNameDelegate d = RemoveUserName; lst_OnlineUser.Invoke(d, userName); } else { lst_OnlineUser.Items.Remove(userName); lst_OnlineUser.SelectedIndex = lst_OnlineUser.Items.Count - 1; lst_OnlineUser.ClearSelected(); } } private void FormClient_FormClosing(object sender, FormClosingEventArgs e) { if (client != null) { AsyncSendMessage("Logout," + txt_UserName.Text); isExit = true; br.Close(); bw.Close(); client.Close(); } } } }