年后准本打算换工作了,闲暇无事接了个私活做个企业内部的用的信箱的页面, 客户要求用HTML5 做页面
本来打算用ajax 做后来想想还是用Websocket 的吧 发现以前用的版本居然不能用了-。-! 很是无奈 ,于是谷歌求科普,原来草案改了。。很是无奈
只有改了, 顺便看了下MSDN 上的异步 Socket 的例子, 便在其技术上修改了, 不废话了直接上代码
WebSocket 类
View Code1 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 Code1 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 Code1 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 Code1 <!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
浙公网安备 33010602011771号