首先是服务端:服务端采用控制台程序实现!
using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.Threading; using System.Text.RegularExpressions; using System.Security.Cryptography; namespace socket { class Program { static void Main(string[] args) { new socketSe().Run(); } } public class socketSe { private Dictionary<Socket, ClientInfo> clientPool = new Dictionary<Socket, ClientInfo>(); private List<SocketMessage> msgPool = new List<SocketMessage>(); private bool isClear = true; Socket soke = null; Thread trad = null; public void Run() { trad = new Thread(() => { soke = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ia = IPAddress.Parse("192.168.8.106"); IPEndPoint ie = new IPEndPoint(ia, 8043); soke.Bind(ie); //参数:挂起连接队列的最大长度。 soke.Listen(10); soke.BeginAccept(new AsyncCallback(Accept), soke); }); trad.Start(); Console.WriteLine("服务器已启动"); Broadcast(); } private void Broadcast() { Thread broadcast = new Thread(() => { while (true) { if (!isClear) { byte[] msg = PackageServerData(msgPool[0]); foreach (KeyValuePair<Socket, ClientInfo> cs in clientPool) { Socket client = cs.Key; if (client.Poll(10, SelectMode.SelectWrite)) { client.Send(msg, msg.Length, SocketFlags.None); } } msgPool.RemoveAt(0); isClear = msgPool.Count == 0 ? true : false; } } }); broadcast.Start(); } private void Accept(IAsyncResult result) { Socket server = result.AsyncState as Socket; Socket client = server.EndAccept(result); try { server.BeginAccept(new AsyncCallback(Accept), server); byte[] buffer = new byte[1024]; client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), client); ClientInfo info = new ClientInfo(); info.Id = client.RemoteEndPoint; info.handle = client.Handle; info.buffer = buffer; this.clientPool.Add(client, info); Console.WriteLine(string.Format("客户端 {0} 连接", client.RemoteEndPoint)); } catch (Exception ex) { Console.WriteLine("报错 :\r\n\t" + ex.ToString()); } } private void Recieve(IAsyncResult result) { Socket client = result.AsyncState as Socket; if (client == null || !clientPool.ContainsKey(client)) { return; } try { int length = client.EndReceive(result); byte[] buffer = clientPool[client].buffer; client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), client); string msg = Encoding.UTF8.GetString(buffer, 0, length); if (!clientPool[client].IsHandShaked && msg.Contains("Sec-WebSocket-Key")) { client.Send(PackageHandShakeData(buffer, length)); clientPool[client].IsHandShaked = true; return; } msg = AnalyzeClientData(buffer, length); SocketMessage sm = new SocketMessage(); sm.Client = clientPool[client]; sm.Time = DateTime.Now; Regex reg = new Regex(@"{<(.*?)>}"); Match m = reg.Match(msg); if (m.Value != "") { //处理客户端传来的用户名 clientPool[client].NickName = Regex.Replace(m.Value, @"{<(.*?)>}", "$1"); sm.isLoginMessage = true; sm.Message = "登录"; Console.WriteLine("{0} login @ {1}", client.RemoteEndPoint, DateTime.Now); } else { sm.isLoginMessage = false; sm.Message = msg; Console.WriteLine("{0} @ {1}\r\n {2}", client.RemoteEndPoint, DateTime.Now, sm.Message); } msgPool.Add(sm); isClear = false; } catch { client.Disconnect(true); Console.WriteLine("客户端 {0} 报错", clientPool[client].Name); clientPool.Remove(client); } } private byte[] PackageHandShakeData(byte[] handShakeBytes, int length) { string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length); string key = string.Empty; Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n"); Match m = reg.Match(handShakeText); if (m.Value != "") { key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim(); } byte[] secKeyBytes = SHA1.Create().ComputeHash( Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); string secKey = Convert.ToBase64String(secKeyBytes); var responseBuilder = new StringBuilder(); responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n"); responseBuilder.Append("Upgrade: websocket" + "\r\n"); responseBuilder.Append("Connection: Upgrade" + "\r\n"); responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n"); return Encoding.UTF8.GetBytes(responseBuilder.ToString()); } private string AnalyzeClientData(byte[] recBytes, int length) { if (length < 2) { return string.Empty; } bool fin = (recBytes[0] & 0x80) == 0x80; if (!fin) { return string.Empty; } bool mask_flag = (recBytes[1] & 0x80) == 0x80; if (!mask_flag) { return string.Empty; } int payload_len = recBytes[1] & 0x7F; byte[] masks = new byte[4]; byte[] payload_data; if (payload_len == 126) { Array.Copy(recBytes, 4, masks, 0, 4); payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]); payload_data = new byte[payload_len]; Array.Copy(recBytes, 8, payload_data, 0, payload_len); } else if (payload_len == 127) { Array.Copy(recBytes, 10, masks, 0, 4); byte[] uInt64Bytes = new byte[8]; for (int i = 0; i < 8; i++) { uInt64Bytes[i] = recBytes[9 - i]; } UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0); payload_data = new byte[len]; for (UInt64 i = 0; i < len; i++) { payload_data[i] = recBytes[i + 14]; } } else { Array.Copy(recBytes, 2, masks, 0, 4); payload_data = new byte[payload_len]; Array.Copy(recBytes, 6, payload_data, 0, payload_len); } for (var i = 0; i < payload_len; i++) { payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]); } return Encoding.UTF8.GetString(payload_data); } private byte[] PackageServerData(SocketMessage sm) { StringBuilder msg = new StringBuilder(); if (!sm.isLoginMessage) { msg.AppendFormat("{0} @ {1}:\r\n ", sm.Client.Name, sm.Time.ToShortTimeString()); msg.Append(sm.Message); } else { msg.AppendFormat("{0} 登录 @ {1}", sm.Client.Name, sm.Time.ToShortTimeString()); } byte[] content = null; byte[] temp = Encoding.UTF8.GetBytes(msg.ToString()); if (temp.Length < 126) { content = new byte[temp.Length + 2]; content[0] = 0x81; content[1] = (byte)temp.Length; Array.Copy(temp, 0, content, 2, temp.Length); } else if (temp.Length < 0xFFFF) { content = new byte[temp.Length + 4]; content[0] = 0x81; content[1] = 126; content[2] = (byte)(temp.Length & 0xFF); content[3] = (byte)(temp.Length >> 8 & 0xFF); Array.Copy(temp, 0, content, 4, temp.Length); } else { // 不处理超长内容 } return content; } } public class ClientInfo { public byte[] buffer; public string NickName { get; set; } public EndPoint Id { get; set; } public IntPtr handle { get; set; } public string Name { get { if (!string.IsNullOrEmpty(NickName)) { return NickName; } else { return string.Format("{0}#{1}", Id, handle); } } } public bool IsHandShaked { get; set; } } public class SocketMessage { public bool isLoginMessage { get; set; } public ClientInfo Client { get; set; } public string Message { get; set; } public DateTime Time { get; set; } } }
客户端:使用一个html文件实现即可
<!DOCTYPE html> <html> <head> <title>简简单单的聊天</title> <meta charset="utf-8" /> </head> <body style="padding:10px;"> <h1 style="text-align: center;color: cornflowerblue;">聊天室</h1> <div style="margin:5px 0px;"> <div><input id="name" type="text" value="" placeholder="请输入用户名" style="width:100%;height: 30px; border-radius: 5px;border: 1px green solid;" /></div> </div> <div> <button id="connect" onclick="connect();">连接服务器</button> <button id="disconnect" onclick="quit();">下线</button> <button id="clear" onclick="clearMsg();">清空</button> </div> <div id="message" style="border:solid 1px #333; border-radius: 5px; padding:0px; width:100%; overflow:auto; background-color:#404040; height:300px; margin-bottom:8px; font-size:14px;"> <h5 style="margin:4px 0px; color: white;font-size: 15px;font-family: '微软雅黑'; text-align: center;border-bottom: 1px red dashed;">消息</h5> </div> <input id="text" type="text" onkeypress="enter(event);" placeholder="消息" style="width:100%;height: 30px; border-radius: 5px;border: 1px green solid;" /> <button id="send" onclick="send();" style="background-color: goldenrod;border: none;float: right;height: 30px;width: 50px;margin-top: 5px;border-radius: 5px;">发送</button> <script type="text/javascript"> var name = document.getElementById('name').value; var msgContainer = document.getElementById('message'); var text = document.getElementById('text'); function connect() { ws = new WebSocket("ws://192.168.8.106:8043"); ws.onopen = function (e) { var msg = document.createElement('div'); msg.style.color = '#0f0'; msg.innerHTML = "Server > 已连接,可以聊天了."; msgContainer.appendChild(msg); ws.send('{<' + document.getElementById('name').value + '>}'); }; ws.onmessage = function (e) { var msg = document.createElement('div'); msg.style.color = '#fff'; msg.innerHTML = e.data; msgContainer.appendChild(msg); }; ws.onerror = function (e) { var msg = document.createElement('div'); msg.style.color = '#0f0'; msg.innerHTML = 'Server > ' + e.data; msgContainer.appendChild(msg); }; ws.onclose = function (e) { var msg = document.createElement('div'); msg.style.color = '#0f0'; msg.innerHTML = "Server > 服务器连接不上或已关闭"; msgContainer.appendChild(msg); }; text.focus(); } function quit() { if (ws) { ws.close(); var msg = document.createElement('div'); msg.style.color = '#0f0'; msg.innerHTML = 'Server > 连接关闭.'; msgContainer.appendChild(msg); ws = null; } } function send() { ws.send(text.value); setTimeout(function () { msgContainer.scrollTop = msgContainer.getBoundingClientRect().height; }, 100); text.value = ''; text.focus(); } function clearMsg() { msgContainer.innerHTML = ""; } function enter(event) { if (event.keyCode == 13) { send(); } } </script> </body> </html>
留下您的脚步
最近评论