Fork me on GitHub
websocket +C#

年后准本打算换工作了,闲暇无事接了个私活做个企业内部的用的信箱的页面, 客户要求用HTML5 做页面 

本来打算用ajax 做后来想想还是用Websocket 的吧 发现以前用的版本居然不能用了-。-! 很是无奈 ,于是谷歌求科普,原来草案改了。。很是无奈

只有改了, 顺便看了下MSDN 上的异步 Socket 的例子, 便在其技术上修改了, 不废话了直接上代码

WebSocket 类

View Code
1  public WebSocket(int numConnections, int receiveBufferSize)
  2         {
  3             this._maxConntionNum = numConnections;
  4             this._receiveBufferSize = receiveBufferSize;
  5             this._SocketPools = new SocketAsyncEventArgsPools(_maxConntionNum);
  6             this.maxAcceptedClients = new Semaphore(this._maxConntionNum, this._maxConntionNum);
  7             this._bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc, receiveBufferSize);
  8             this._bufferManager.InitBuffer();
  9             UserList = new SessionManager();
 10         }
 11         /// <summary>
 12         /// 初始化
 13         /// </summary>
 14         public void Init()
 15         {
 16             SocketAsyncEventArgs IO_EventArg;
 17             for (int i = 0; i < _maxConntionNum; i++)
 18             {
 19                 //预分配一组可重用的SocketAsyncEventArgs
 20                 IO_EventArg = new SocketAsyncEventArgs();
 21                 IO_EventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
 22                 IO_EventArg.UserToken = new AsyncUserToken();
 23                 // 分配一个字节的缓冲区从缓冲池的SocketAsyncEventArg对象的
 24                 this._bufferManager.SetBuffer(IO_EventArg);
 25                 // 添加SocketAsyncEventArg的池
 26                 _SocketPools.Push(IO_EventArg);
 27             }
 28         }
 29         /// <summary>
 30         /// 启动socket监听
 31         /// </summary>
 32         /// <param name="localEndPoint"></param>
 33         public void Start(IPEndPoint localEndPoint)
 34         {
 35             // 创建套接字监听的传入连接
 36             this._listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
 37             this._listenSocket.Bind(localEndPoint);
 38             // 启动服务器,最大挂起100个队列监听
 39             this._listenSocket.Listen(100);
 40             // 监听连接 
 41             StartAccept(null);
 42             Console.WriteLine("Press any key to terminate the server process....");
 43             Console.ReadKey();
 44         }
 45         ///开始操作来接受一个连接请求从客户端
 46         /// <param name="acceptEventArg">的上下文对象,使用时发出接受服务器的侦听套接字上的操作</param> 
 47         public void StartAccept(SocketAsyncEventArgs acceptEventArg)
 48         {
 49             if (acceptEventArg == null)
 50             {
 51                 acceptEventArg = new SocketAsyncEventArgs();
 52                 acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
 53             }
 54             else
 55             {
 56                 //插座必须被清除,由于上下文对象被重复使用 
 57                 acceptEventArg.AcceptSocket = null;
 58             }
 59 
 60             this.maxAcceptedClients.WaitOne();
 61             bool willRaiseEvent = this._listenSocket.AcceptAsync(acceptEventArg);
 62             if (!willRaiseEvent)
 63             {
 64                 ProcessAccept(acceptEventArg);
 65             }
 66         }
 67         private void ProcessAccept(SocketAsyncEventArgs e)
 68         {
 69             Interlocked.Increment(ref this._numConnectedSockets);
 70             Console.WriteLine("接受的客户连接:有{0}个客户端连接到服务器", this._numConnectedSockets);
 71             // 从连接池中取出一个链接用户响应
 72             SocketAsyncEventArgs readEventArgs = _SocketPools.Pop();
 73             byte[] bytes = new byte[1024];
 74             int _byteLength = e.AcceptSocket.Receive(bytes);
 75             MessageEncoded.Handshaked(bytes, _byteLength, ref readEventArgs);
 76             ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;
 77             //当客户端连接,建立一个接收到的连接
 78             bool willRaiseEvent = e.AcceptSocket.SendAsync(readEventArgs);
 79             if (!willRaiseEvent)
 80             {
 81                 ProcessReceive(readEventArgs);
 82             }
 83             this.UserList.add(((AsyncUserToken)readEventArgs.UserToken));
 84             // 接受下一个连接请求
 85             StartAccept(e);
 86         }
 87         ///此方法是一个回调方法与Socket.AcceptAsync 
 88         ///操作被调用时接受操作完成
 89         public void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
 90         {
 91             ProcessAccept(e);
 92         }
 93         /// ,接收或发送操作完成的套接字此方法被调用
 94         private void IO_Completed(object sender, SocketAsyncEventArgs e)
 95         {
 96            
 97             // 判断哪种类型的操作完成,并调用相关的处理程序
 98             switch (e.LastOperation)
 99             {
100                 case SocketAsyncOperation.Receive:
101                     ProcessReceive(e);
102                     break;
103                 case SocketAsyncOperation.Send:
104                     ProcessSend(e);
105                     break;
106                 default:
107                     throw new ArgumentException("socket 完成的不是接受和发送");
108             }
109            
110         }
111         ///调用此方法的异步接收操作完成时,
112         ///如果远程主机关闭了连接,然后关闭套接字,   
113         ///如果收到数据,然后将数据返回到客户端。
114         private void ProcessReceive(SocketAsyncEventArgs e)
115         {
116             //检查,如果远程主机关闭了连接
117             AsyncUserToken token = (AsyncUserToken)e.UserToken;
118             if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
119             {
120                 //递增计数的总字节数接收的服务器 
121                 Interlocked.Add(ref _dataCount, e.BytesTransferred);
122                 byte[] data = MessageEncoded.sendData(MessageEncoded.DecodeClientMsg(e.Buffer,e.Offset,e.BytesTransferred));
123                 e.SetBuffer(data, 0, data.Length);
124                 //回声接收到的数据返回给客户端
125                 bool willRaiseEvent = token.Socket.SendAsync(e);
126                  if (!willRaiseEvent)
127                  {
128                      ProcessSend(e);
129                  }
130                  this.UserList.Fs(data, token);
131             }
132             else
133             {
134                 CloseClientSocket(e);
135             }
136         }
137         ///,异步发送操作完成此方法被调用。   
138         /// <param name="e">从客户端发送的数据</param>
139         private void ProcessSend(SocketAsyncEventArgs e)
140         {
141 
142             if (e.SocketError == SocketError.Success)
143             {
144                 // 做了呼应的数据返回给客户端
145                 AsyncUserToken token = (AsyncUserToken)e.UserToken;
146                 _bufferManager.SetBuffer(e);
147                 // 从客户端读取下一个数据块
148                 bool willRaiseEvent = token.Socket.ReceiveAsync(e);
149                 if (!willRaiseEvent)
150                 {
151                     ProcessReceive(e);
152                 }
153             }
154             else
155             {
156                 CloseClientSocket(e);
157             }
158         }
159         private void CloseClientSocket(SocketAsyncEventArgs e)
160         {
161             AsyncUserToken token = e.UserToken as AsyncUserToken;
162             // 关闭与客户端的连接
163             try
164             {
165                 token.Socket.Shutdown(SocketShutdown.Send);
166             }
167             //抛出,如果客户端进程已经关闭
168             catch (Exception) { }
169             token.Socket.Close();
170 
171             // 递减计数器的记录的客户端连接到服务器 
172             Interlocked.Decrement(ref _numConnectedSockets);
173             maxAcceptedClients.Release();
174             Console.WriteLine("有一个客户端从服务器断开,有{0}客户端已连接", _numConnectedSockets);
175             // 释放的SocketAsyncEventArg,使他们能够重复使用的另一个客户端
176             _SocketPools.Push(e);
177         }

 

