C# socket異步 服務端
阿新 • • 發佈:2017-09-14
下一個 摘要 move afa nal asc orm cep cor
轉自: http://blog.csdn.net/qq_20282263/article/details/54310737
1 private Dictionary<string, Session> SessionPool = new Dictionary<string, Session>(); 2 private Dictionary<string, string> MsgPool = new Dictionary<string, string>(); 3 4 #region 啟動WebSocket服務 5/// <summary> 6 /// 啟動WebSocket服務 7 /// </summary> 8 public void start(int port) 9 { 10 Socket SockeServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 11 SockeServer.Bind(new IPEndPoint(IPAddress.Any, port));12 SockeServer.Listen(20); 13 SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer); 14 Console.WriteLine("服務已啟動"); 15 Console.WriteLine("按任意鍵關閉服務"); 16 Console.ReadLine(); 17 } 18 #endregion 19 20 #region處理客戶端連接請求 21 /// <summary> 22 /// 處理客戶端連接請求 23 /// </summary> 24 /// <param name="result"></param> 25 private void Accept(IAsyncResult socket) 26 { 27 // 還原傳入的原始套接字 28 Socket SockeServer = (Socket)socket.AsyncState; 29 // 在原始套接字上調用EndAccept方法,返回新的套接字 30 Socket SockeClient = SockeServer.EndAccept(socket); 31 byte[] buffer = new byte[4096]; 32 try 33 { 34 //接收客戶端的數據 35 SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient); 36 //保存登錄的客戶端 37 Session session = new Session(); 38 session.SockeClient = SockeClient; 39 session.IP = SockeClient.RemoteEndPoint.ToString(); 40 session.buffer = buffer; 41 lock (SessionPool) 42 { 43 if (SessionPool.ContainsKey(session.IP)) 44 { 45 this.SessionPool.Remove(session.IP); 46 } 47 this.SessionPool.Add(session.IP, session); 48 } 49 //準備接受下一個客戶端 50 SockeServer.BeginAccept(new AsyncCallback(Accept), SockeServer); 51 Console.WriteLine(string.Format("Client {0} connected", SockeClient.RemoteEndPoint)); 52 } 53 catch (Exception ex) 54 { 55 Console.WriteLine("Error : " + ex.ToString()); 56 } 57 } 58 #endregion 59 60 #region 處理接收的數據 61 /// <summary> 62 /// 處理接受的數據 63 /// </summary> 64 /// <param name="socket"></param> 65 private void Recieve(IAsyncResult socket) 66 { 67 Socket SockeClient = (Socket)socket.AsyncState; 68 string IP = SockeClient.RemoteEndPoint.ToString(); 69 if (SockeClient == null || !SessionPool.ContainsKey(IP)) 70 { 71 return; 72 } 73 try 74 { 75 int length = SockeClient.EndReceive(socket); 76 byte[] buffer = SessionPool[IP].buffer; 77 SockeClient.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient); 78 string msg = Encoding.UTF8.GetString(buffer, 0, length); 79 // websocket建立連接的時候,除了TCP連接的三次握手,websocket協議中客戶端與服務器想建立連接需要一次額外的握手動作 80 if (msg.Contains("Sec-WebSocket-Key")) 81 { 82 SockeClient.Send(PackageHandShakeData(buffer, length)); 83 SessionPool[IP].isWeb = true; 84 return; 85 } 86 if (SessionPool[IP].isWeb) 87 { 88 msg = AnalyzeClientData(buffer, length); 89 } 90 byte[] msgBuffer = PackageServerData(msg); 91 foreach (Session se in SessionPool.Values) 92 { 93 se.SockeClient.Send(msgBuffer, msgBuffer.Length, SocketFlags.None); 94 } 95 } 96 catch 97 { 98 SockeClient.Disconnect(true); 99 Console.WriteLine("客戶端 {0} 斷開連接", IP); 100 SessionPool.Remove(IP); 101 } 102 } 103 #endregion 104 #region 客戶端和服務端的響應 105 /* 106 * 客戶端向服務器發送請求 107 * 108 * GET / HTTP/1.1 109 * Origin: http://localhost:1416 110 * Sec-WebSocket-Key: vDyPp55hT1PphRU5OAe2Wg== 111 * Connection: Upgrade 112 * Upgrade: Websocket 113 *Sec-WebSocket-Version: 13 114 * User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko 115 * Host: localhost:8064 116 * DNT: 1 117 * Cache-Control: no-cache 118 * Cookie: DTRememberName=admin 119 * 120 * 服務器給出響應 121 * 122 * HTTP/1.1 101 Switching Protocols 123 * Upgrade: websocket 124 * Connection: Upgrade 125 * Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA= 126 * 127 * 在請求中的“Sec-WebSocket-Key”是隨機的,服務器端會用這些數據來構造出一個SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一個魔幻字符串 128 * “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之後進行 BASE-64編碼,將結果做為 “Sec-WebSocket-Accept” 頭的值,返回給客戶端 129 */ 130 #endregion 131 132 #region 打包請求連接數據 133 /// <summary> 134 /// 打包請求連接數據 135 /// </summary> 136 /// <param name="handShakeBytes"></param> 137 /// <param name="length"></param> 138 /// <returns></returns> 139 private byte[] PackageHandShakeData(byte[] handShakeBytes, int length) 140 { 141 string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length); 142 string key = string.Empty; 143 Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n"); 144 Match m = reg.Match(handShakeText); 145 if (m.Value != "") 146 { 147 key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim(); 148 } 149 byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); 150 string secKey = Convert.ToBase64String(secKeyBytes); 151 var responseBuilder = new StringBuilder(); 152 responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n"); 153 responseBuilder.Append("Upgrade: websocket" + "\r\n"); 154 responseBuilder.Append("Connection: Upgrade" + "\r\n"); 155 responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n"); 156 return Encoding.UTF8.GetBytes(responseBuilder.ToString()); 157 } 158 #endregion 159 160 #region 處理接收的數據 161 /// <summary> 162 /// 處理接收的數據 163 /// 參考 http://www.cnblogs.com/smark/archive/2012/11/26/2789812.html 164 /// </summary> 165 /// <param name="recBytes"></param> 166 /// <param name="length"></param> 167 /// <returns></returns> 168 private string AnalyzeClientData(byte[] recBytes, int length) 169 { 170 int start = 0; 171 // 如果有數據則至少包括3位 172 if (length < 2) return ""; 173 // 判斷是否為結束針 174 bool IsEof = (recBytes[start] >> 7) > 0; 175 // 暫不處理超過一幀的數據 176 if (!IsEof) return ""; 177 start++; 178 // 是否包含掩碼 179 bool hasMask = (recBytes[start] >> 7) > 0; 180 // 不包含掩碼的暫不處理 181 if (!hasMask) return ""; 182 // 獲取數據長度 183 UInt64 mPackageLength = (UInt64)recBytes[start] & 0x7F; 184 start++; 185 // 存儲4位掩碼值 186 byte[] Masking_key = new byte[4]; 187 // 存儲數據 188 byte[] mDataPackage; 189 if (mPackageLength == 126) 190 { 191 // 等於126 隨後的兩個字節16位表示數據長度 192 mPackageLength = (UInt64)(recBytes[start] << 8 | recBytes[start + 1]); 193 start += 2; 194 } 195 if (mPackageLength == 127) 196 { 197 // 等於127 隨後的八個字節64位表示數據長度 198 mPackageLength = (UInt64)(recBytes[start] << (8 * 7) | recBytes[start] << (8 * 6) | recBytes[start] << (8 * 5) | recBytes[start] << (8 * 4) | recBytes[start] << (8 * 3) | recBytes[start] << (8 * 2) | recBytes[start] << 8 | recBytes[start + 1]); 199 start += 8; 200 } 201 mDataPackage = new byte[mPackageLength]; 202 for (UInt64 i = 0; i < mPackageLength; i++) 203 { 204 mDataPackage[i] = recBytes[i + (UInt64)start + 4]; 205 } 206 Buffer.BlockCopy(recBytes, start, Masking_key, 0, 4); 207 for (UInt64 i = 0; i < mPackageLength; i++) 208 { 209 mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % 4]); 210 } 211 return Encoding.UTF8.GetString(mDataPackage); 212 } 213 #endregion 214 215 #region 發送數據 216 /// <summary> 217 /// 把發送給客戶端消息打包處理(拼接上誰什麽時候發的什麽消息) 218 /// </summary> 219 /// <returns>The data.</returns> 220 /// <param name="message">Message.</param> 221 private byte[] PackageServerData(string msg) 222 { 223 byte[] content = null; 224 byte[] temp = Encoding.UTF8.GetBytes(msg); 225 if (temp.Length < 126) 226 { 227 content = new byte[temp.Length + 2]; 228 content[0] = 0x81; 229 content[1] = (byte)temp.Length; 230 Buffer.BlockCopy(temp, 0, content, 2, temp.Length); 231 } 232 else if (temp.Length < 0xFFFF) 233 { 234 content = new byte[temp.Length + 4]; 235 content[0] = 0x81; 236 content[1] = 126; 237 content[2] = (byte)(temp.Length & 0xFF); 238 content[3] = (byte)(temp.Length >> 8 & 0xFF); 239 Buffer.BlockCopy(temp, 0, content, 4, temp.Length); 240 } 241 return content; 242 } 243 #endregion
Session
1 public class Session 2 { 3 private Socket _sockeclient; 4 private byte[] _buffer; 5 private string _ip; 6 private bool _isweb = false; 7 8 public Socket SockeClient 9 { 10 set { _sockeclient = value; } 11 get { return _sockeclient; } 12 } 13 14 public byte[] buffer 15 { 16 set { _buffer = value; } 17 get { return _buffer; } 18 } 19 20 public string IP 21 { 22 set { _ip = value; } 23 get { return _ip; } 24 } 25 26 public bool isWeb 27 { 28 set { _isweb = value; } 29 get { return _isweb; } 30 } 31 }
C# socket異步 服務端