参考自: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