WebSocket 握手和byte[] 转码编码 只支持13版  不兼容旧版本

View Code
1 public static class MessageEncoded
 2     {
 3         private const string Guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 4         public static bool Handshaked(byte[] bytes, int recByteLength, ref SocketAsyncEventArgs Async)
 5         {
 6             string recStr = Encoding.UTF8.GetString(bytes);
 7             string[] ss = recStr.Split(Environment.NewLine.ToCharArray());
 8             string key = ss[10].Replace("Sec-WebSocket-Key: ", "");
 9             key = string.Format("{0}{1}", key, Guid);
10             SHA1 s1 = SHA1.Create();
11             byte[] sha1bytes = s1.ComputeHash(Encoding.UTF8.GetBytes(key));
12             string acceptStr = Convert.ToBase64String(sha1bytes);
13             StringBuilder content = new System.Text.StringBuilder("HTTP/1.1 101 Switching Protocols\r\n");
14             content.Append("Upgrade: websocket\r\n");
15             content.Append("Connection: Upgrade\r\n");
16             content.AppendFormat("Sec-WebSocket-Accept: {0}\r\n\r\n", acceptStr);
17             // content.Append("Sec-WebSocket-Protocol: ws://192.168.1.21:2000 \r\n\r\n");
18             byte[] buff =Encoding.UTF8.GetBytes(content.ToString());
19             Async.SetBuffer(buff, 0, buff.Length);
20             Console.WriteLine("缓冲完毕。");
21             return true;
22         }
23         public static String DecodeClientMsg(Byte[] buffer, int len)
24         {
25             Byte[] mask = new Byte[4];
26             int beginIndex = 0;
27             if ((buffer[0] & 0x80) != 0x80)
28             {
29 
30             }
31             else if (buffer[0] != 0x81)
32             {
33                 return null;
34             }
35             else if ((buffer[1] & 0x80) != 0x80)//没有mask,
36             {
37                 return null;  
38             }
39             else
40             {
41                 
42                 //获取 frame-payload-length的值  
43                 int payload_len = buffer[1] & 0x7F;//有效信息长度  
44                 if (payload_len == 0x7F)
45                 {
46                 }
47                 else if (payload_len == 0x7E)
48                 {
49                     Array.Copy(buffer, 4, mask, 0, 4);
50                   
51                     payload_len = payload_len & 0x00000000;
52                     payload_len = payload_len | buffer[2];
53                     payload_len = (payload_len << 8) | buffer[3];
54                     beginIndex = 8;
55                 }
56                 else
57                 {
58                     Array.Copy(buffer, 2, mask, 0, 4);
59                     beginIndex = 6;
60                 }
61 
62                 for (int i = 0; i < payload_len; i++)
63                 {
64                     buffer[i + beginIndex] = (byte)(buffer[i + beginIndex] ^ mask[i % 4]);
65                 }
66                 return Encoding.UTF8.GetString(buffer, beginIndex, payload_len);
67             }
68             return null;
69         }
70         public static byte[] sendData(string content)
71         {
72             byte[] contentBytes = null;
73             byte[] temp = Encoding.UTF8.GetBytes(content);
74             if (temp.Length < 126)
75             {
76                 contentBytes = new byte[temp.Length + 2];
77                 contentBytes[0] = 0x81;
78                 contentBytes[1] = (byte)temp.Length;
79                 Array.Copy(temp, 0, contentBytes, 2, temp.Length);
80             }
81             else if (temp.Length < 0xFFFF)
82             {
83                 contentBytes = new byte[temp.Length + 4];
84                 contentBytes[0] = 0x81;
85                 contentBytes[1] = 126;
86                 contentBytes[2] = (byte)(temp.Length & 0xFF);
87                 contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
88                 Array.Copy(temp, 0, contentBytes, 4, temp.Length);
89             }
90             else
91             {
92                 byte[] st = System.Text.Encoding.UTF8.GetBytes(string.Format("暂不处理超长内容").ToCharArray());
93             }
94             return contentBytes;
95         }

这是MSDN 异步socket 的几个类如果要实现群发稍微修改下就行了修改的代码我就不贴了很简单

 

View Code
1 public class SocketAsyncEventArgsPools
 2     {
 3         private Stack<SocketAsyncEventArgs> Sockets = null;
 4         public int Count { get { return Sockets.Count; } }
 5         public SocketAsyncEventArgsPools(int MaxNum)
 6         {
 7             this.Sockets = new Stack<SocketAsyncEventArgs>(MaxNum);
 8 
 9         }
10         public void Push(SocketAsyncEventArgs item)
11         {
12             if (item == null) { throw new ArgumentNullException("SocketAsyncEventArgsPool 集合对象不能为空"); }
13             lock (Sockets)
14             {
15                 Sockets.Push(item);
16             }
17         }
18         public SocketAsyncEventArgs Pop()
19         {
20             lock (Sockets)
21             {
22                 return Sockets.Pop();
23             }
24         }
25     }
26     public class AsyncUserToken
27     {
28         Socket m_socket;
29         public AsyncUserToken() : this(null) { }
30         public AsyncUserToken(Socket socket)
31         {
32             m_socket = socket;
33         }
34         public Socket Socket
35         {
36             get { return m_socket; }
37             set { m_socket = value; }
38         }
39     }
40     public class BufferManager
41     {
42         int m_numBytes;                 // the total number of bytes controlled by the buffer pool 总数量的字节控制缓冲池
43         byte[] m_buffer;                // the underlying byte array maintained by the Buffer Manager 底层字节数组所维护的缓冲区管理器
44         Stack<int> m_freeIndexPool;
45         int m_currentIndex;
46         int m_bufferSize;
47 
48         public BufferManager(int totalBytes, int bufferSize)
49         {
50             m_numBytes = totalBytes;
51             m_currentIndex = 0;
52             m_bufferSize = bufferSize;
53             m_freeIndexPool = new Stack<int>();
54         }
55 
56         // Allocates buffer space used by the buffer pool
57         public void InitBuffer()
58         {
59             // create one big large buffer and divide that 
60             // out to each SocketAsyncEventArg object
61             m_buffer = new byte[m_numBytes];
62         }
63 
64         // Assigns a buffer from the buffer pool to the 
65         // specified SocketAsyncEventArgs object
66         //
67         // <returns>true if the buffer was successfully set, else false</returns>
68         public bool SetBuffer(SocketAsyncEventArgs args)
69         {
70             if (m_freeIndexPool.Count > 0)
71             {
72                 args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
73             }
74             else
75             {
76                 if ((m_numBytes - m_bufferSize) < m_currentIndex)
77                 {
78                     return false;
79                 }
80                 args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
81                 m_currentIndex += m_bufferSize;
82             }
83             return true;
84         }
85 
86         // Removes the buffer from a SocketAsyncEventArg object.  
87         // This frees the buffer back to the buffer pool
88         public void FreeBuffer(SocketAsyncEventArgs args)
89         {
90             m_freeIndexPool.Push(args.Offset);
91             args.SetBuffer(null, 0, 0);
92         }
93     }

这是前台页面的代码很简单懒的写了 百度了一个

View Code
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 
 3 <html><head>  
 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">  
 5 <title>1</title>  
 6 <script>
 7     if (!window.WebSocket) {
 8         alert("WebSocket not supported by this browser!");
 9     }
10     var ws,txt;
11     window.onload = function () {
12         txt = document.getElementById("txt");
13        
14     }
15     function display() {
16         if (ws == null) {
17             ws = new WebSocket("ws://192.168.1.21:2000");
18             ws.onmessage = function (evt) {
19                 txt.innerHTML = txt.innerHTML + "接收到信息:" + evt.data + "<br\>";
20             };
21             ws.onclose = function () {
22                 txt.innerHTML = txt.innerHTML + "连接已关闭<br\>";
23                 ws = null;
24             };
25             ws.onerror = function (evt) {
26                 txt.innerHTML = txt.innerHTML + "连接出错<br\>";
27                 ws = null;
28             }
29             ws.onopen = function (evt) {
30                 txt.innerHTML = txt.innerHTML + "连接已打开<br\>";
31             };
32         }
33     }
34     function wssend() {
35         if (ws != null) {
36             var sendstr = document.getElementById("txtSend").value;
37             if (sendstr == "") {
38                 sendstr = " ";
39             }
40             ws.send(sendstr);
41         } else {
42             alert("连接失效。");
43         }
44     }  
45 </script>
46 </head>  
47 <body style="text-align:center;" mce_style="text-align:center;">  
48 <input type="button" value="点我" onclick="display()">  
49 <br>  
50 <input type="text" id="txtSend"><input type="button" id="btnSend" value="发送" onclick="wssend()">  
51  <div>
52     <p id="txt"></p>
53  </div>
54   
55 </body></html>

 

 

 
分类: C#Html5+css3
posted on 2012-12-23 18:27  HackerVirus  阅读(598)  评论(1)    收藏  举报