转自:  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();
  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[];
  32. try
  33. {
  34. //接收客户端的数据
  35. SockeClient.BeginReceive(buffer, , 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, , buffer.Length, SocketFlags.None, new AsyncCallback(Recieve), SockeClient);
  78. string msg = Encoding.UTF8.GetString(buffer, , 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, , 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 = ;
  171. // 如果有数据则至少包括3位
  172. if (length < ) return "";
  173. // 判断是否为结束针
  174. bool IsEof = (recBytes[start] >> ) > ;
  175. // 暂不处理超过一帧的数据
  176. if (!IsEof) return "";
  177. start++;
  178. // 是否包含掩码
  179. bool hasMask = (recBytes[start] >> ) > ;
  180. // 不包含掩码的暂不处理
  181. if (!hasMask) return "";
  182. // 获取数据长度
  183. UInt64 mPackageLength = (UInt64)recBytes[start] & 0x7F;
  184. start++;
  185. // 存储4位掩码值
  186. byte[] Masking_key = new byte[];
  187. // 存储数据
  188. byte[] mDataPackage;
  189. if (mPackageLength == )
  190. {
  191. // 等于126 随后的两个字节16位表示数据长度
  192. mPackageLength = (UInt64)(recBytes[start] << | recBytes[start + ]);
  193. start += ;
  194. }
  195. if (mPackageLength == )
  196. {
  197. // 等于127 随后的八个字节64位表示数据长度
  198. mPackageLength = (UInt64)(recBytes[start] << ( * ) | recBytes[start] << ( * ) | recBytes[start] << ( * ) | recBytes[start] << ( * ) | recBytes[start] << ( * ) | recBytes[start] << ( * ) | recBytes[start] << | recBytes[start + ]);
  199. start += ;
  200. }
  201. mDataPackage = new byte[mPackageLength];
  202. for (UInt64 i = ; i < mPackageLength; i++)
  203. {
  204. mDataPackage[i] = recBytes[i + (UInt64)start + ];
  205. }
  206. Buffer.BlockCopy(recBytes, start, Masking_key, , );
  207. for (UInt64 i = ; i < mPackageLength; i++)
  208. {
  209. mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % ]);
  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 < )
  226. {
  227. content = new byte[temp.Length + ];
  228. content[] = 0x81;
  229. content[] = (byte)temp.Length;
  230. Buffer.BlockCopy(temp, , content, , temp.Length);
  231. }
  232. else if (temp.Length < 0xFFFF)
  233. {
  234. content = new byte[temp.Length + ];
  235. content[] = 0x81;
  236. content[] = ;
  237. content[] = (byte)(temp.Length & 0xFF);
  238. content[] = (byte)(temp.Length >> & 0xFF);
  239. Buffer.BlockCopy(temp, , content, , temp.Length);
  240. }
  241. return content;
  242. }
  243. #endregion
  1. 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异步 服务端的更多相关文章

  1. java socket实现服务端,客户端简单网络通信。Chat

    之前写的实现简单网络通信的代码,有一些严重bug.后面详细写. 根据上次的代码,主要增加了用户注册,登录页面,以及实现了实时显示当前在登录状态的人数.并解决一些上次未发现的bug.(主要功能代码参见之 ...

  2. 【gRPC】C++异步服务端优化版,多服务接口样例

    官方的C++异步服务端API样例可读性并不好,理解起来非常的费劲,各种状态机也并不明了,整个运行过程也容易读不懂,因此此处参考网上的博客进行了重写,以求顺利读懂. C++异步服务端实例,详细注释版 g ...

  3. Socket客户端/服务端简单实例

    1.client端 package demo.socket; import java.io.BufferedReader;import java.io.IOException;import java. ...

  4. java.net.SocketException:Software caused connection abort: recv failed 异常分析 +socket客户端&服务端代码

    java.net.SocketException:Software caused connection abort: recv failed 异常分析 分类: 很多的技术 2012-01-04 12: ...

  5. JAVA Socket获取服务端信息

    1.Socket.getInetAddress(),获取服务端地址. 2.Socket.getPort(),获取服务端端口.

  6. 简单的同步Socket程序服务端

    首先,Socket是.Net提供的 System.Net.Sockets命名空间的Scoket类为网络通信提供了一套丰富的方法和属性 服务器按照Socket的基本流程 先创建Socket 在用Bind ...

  7. c#Socket Tcp服务端编程

    创建一个socket服务类,绑定监听端口, 然后创建一个线程listen连接的客户端, 把监听到的客户端加入dictionary里面,以便于管理, 同时创建receive线程,循环接收数据加入list ...

  8. python网络编程:socket、服务端、客户端

    本文内容: socket介绍 TCP: 服务端 客户端 UDP: 服务端 客户端 首发时间:2018-02-08 01:14 修改: 2018-03-20 :重置了布局,增加了UDP 什么是socke ...

  9. 利用Socket 客户端---->服务端 传送文件到指定路径,并返回一个友好的回馈

    首先盲写的一个传输文件的方法,但测试发现了一个非常不容易发现的问题,这里先说明一下. 错误的代码如下: package com.TCP.java; import java.io.File; impor ...

随机推荐

  1. C#读操作(字节/字符)Filestream、File、StreamReader

    方法一:使用Filestream,将文本一次性全部转换为字节,之后转换为string显示在text中 OpenFileDialog fd = new OpenFileDialog(); fd.Filt ...

  2. Java代码执行顺序及多态体现

    /** * Description: * 基类的引用变量可以只想基类的实例对象也可指向其子类的事来对象 * 接口的引用变量也可以指向实现类的实例对象 * 程序调用的方法在运行期才动态绑定 * 绑定指将 ...

  3. 04: redis集群

    1.1 主从同步 1.CPA原理 1. CPA原理是分布式存储理论的基石: C(一致性):   A(可用性):  P(分区容忍性); 2. 当主从网络无法连通时,修改操作无法同步到节点,所以“一致性” ...

  4. Python 入门 之 类的约束以及super()剖析

    Python 入门 之 类的约束以及super()剖析 1.类的约束 第一版: class WechatPay: def pay(self): print("微信支付") clas ...

  5. Web前端开发HTML基础

    HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记),相当于定义统一的一套规则,大家都来遵守他,这样就可以让浏览器根据标 ...

  6. Oracle Help 类

    public static string ConnString = @"Data Source=xxx;USER ID=xxx;PASSWORD=xxx"; /// <sum ...

  7. java 问题汇总

    1.自动加载出错 require a bean of .... The injection point has the following annotations: - @org.springfram ...

  8. 一份非常完整、详细的MySQL规范

    一.数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名 ...

  9. Stream 分布式数据流的轻量级异步快照

    1. 概述 分布式有状态流处理支持在云中部署和执行大规模连续计算,主要针对低延迟和高吞吐量.这种模式的一个最根本的挑战就是在可能的失败情况下提供处理保证.现有方法依赖于可用于故障恢复的周期性全局状态快 ...

  10. webpack4 打包

    1. 基本安装及命令 npm config set registry https://registry.npm.taobao.org     //  淘宝镜像npm install webpack-c ...