参考自:http://blog.csdn.net/liguo9860/article/details/6148614
服务端:
1 #region 属性 2 3 //请求的客户端连接 4 Socket clientsocket; 5 //当前连接集合 6 List<Client> clients; 7 //请求客户端线程 8 Thread clientservice; 9 //服务器监听线程 10 Thread threadListen; 11 //服务器监听连接 12 TcpListener listener; 13 //本机ip地址 14 IPAddress currentIp; 15 //端口号 16 Int32 currentPort; 17 18 public delegate void MyInvoke(string str); 19 20 #endregion 21 22 #region 自定义事件 23 24 /// <summary> 25 /// 开始监听 26 /// </summary> 27 public void StartListening() 28 { 29 //本机ip 30 currentIp = GetIPAddress(); 31 //使用端口号 32 currentPort = int.Parse(nudPort.Value.ToString()); 33 //定义网络连接监听 34 listener = new TcpListener(currentIp, currentPort); 35 listener.Start(); 36 while (true) 37 { 38 try 39 { 40 //开始监听网络连接 41 clientsocket = listener.AcceptSocket(); 42 //客户端连接线程 43 clientservice = new Thread(new ThreadStart(ServiceClient)); 44 clientservice.IsBackground = true; 45 //启用线程 46 clientservice.Start(); 47 } 48 catch (Exception ex) 49 { 50 //创建设置状态文本线程 51 Thread handlerControl = new Thread(delegate() { setErrorText(ex.Message); }); 52 //启动线程 53 handlerControl.Start(); 54 } 55 } 56 } 57 58 /// <summary> 59 /// 客户端服务 60 /// </summary> 61 public void ServiceClient() 62 { 63 //获取一个客户端连接 64 Socket client = clientsocket; 65 //用于标识连接是否存活 66 Boolean keepalive = true; 67 //判断新连接是否存在 68 if (client == null) 69 { 70 keepalive = false; 71 } 72 else 73 { 74 keepalive = true; 75 } 76 77 //客户端连接存活 78 while (keepalive) 79 { 80 //数据字节 81 Byte[] buffer = new Byte[1024]; 82 //数据量 83 int bufLen = 0; 84 try 85 { 86 //获取从客户端获取的数据量 87 bufLen = client.Available; 88 //无数据,再监听 89 if (bufLen < 1) 90 { 91 continue; 92 } 93 94 //有数据,接受客户端发送的数据 95 client.Receive(buffer, 0, bufLen, SocketFlags.None); 96 97 //将字节数组转为字符串 98 string infoStr = Encoding.UTF8.GetString(buffer).Substring(0, bufLen).Trim(new char[] { ‘\r‘, ‘\0‘ }); 99 100 //显示在消息框 101 setText(infoStr); 102 103 //分割传入的信息 104 string[] infoArr = infoStr.Split(‘|‘); 105 //消息类型 106 string type = infoArr[0]; 107 108 //CONN:表示新建连接。发送格式:CONN|发送者名称 109 if (type == "CONN") 110 { 111 //是否重复 112 bool isRepeat = false; 113 //循环所有在线看账号是否重复 114 foreach (Client item in clients) 115 { 116 if (infoArr[1].Trim() == item.Name.Trim()) 117 { 118 //如果还在连接状态 119 if (client.Connected) 120 { 121 //发送消息 122 sendToClient(new Client("", null, null, client), "ERROR|账号重复"); 123 isRepeat = true; 124 } 125 break; 126 } 127 } 128 129 //重复了,重新监听 130 if (isRepeat) 131 { 132 keepalive = false; 133 continue; 134 } 135 136 //循环所有在线连接 137 foreach (Client item in clients) 138 { 139 //向所有在线连接发送当前连接加入消息 140 sendToClient(item, "JOIN|" + infoArr[1]); 141 } 142 143 //新连接的网络地址 144 EndPoint ep = client.RemoteEndPoint; 145 //新建一个客户端连接 146 Client newCl = new Client(infoArr[1], ep, clientservice, client); 147 //添加到在线列表中 148 clients.Add(newCl); 149 150 //向新连接发送在线列表 151 sendToClient(newCl, "LIST|" + getChatterList()); 152 153 //将新连接添加到服务器当前连接列表中 154 new Thread(delegate() { handerListBox(string.Format("{0}|{1}", newCl.Name, newCl.ClHost.ToString()), "add"); }).Start(); 155 } 156 157 //CHAT:发送全局消息,所有其他连接都可以接收到。发送格式:CHAT|发送者名称|信息内容 158 if (type == "CHAT") 159 { 160 //找出所有在线连接并发送消息 161 foreach (Client item in clients) 162 { 163 //发送消息 164 sendToClient(item, infoStr); 165 } 166 } 167 168 //PRIV:发送私聊信息。发送格式:PRIV|发送者名称|信息内容|接受者名称 169 if (type == "PRIV") 170 { 171 //找出接受者和发送者发送消息 172 foreach (Client item in clients) 173 { 174 //如果是接受者或者是发送者 175 if (item.Name == infoArr[1] || item.Name == infoArr[3]) 176 { 177 //发送消息 178 sendToClient(item, infoStr); 179 } 180 } 181 } 182 183 //GONE 退出。发送格式:GONE|发送者名称 184 if (type == "GONE") 185 { 186 //是否存在 187 bool found = false; 188 //连接名称 189 string clientname = ""; 190 //在在线集合中索引 191 int index = 0; 192 int i = 0; 193 194 //循环所有连接 195 foreach (Client item in clients) 196 { 197 //如果是退出者 198 if (item.Name == infoArr[1]) 199 { 200 //发送确认退出信息 201 sendToClient(item, "QUIT|"); 202 //获取连接名称 203 clientname = item.Name + "|" + item.ClHost.ToString(); 204 found = true; 205 index = i; 206 } 207 //不是退出者 208 else 209 { 210 //发送消息 211 sendToClient(item, infoStr); 212 } 213 i++; 214 } 215 216 //已退出 217 if (found) 218 { 219 //创建从服务器在线列表移除退出连接线程 220 new Thread(delegate() { delItem(clientname); }).Start(); 221 //从集合中删除连接 222 clients.RemoveAt(index); 223 //改变存活状态 224 keepalive = false; 225 } 226 } 227 228 } 229 catch (Exception ex) 230 { 231 //创建设置状态文本线程 232 new Thread(delegate() { setErrorText(ex.Message); }).Start(); 233 keepalive = false; 234 } 235 } 236 } 237 238 /// <summary> 239 /// 发送消息到客户端 240 /// </summary> 241 /// <param name="cl">客户端</param> 242 /// <param name="msg">要发送的消息,其中包含了消息类型</param> 243 private void sendToClient(Client cl, string msg) 244 { 245 //将消息转为字节数组 246 Byte[] byMsg = Encoding.UTF8.GetBytes(msg); 247 //获取客户端连接 248 Socket clSocket = cl.ClSock; 249 //如果还在连接状态 250 if (clSocket.Connected) 251 { 252 //发送消息 253 clSocket.Send(byMsg, byMsg.Length, 0); 254 } 255 } 256 257 /// <summary> 258 /// 输出信息 259 /// </summary> 260 /// <param name="msg">信息文本</param> 261 private void setText(string msg) 262 { 263 //不是当前线程调用 264 if (rtbInfo.InvokeRequired) 265 { 266 MyInvoke _myInvoke = new MyInvoke(setText); 267 Invoke(_myInvoke, new object[] { msg }); 268 } 269 else 270 { 271 rtbInfo.AppendText(string.Format("\n{0} {1}", msg, DateTime.Now)); 272 } 273 } 274 275 /// <summary> 276 /// 输出错误信息 277 /// </summary> 278 /// <param name="msg">信息文本</param> 279 private void setErrorText(string msg) 280 { 281 //不是当前线程调用 282 if (rtbInfo.InvokeRequired) 283 { 284 MyInvoke _myInvoke = new MyInvoke(setErrorText); 285 Invoke(_myInvoke, new object[] { msg }); 286 } 287 else 288 { 289 rtbError.AppendText(string.Format("\n异常:{0} {1}", msg, DateTime.Now)); 290 } 291 } 292 293 /// <summary> 294 /// 添加或删除当前连接客户端 295 /// </summary> 296 /// <param name="c">客户端信息</param> 297 /// <param name="o">操作</param> 298 private void handerListBox(string c, string o) 299 { 300 //如果是其他线程调用 301 if (lbClients.InvokeRequired) 302 { 303 //委托 304 MyInvoke _myInvoke; 305 //要执行的操作 306 if (o == "add") 307 { 308 _myInvoke = new MyInvoke(addItem); 309 } 310 else 311 { 312 _myInvoke = new MyInvoke(delItem); 313 } 314 Invoke(_myInvoke, new object[] { c }); 315 } 316 } 317 318 /// <summary> 319 /// 在当前连接列表中删除一个连接 320 /// </summary> 321 /// <param name="c"></param> 322 private void delItem(string c) 323 { 324 this.lbClients.Items.Remove(c); 325 } 326 327 /// <summary> 328 /// 在当前连接列表中新建一个连接 329 /// </summary> 330 /// <param name="c"></param> 331 private void addItem(string c) 332 { 333 this.lbClients.Items.Add(c); 334 } 335 336 /// <summary> 337 /// 获取当前连接集合 338 /// </summary> 339 /// <returns></returns> 340 private string getChatterList() 341 { 342 StringBuilder sb = new StringBuilder(); 343 foreach (Client item in clients) 344 { 345 sb.Append(item.Name); 346 sb.Append("|"); 347 } 348 349 return sb.Length > 0 ? sb.ToString().TrimEnd(‘|‘) : ""; 350 } 351 352 /// <summary> 353 /// 获取本机ip 354 /// </summary> 355 /// <returns></returns> 356 private IPAddress GetIPAddress() 357 { 358 String hostName = Dns.GetHostName(); 359 IPAddress[] myIP = Dns.GetHostAddresses(hostName); 360 foreach (IPAddress address in myIP) 361 { 362 if (address.AddressFamily.ToString() == "InterNetwork") 363 { 364 return address; 365 } 366 } 367 return IPAddress.None; 368 } 369 370 /// <summary> 371 /// 端口是否被占用 372 /// </summary> 373 /// <param name="port"></param> 374 /// <returns></returns> 375 private static bool portInUse(int port) 376 { 377 bool inUse = false; 378 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); 379 IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); 380 381 foreach (IPEndPoint endPoint in ipEndPoints) 382 { 383 if (endPoint.Port == port) 384 { 385 inUse = true; 386 break; 387 } 388 } 389 return inUse; 390 } 391 392 #endregion 393 394 #region 页面Load事件 395 396 private void ServerForm_Load(object sender, EventArgs e) 397 { 398 //实例化当前连接集合 399 clients = new List<Client>(); 400 lblAdress.Text = GetIPAddress().ToString(); 401 } 402 403 #endregion 404 405 #region 页面控件事件 406 private void btnStart_Click(object sender, EventArgs e) 407 { 408 try { int.Parse(nudPort.Value.ToString()); } 409 catch { MessageBox.Show("端口号错误!"); return; } 410 if (portInUse(int.Parse(nudPort.Value.ToString()))) 411 { 412 MessageBox.Show("该端口号已被占用!"); 413 return; 414 } 415 416 btnStart.Enabled = false; 417 try 418 { 419 //创建监听线程 420 threadListen = new Thread(StartListening); 421 threadListen.IsBackground = true; 422 //启动线程 423 threadListen.Start(); 424 //创建设置状态文本线程 425 new Thread(delegate() { setText("服务器正在监听..."); }).Start(); 426 lblRunTime.Text = DateTime.Now.ToString(); 427 } 428 catch (Exception ex) 429 { 430 //创建设置状态文本线程 431 new Thread(delegate() { setErrorText(ex.Message); }).Start(); 432 } 433 } 434 435 private void ServerForm_FormClosed(object sender, FormClosedEventArgs e) 436 { 437 if (clients != null) clients.Clear(); 438 if (listener != null) listener.Stop(); 439 if (threadListen != null && threadListen.IsAlive) threadListen.Abort(); 440 GC.Collect(); 441 GC.WaitForPendingFinalizers(); 442 } 443 444 private void rtbInfo_TextChanged(object sender, EventArgs e) 445 { 446 rtbInfo.ScrollToCaret(); 447 } 448 449 private void rtbError_TextChanged(object sender, EventArgs e) 450 { 451 rtbError.ScrollToCaret(); 452 } 453 454 #endregion
客户端:
1 #region 属性 2 3 //NetWorkStream 4 NetworkStream ns; 5 //数据流 6 StreamReader sr; 7 //客户端连接 8 TcpClient clientsocket; 9 //是否连接 10 Boolean connected; 11 //接受信息线程 12 Thread receive; 13 //服务器网络地址 14 String serveraddress = "192.168.1.45"; 15 //服务器端口号 16 Int32 serverport = 8888; 17 //客户端连接名称 18 String clientname; 19 //实现操纵其他线程委托 20 public delegate void MyInvoke(string c); 21 22 #endregion 23 24 #region 自定义事件 25 26 /// <summary> 27 /// 连接到服务器(连接端口) 28 /// </summary> 29 public void EstablishConnection() 30 { 31 //设置状态栏 32 setState("正在连接到服务器..."); 33 try 34 { 35 //根据网络地址和端口号获取服务器连接 36 clientsocket = new TcpClient(serveraddress, serverport); 37 //获取发送和接受数据的NetWorkStream 38 ns = clientsocket.GetStream(); 39 //为NetworkStream 初始化StreamReader实例 40 sr = new StreamReader(ns); 41 //连接成功 42 connected = true; 43 } 44 catch (Exception ex) 45 { 46 setErrorText(ex.Message); 47 setState("连接出错,连接已断开!"); 48 } 49 } 50 51 /// <summary> 52 /// 注册到服务器(注册数据) 53 /// </summary> 54 public void RegisterWithServer() 55 { 56 //获取用户名 57 clientname = txtAccount.Text.Trim(); 58 //清空当前连接列表 59 lbClients.Items.Clear(); 60 61 try 62 { 63 //消息内容 64 string command = "CONN|" + clientname; 65 //转为字节数组 66 Byte[] byMsg = Encoding.UTF8.GetBytes(command.ToCharArray()); 67 //写入流 68 ns.Write(byMsg, 0, byMsg.Length); 69 //接受消息数组 70 Byte[] byInfo = new Byte[1024]; 71 //读取数据到数组 72 ns.Read(byInfo, 0, byInfo.Length); 73 //转为字符串 74 string returnInfo = Encoding.UTF8.GetString(byInfo).Trim(new char[] { ‘\0‘, ‘\r‘ }).TrimEnd(‘|‘); 75 //分割出信息 76 string[] infoArr = returnInfo.Split(‘|‘); 77 //LIST:返回所有连接到服务器的客户端连接。发送格式:LIST|客户端名称|客户端名称|... 78 if (infoArr[0] == "LIST") 79 { 80 //加入连接列表 81 for (int i = 1; i < infoArr.Length; i++) 82 { 83 //将信息逐条添加到列表中 84 lbClients.Items.Add(infoArr[i].Trim()); 85 } 86 //改变状态栏信息 87 setState(string.Format("{0}已连接到服务器!", clientname)); 88 89 //改变页面控件状态 90 txtAccount.Enabled = false; 91 btnLink.Enabled = false; 92 btnSendMsg.Enabled = true; 93 } 94 95 //ERROR:错误消息 96 if (infoArr[0] == "ERROR") 97 { 98 //断开连接 99 connected = false; 100 //改变页面控件状态 101 txtAccount.Enabled = true; 102 btnLink.Enabled = true; 103 btnSendMsg.Enabled = false; 104 throw new Exception(infoArr[1]); 105 } 106 } 107 catch (Exception ex) 108 { 109 setErrorText(ex.Message); 110 setState("注册出错,连接已断开!"); 111 connected = false; 112 } 113 } 114 115 /// <summary> 116 /// 接受服务器消息 117 /// </summary> 118 public void ReceiveChat() 119 { 120 //保持连接状态 121 bool keepalive = true; 122 while (keepalive) 123 { 124 try 125 { 126 //接受消息数组 127 Byte[] byInfo = new Byte[1024]; 128 //读取数据到数组 129 ns.Read(byInfo, 0, byInfo.Length); 130 //转为字符串 131 string infoStr = Encoding.UTF8.GetString(byInfo).Trim(new char[] { ‘\r‘, ‘\0‘ }).TrimEnd(‘|‘); 132 //分割 133 string[] infoArr = infoStr.Split(‘|‘); 134 //消息类型 135 string type = infoArr[0]; 136 137 //CHAT:是否为群发消息 138 if (type == "CHAT") 139 { 140 //显示消息 141 new Thread(delegate() 142 { 143 setText(string.Format("{0}:{1}", infoArr[1], infoArr[2].Trim())); 144 }).Start(); 145 } 146 147 //PRIV:是否为私聊消息 148 if (type == "PRIV") 149 { 150 //在消息面板上显示:信息来自 151 new Thread(delegate() 152 { 153 setText(string.Format("来自【{0}】的私信:{1}", infoArr[1], infoArr[2].Trim())); 154 }).Start(); 155 } 156 157 //JOIN:是否添加新连接消息 158 if (type == "JOIN") 159 { 160 //显示加入消息并添加到当前在线连接 161 new Thread(delegate() 162 { 163 setText(string.Format("【{0}】加入房间!", infoArr[1].Trim())); 164 handerListBox(infoArr[1].Trim(new char[] { ‘\r‘, ‘\n‘ }), "add"); 165 }).Start(); 166 } 167 168 //GONE:是否为退出消息 169 if (type == "GONE") 170 { 171 //显示加入消息并添加到当前在线连接 172 new Thread(delegate() 173 { 174 setText(string.Format("【{0}】离开房间!", infoArr[1].Trim())); 175 handerListBox(infoArr[1].Trim(), "del"); 176 }).Start(); 177 } 178 179 //QUIT:此客户端退出 180 if (type == "QUIT") 181 { 182 //停止接收消息 183 keepalive = false; 184 //提示消息线程 185 new Thread(delegate() { setState("连接已断开"); }).Start(); 186 //断开连接 187 connected = false; 188 } 189 } 190 catch (Exception ex) 191 { 192 setErrorText(ex.Message); 193 keepalive = false; 194 } 195 } 196 197 //判断线程是否为活动的 198 if (!connected && receive != null && receive.IsAlive) 199 //如果是则中止线程 200 receive.Abort(); 201 } 202 203 /// <summary> 204 /// 退出服务器端 205 /// </summary> 206 public void QuitChat() 207 { 208 //判断是否为连接状态 209 if (connected) 210 { 211 try 212 { 213 //创建消息 214 string command = "GONE|" + clientname; 215 //将消息转化为字节流 216 Byte[] outbytes = Encoding.UTF8.GetBytes(command.ToCharArray()); 217 //将消息发送给NetWorkStream 218 ns.Write(outbytes, 0, outbytes.Length); 219 } 220 catch (Exception e) 221 { 222 setErrorText(e.Message); 223 } 224 } 225 if (receive != null && receive.IsAlive) receive.Abort(); 226 if (ns != null) { ns.Close(); ns.Dispose(); } 227 if (sr != null) { sr.Close(); sr.Dispose(); } 228 if (clientsocket != null) clientsocket.Close(); 229 GC.Collect(); 230 GC.WaitForPendingFinalizers(); 231 } 232 233 /// <summary> 234 /// 操作当前连接列表 235 /// </summary> 236 /// <param name="c">客户端信息</param> 237 /// <param name="o">操作</param> 238 private void handerListBox(string c, string o) 239 { 240 //判断是否是创建控件的线程调用 241 if (this.lbClients.InvokeRequired) 242 { 243 //创建委托 244 MyInvoke invoke; 245 if (o == "add") 246 invoke = new MyInvoke(addItem); 247 else 248 invoke = new MyInvoke(delItem); 249 //执行委托 250 Invoke(invoke, new object[] { c }); 251 } 252 } 253 /// <summary> 254 /// 添加连接 255 /// </summary> 256 /// <param name="c"></param> 257 private void addItem(string c) 258 { 259 this.lbClients.Items.Add(c); 260 } 261 /// <summary> 262 /// 删除连接 263 /// </summary> 264 /// <param name="c"></param> 265 private void delItem(string c) 266 { 267 this.lbClients.Items.Remove(c); 268 } 269 270 /// <summary> 271 /// 输出信息 272 /// </summary> 273 /// <param name="msg">信息文本</param> 274 private void setText(string msg) 275 { 276 //不是当前线程调用 277 if (rtbInfo.InvokeRequired) 278 { 279 MyInvoke _myInvoke = new MyInvoke(setText); 280 Invoke(_myInvoke, msg); 281 } 282 else 283 { 284 rtbInfo.AppendText(string.Format("\n{0} {1}", msg, DateTime.Now)); 285 } 286 } 287 288 /// <summary> 289 /// 输出错误信息 290 /// </summary> 291 /// <param name="msg">信息文本</param> 292 private void setErrorText(string msg) 293 { 294 //不是当前线程调用 295 if (rtbInfo.InvokeRequired) 296 { 297 MyInvoke _myInvoke = new MyInvoke(setErrorText); 298 Invoke(_myInvoke, new object[] { msg }); 299 } 300 else 301 { 302 rtbError.AppendText(string.Format("\n异常:{0} {1}", msg, DateTime.Now)); 303 } 304 } 305 306 /// <summary> 307 /// 设置状态栏信息 308 /// </summary> 309 /// <param name="c"></param> 310 private void setState(string c) 311 { 312 //判断是否是创建控件的线程调用 313 if (this.ssState.InvokeRequired) 314 { 315 MyInvoke invoke = new MyInvoke(setState); 316 this.Invoke(invoke, new object[] { c }); 317 } 318 else 319 { 320 this.tsslInfo.Text = c; 321 } 322 } 323 324 /// <summary> 325 /// 判断此用户是否存在 326 /// </summary> 327 /// <param name="name"></param> 328 /// <returns></returns> 329 private bool nameIsHave(string name) 330 { 331 for (int i = 0; i < lbClients.Items.Count; i++) 332 { 333 if (name == lbClients.Items[i].ToString()) 334 { 335 return true; 336 } 337 } 338 return false; 339 } 340 341 #endregion 342 343 #region 页面Load事件 344 345 private void ClientForm_Load(object sender, EventArgs e) 346 { 347 btnSendMsg.Enabled = false; 348 txtIp.ReadOnly = true; 349 } 350 351 #endregion 352 353 #region 页面控件事件 354 355 /// <summary> 356 /// 连接服务器 357 /// </summary> 358 /// <param name="sender"></param> 359 /// <param name="e"></param> 360 private void btnLink_Click(object sender, EventArgs e) 361 { 362 if (string.IsNullOrEmpty(txtAccount.Text.Trim())) 363 { 364 MessageBox.Show("请填写你的聊天用户名!"); 365 return; 366 } 367 368 try { int.Parse(nudPort.Value.ToString()); } 369 catch { MessageBox.Show("端口号错误!"); return; } 370 371 serveraddress = txtIp.Text.Trim(); 372 serverport = int.Parse(nudPort.Value.ToString()); 373 374 //连接服务器 375 EstablishConnection(); 376 //注册 377 RegisterWithServer(); 378 379 //连接成功 380 if (connected) 381 { 382 //接受信息线程 383 receive = new Thread(new ThreadStart(ReceiveChat)); 384 //启动线程 385 receive.Start(); 386 } 387 } 388 389 /// <summary> 390 /// 发送消息 391 /// </summary> 392 /// <param name="sender"></param> 393 /// <param name="e"></param> 394 private void btnSendMsg_Click(object sender, EventArgs e) 395 { 396 if (string.IsNullOrEmpty(txtMsg.Text.Trim())) 397 { 398 MessageBox.Show("消息内容不能为空!"); 399 return; 400 } 401 //连接成功 402 if (connected) 403 { 404 try 405 { 406 string command = txtMsg.Text.Trim(); 407 408 //私聊名开始结束位置 409 int start = command.IndexOf(‘@‘); 410 int end = command.LastIndexOf(‘@‘); 411 //私聊名 412 string rName = (start != -1 && end != -1) ? command.Substring(start + 1, end - 1) : ""; 413 //有@并且名称在系统中 为私聊 414 if (!string.IsNullOrEmpty(rName) && nameIsHave(rName)) 415 { 416 //创建私聊 417 command = string.Format("PRIV|{0}|{1}|{2}", clientname, command.Substring(end + 1, command.Length - end-1), rName); 418 } 419 else 420 { 421 //创建群发信息 422 command = string.Format("CHAT|{0}|{1}", clientname, command); 423 } 424 425 //转化为字节流 426 Byte[] outbytes = Encoding.UTF8.GetBytes(command.ToCharArray()); 427 //发送信息 428 ns.Write(outbytes, 0, outbytes.Length); 429 txtMsg.Text = ""; 430 } 431 catch (Exception ex) 432 { 433 setErrorText(ex.Message); 434 } 435 } 436 } 437 438 /// <summary> 439 /// 退出 440 /// </summary> 441 /// <param name="sender"></param> 442 /// <param name="e"></param> 443 private void ClientForm_FormClosed(object sender, FormClosedEventArgs e) 444 { 445 QuitChat(); 446 } 447 448 private void rtbInfo_TextChanged(object sender, EventArgs e) 449 { 450 rtbInfo.ScrollToCaret(); 451 } 452 453 private void rtbError_TextChanged(object sender, EventArgs e) 454 { 455 rtbError.ScrollToCaret(); 456 } 457 458 #endregion
图例
时间: 2024-10-23 13:31:36