WebSocket是下一代客户端-服务器的异步通信方法.
WebSocket最伟大之处在于服务器和客户端可以在任意时刻相互推送信息
WebSocket允许跨域通信
Ajax技术需要客户端发起请求,WebSocket服务器和客户端可以彼此相互推送信息
 
下面实现一个简单的实时多人聊天系统
WebSocket服务端:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Security.Cryptography;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10.  
  11. namespace WebSocketServer
  12. {
  13. class Program
  14. {
  15. static void Main(string[] args)
  16. {
  17. WebSocketServerTest WSServerTest = new WebSocketServerTest();
  18. WSServerTest.Start();
  19. }
  20. }
  21.  
  22. public class WebSocketServerTest : IDisposable
  23. {
  24. private WebSocketServer WSServer;
  25. public WebSocketServerTest()
  26. {
  27. //使用默认的设置
  28. WSServer = new WebSocketServer();
  29. }
  30.  
  31. public void Dispose()
  32. {
  33. Close();
  34. }
  35.  
  36. private void Close()
  37. {
  38. WSServer.Dispose();
  39. GC.SuppressFinalize(this);
  40. }
  41.  
  42. ~WebSocketServerTest()
  43. {
  44. Close();
  45. }
  46.  
  47. public void Start()
  48. {
  49. WSServer.NewConnection += new NewConnectionEventHandler(WSServer_NewConnection);
  50. WSServer.Disconnected += new DisconnectedEventHandler(WSServer_Disconnected);
  51. WSServer.StartServer();
  52. }
  53.  
  54. void WSServer_Disconnected(Object sender, EventArgs e)
  55. {
  56. }
  57.  
  58. void WSServer_NewConnection(string loginName, EventArgs e)
  59. {
  60. }
  61. }
  62.  
  63. public class Logger
  64. {
  65. public bool LogEvents { get; set; }
  66.  
  67. public Logger()
  68. {
  69. LogEvents = true;
  70. }
  71.  
  72. public void Log(string Text)
  73. {
  74. if (LogEvents) Console.WriteLine(Text);
  75. }
  76. }
  77.  
  78. public enum ServerStatusLevel { Off, WaitingConnection, ConnectionEstablished };
  79.  
  80. public delegate void NewConnectionEventHandler(string loginName, EventArgs e);
  81. public delegate void DataReceivedEventHandler(Object sender, string message, EventArgs e);
  82. public delegate void DisconnectedEventHandler(Object sender, EventArgs e);
  83. public delegate void BroadcastEventHandler(string message, EventArgs e);
  84.  
  85. public class WebSocketServer : IDisposable
  86. {
  87. private bool AlreadyDisposed;
  88. private Socket Listener;
  89. private int ConnectionsQueueLength;
  90. private int MaxBufferSize;
  91. private string Handshake;
  92. private StreamReader ConnectionReader;
  93. private StreamWriter ConnectionWriter;
  94. private Logger logger;
  95. private byte[] FirstByte;
  96. private byte[] LastByte;
  97. private byte[] ServerKey1;
  98. private byte[] ServerKey2;
  99.  
  100. List<SocketConnection> connectionSocketList = new List<SocketConnection>();
  101.  
  102. public ServerStatusLevel Status { get; private set; }
  103. public int ServerPort { get; set; }
  104. public string ServerLocation { get; set; }
  105. public string ConnectionOrigin { get; set; }
  106. public bool LogEvents
  107. {
  108. get { return logger.LogEvents; }
  109. set { logger.LogEvents = value; }
  110. }
  111.  
  112. public event NewConnectionEventHandler NewConnection;
  113. public event DataReceivedEventHandler DataReceived;
  114. public event DisconnectedEventHandler Disconnected;
  115.  
  116. private void Initialize()
  117. {
  118. AlreadyDisposed = false;
  119. logger = new Logger();
  120.  
  121. Status = ServerStatusLevel.Off;
  122. ConnectionsQueueLength = ;
  123. MaxBufferSize = * ;
  124. FirstByte = new byte[MaxBufferSize];
  125. LastByte = new byte[MaxBufferSize];
  126. FirstByte[] = 0x00;
  127. LastByte[] = 0xFF;
  128. logger.LogEvents = true;
  129. }
  130.  
  131. public WebSocketServer()
  132. {
  133. ServerPort = ;
  134. ServerLocation = string.Format("ws://{0}:4141/chat", getLocalmachineIPAddress());
  135. Initialize();
  136. }
  137.  
  138. public WebSocketServer(int serverPort, string serverLocation, string connectionOrigin)
  139. {
  140. ServerPort = serverPort;
  141. ConnectionOrigin = connectionOrigin;
  142. ServerLocation = serverLocation;
  143. Initialize();
  144. }
  145.  
  146. ~WebSocketServer()
  147. {
  148. Close();
  149. }
  150.  
  151. public void Dispose()
  152. {
  153. Close();
  154. }
  155.  
  156. private void Close()
  157. {
  158. if (!AlreadyDisposed)
  159. {
  160. AlreadyDisposed = true;
  161. if (Listener != null) Listener.Close();
  162. foreach (SocketConnection item in connectionSocketList)
  163. {
  164. item.ConnectionSocket.Close();
  165. }
  166. connectionSocketList.Clear();
  167. GC.SuppressFinalize(this);
  168. }
  169. }
  170.  
  171. public static IPAddress getLocalmachineIPAddress()
  172. {
  173. string strHostName = Dns.GetHostName();
  174. IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
  175.  
  176. foreach (IPAddress ip in ipEntry.AddressList)
  177. {
  178. //IPV4
  179. if (ip.AddressFamily == AddressFamily.InterNetwork)
  180. return ip;
  181. }
  182.  
  183. return ipEntry.AddressList[];
  184. }
  185.  
  186. public void StartServer()
  187. {
  188. Char char1 = Convert.ToChar();
  189.  
  190. Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
  191. Listener.Bind(new IPEndPoint(getLocalmachineIPAddress(), ServerPort));
  192.  
  193. Listener.Listen(ConnectionsQueueLength);
  194.  
  195. logger.Log(string.Format("聊天服务器启动。监听地址:{0}, 端口:{1}", getLocalmachineIPAddress(), ServerPort));
  196. logger.Log(string.Format("WebSocket服务器地址: ws://{0}:{1}/chat", getLocalmachineIPAddress(), ServerPort));
  197.  
  198. while (true)
  199. {
  200. Socket sc = Listener.Accept();
  201.  
  202. if (sc != null)
  203. {
  204. System.Threading.Thread.Sleep();
  205. SocketConnection socketConn = new SocketConnection();
  206. socketConn.ConnectionSocket = sc;
  207. socketConn.NewConnection += new NewConnectionEventHandler(socketConn_NewConnection);
  208. socketConn.DataReceived += new DataReceivedEventHandler(socketConn_BroadcastMessage);
  209. socketConn.Disconnected += new DisconnectedEventHandler(socketConn_Disconnected);
  210.  
  211. socketConn.ConnectionSocket.BeginReceive(socketConn.receivedDataBuffer,
  212. , socketConn.receivedDataBuffer.Length,
  213. , new AsyncCallback(socketConn.ManageHandshake),
  214. socketConn.ConnectionSocket.Available);
  215. connectionSocketList.Add(socketConn);
  216. }
  217. }
  218. }
  219.  
  220. void socketConn_Disconnected(Object sender, EventArgs e)
  221. {
  222. SocketConnection sConn = sender as SocketConnection;
  223. if (sConn != null)
  224. {
  225. Send(string.Format("【{0}】离开了聊天室!", sConn.Name));
  226. sConn.ConnectionSocket.Close();
  227. connectionSocketList.Remove(sConn);
  228. }
  229. }
  230.  
  231. void socketConn_BroadcastMessage(Object sender, string message, EventArgs e)
  232. {
  233. if (message.IndexOf("login:") != -)
  234. {
  235. SocketConnection sConn = sender as SocketConnection;
  236. sConn.Name = message.Substring(message.IndexOf("login:") + "login:".Length);
  237. message = string.Format("欢迎【{0}】来到聊天室!", message.Substring(message.IndexOf("login:") + "login:".Length));
  238. }
  239. Send(message);
  240. }
  241.  
  242. void socketConn_NewConnection(string name, EventArgs e)
  243. {
  244. if (NewConnection != null)
  245. NewConnection(name, EventArgs.Empty);
  246. }
  247.  
  248. public void Send(string message)
  249. {
  250. foreach (SocketConnection item in connectionSocketList)
  251. {
  252. if (!item.ConnectionSocket.Connected) return;
  253. try
  254. {
  255. if (item.IsDataMasked)
  256. {
  257. DataFrame dr = new DataFrame(message);
  258. item.ConnectionSocket.Send(dr.GetBytes());
  259. }
  260. else
  261. {
  262. item.ConnectionSocket.Send(FirstByte);
  263. item.ConnectionSocket.Send(Encoding.UTF8.GetBytes(message));
  264. item.ConnectionSocket.Send(LastByte);
  265. }
  266. }
  267. catch (Exception ex)
  268. {
  269. logger.Log(ex.Message);
  270. }
  271. }
  272. }
  273. }
  274.  
  275. public class SocketConnection
  276. {
  277. private Logger logger;
  278.  
  279. private string name;
  280. public string Name
  281. {
  282. get { return name; }
  283. set { name = value; }
  284. }
  285.  
  286. private Boolean isDataMasked;
  287. public Boolean IsDataMasked
  288. {
  289. get { return isDataMasked; }
  290. set { isDataMasked = value; }
  291. }
  292.  
  293. public Socket ConnectionSocket;
  294.  
  295. private int MaxBufferSize;
  296. private string Handshake;
  297. private string New_Handshake;
  298.  
  299. public byte[] receivedDataBuffer;
  300. private byte[] FirstByte;
  301. private byte[] LastByte;
  302. private byte[] ServerKey1;
  303. private byte[] ServerKey2;
  304.  
  305. public event NewConnectionEventHandler NewConnection;
  306. public event DataReceivedEventHandler DataReceived;
  307. public event DisconnectedEventHandler Disconnected;
  308.  
  309. public SocketConnection()
  310. {
  311. logger = new Logger();
  312. MaxBufferSize = * ;
  313. receivedDataBuffer = new byte[MaxBufferSize];
  314. FirstByte = new byte[MaxBufferSize];
  315. LastByte = new byte[MaxBufferSize];
  316. FirstByte[] = 0x00;
  317. LastByte[] = 0xFF;
  318.  
  319. Handshake = "HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine;
  320. Handshake += "Upgrade: WebSocket" + Environment.NewLine;
  321. Handshake += "Connection: Upgrade" + Environment.NewLine;
  322. Handshake += "Sec-WebSocket-Origin: " + "{0}" + Environment.NewLine;
  323. Handshake += string.Format("Sec-WebSocket-Location: " + "ws://{0}:4141/chat" + Environment.NewLine, WebSocketServer.getLocalmachineIPAddress());
  324. Handshake += Environment.NewLine;
  325.  
  326. New_Handshake = "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;
  327. New_Handshake += "Upgrade: WebSocket" + Environment.NewLine;
  328. New_Handshake += "Connection: Upgrade" + Environment.NewLine;
  329. New_Handshake += "Sec-WebSocket-Accept: {0}" + Environment.NewLine;
  330. New_Handshake += Environment.NewLine;
  331. }
  332.  
  333. private void Read(IAsyncResult status)
  334. {
  335. if (!ConnectionSocket.Connected) return;
  336. string messageReceived = string.Empty;
  337. DataFrame dr = new DataFrame(receivedDataBuffer);
  338.  
  339. try
  340. {
  341. if (!this.isDataMasked)
  342. {
  343. // Web Socket protocol: messages are sent with 0x00 and 0xFF as padding bytes
  344. System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
  345. int startIndex = ;
  346. int endIndex = ;
  347.  
  348. // Search for the start byte
  349. while (receivedDataBuffer[startIndex] == FirstByte[]) startIndex++;
  350. // Search for the end byte
  351. endIndex = startIndex + ;
  352. while (receivedDataBuffer[endIndex] != LastByte[] && endIndex != MaxBufferSize - ) endIndex++;
  353. if (endIndex == MaxBufferSize - ) endIndex = MaxBufferSize;
  354.  
  355. // Get the message
  356. messageReceived = decoder.GetString(receivedDataBuffer, startIndex, endIndex - startIndex);
  357. }
  358. else
  359. {
  360. messageReceived = dr.Text;
  361. }
  362.  
  363. if ((messageReceived.Length == MaxBufferSize && messageReceived[] == Convert.ToChar()) ||
  364. messageReceived.Length == )
  365. {
  366. logger.Log("接受到的信息 [\"" + string.Format("logout:{0}", this.name) + "\"]");
  367. if (Disconnected != null)
  368. Disconnected(this, EventArgs.Empty);
  369. }
  370. else
  371. {
  372. if (DataReceived != null)
  373. {
  374. logger.Log("接受到的信息 [\"" + messageReceived + "\"]");
  375. DataReceived(this, messageReceived, EventArgs.Empty);
  376. }
  377. Array.Clear(receivedDataBuffer, , receivedDataBuffer.Length);
  378. ConnectionSocket.BeginReceive(receivedDataBuffer, , receivedDataBuffer.Length, , new AsyncCallback(Read), null);
  379. }
  380. }
  381. catch (Exception ex)
  382. {
  383. logger.Log(ex.Message);
  384. logger.Log("Socket连接将会被终止。");
  385. if (Disconnected != null)
  386. Disconnected(this, EventArgs.Empty);
  387. }
  388. }
  389.  
  390. private void BuildServerPartialKey(int keyNum, string clientKey)
  391. {
  392. string partialServerKey = "";
  393. byte[] currentKey;
  394. int spacesNum = ;
  395. char[] keyChars = clientKey.ToCharArray();
  396. foreach (char currentChar in keyChars)
  397. {
  398. if (char.IsDigit(currentChar)) partialServerKey += currentChar;
  399. if (char.IsWhiteSpace(currentChar)) spacesNum++;
  400. }
  401. try
  402. {
  403. currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey) / spacesNum));
  404. if (BitConverter.IsLittleEndian) Array.Reverse(currentKey);
  405.  
  406. if (keyNum == ) ServerKey1 = currentKey;
  407. else ServerKey2 = currentKey;
  408. }
  409. catch
  410. {
  411. if (ServerKey1 != null) Array.Clear(ServerKey1, , ServerKey1.Length);
  412. if (ServerKey2 != null) Array.Clear(ServerKey2, , ServerKey2.Length);
  413. }
  414. }
  415.  
  416. private byte[] BuildServerFullKey(byte[] last8Bytes)
  417. {
  418. byte[] concatenatedKeys = new byte[];
  419. Array.Copy(ServerKey1, , concatenatedKeys, , );
  420. Array.Copy(ServerKey2, , concatenatedKeys, , );
  421. Array.Copy(last8Bytes, , concatenatedKeys, , );
  422.  
  423. // MD5 Hash
  424. System.Security.Cryptography.MD5 MD5Service = System.Security.Cryptography.MD5.Create();
  425. return MD5Service.ComputeHash(concatenatedKeys);
  426. }
  427.  
  428. public void ManageHandshake(IAsyncResult status)
  429. {
  430. string header = "Sec-WebSocket-Version:";
  431. int HandshakeLength = (int)status.AsyncState;
  432. byte[] last8Bytes = new byte[];
  433.  
  434. System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
  435. String rawClientHandshake = decoder.GetString(receivedDataBuffer, , HandshakeLength);
  436.  
  437. Array.Copy(receivedDataBuffer, HandshakeLength - , last8Bytes, , );
  438.  
  439. //现在使用的是比较新的Websocket协议
  440. if (rawClientHandshake.IndexOf(header) != -)
  441. {
  442. this.isDataMasked = true;
  443. string[] rawClientHandshakeLines = rawClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
  444. string acceptKey = "";
  445. foreach (string Line in rawClientHandshakeLines)
  446. {
  447. Console.WriteLine(Line);
  448. if (Line.Contains("Sec-WebSocket-Key:"))
  449. {
  450. acceptKey = ComputeWebSocketHandshakeSecurityHash09(Line.Substring(Line.IndexOf(":") + ));
  451. }
  452. }
  453.  
  454. New_Handshake = string.Format(New_Handshake, acceptKey);
  455. byte[] newHandshakeText = Encoding.UTF8.GetBytes(New_Handshake);
  456. ConnectionSocket.BeginSend(newHandshakeText, , newHandshakeText.Length, , HandshakeFinished, null);
  457. return;
  458. }
  459.  
  460. string ClientHandshake = decoder.GetString(receivedDataBuffer, , HandshakeLength - );
  461.  
  462. string[] ClientHandshakeLines = ClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
  463.  
  464. logger.Log("新的连接请求来自" + ConnectionSocket.LocalEndPoint + "。正在准备连接 ...");
  465.  
  466. // Welcome the new client
  467. foreach (string Line in ClientHandshakeLines)
  468. {
  469. logger.Log(Line);
  470. if (Line.Contains("Sec-WebSocket-Key1:"))
  471. BuildServerPartialKey(, Line.Substring(Line.IndexOf(":") + ));
  472. if (Line.Contains("Sec-WebSocket-Key2:"))
  473. BuildServerPartialKey(, Line.Substring(Line.IndexOf(":") + ));
  474. if (Line.Contains("Origin:"))
  475. try
  476. {
  477. Handshake = string.Format(Handshake, Line.Substring(Line.IndexOf(":") + ));
  478. }
  479. catch
  480. {
  481. Handshake = string.Format(Handshake, "null");
  482. }
  483. }
  484. // Build the response for the client
  485. byte[] HandshakeText = Encoding.UTF8.GetBytes(Handshake);
  486. byte[] serverHandshakeResponse = new byte[HandshakeText.Length + ];
  487. byte[] serverKey = BuildServerFullKey(last8Bytes);
  488. Array.Copy(HandshakeText, serverHandshakeResponse, HandshakeText.Length);
  489. Array.Copy(serverKey, , serverHandshakeResponse, HandshakeText.Length, );
  490.  
  491. logger.Log("发送握手信息 ...");
  492. ConnectionSocket.BeginSend(serverHandshakeResponse, , HandshakeText.Length + , , HandshakeFinished, null);
  493. logger.Log(Handshake);
  494. }
  495.  
  496. public static String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
  497. {
  498. const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  499. String secWebSocketAccept = String.Empty;
  500. // 1. Combine the request Sec-WebSocket-Key with magic key.
  501. String ret = secWebSocketKey + MagicKEY;
  502. // 2. Compute the SHA1 hash
  503. SHA1 sha = new SHA1CryptoServiceProvider();
  504. byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
  505. // 3. Base64 encode the hash
  506. secWebSocketAccept = Convert.ToBase64String(sha1Hash);
  507. return secWebSocketAccept;
  508. }
  509.  
  510. private void HandshakeFinished(IAsyncResult status)
  511. {
  512. ConnectionSocket.EndSend(status);
  513. ConnectionSocket.BeginReceive(receivedDataBuffer, , receivedDataBuffer.Length, , new AsyncCallback(Read), null);
  514. if (NewConnection != null) NewConnection("", EventArgs.Empty);
  515. }
  516. }
  517.  
  518. public class DataFrame
  519. {
  520. DataFrameHeader _header;
  521. private byte[] _extend = new byte[];
  522. private byte[] _mask = new byte[];
  523. private byte[] _content = new byte[];
  524.  
  525. public DataFrame(byte[] buffer)
  526. {
  527. //帧头
  528. _header = new DataFrameHeader(buffer);
  529.  
  530. //扩展长度
  531. if (_header.Length == )
  532. {
  533. _extend = new byte[];
  534. Buffer.BlockCopy(buffer, , _extend, , );
  535. }
  536. else if (_header.Length == )
  537. {
  538. _extend = new byte[];
  539. Buffer.BlockCopy(buffer, , _extend, , );
  540. }
  541.  
  542. //是否有掩码
  543. if (_header.HasMask)
  544. {
  545. _mask = new byte[];
  546. Buffer.BlockCopy(buffer, _extend.Length + , _mask, , );
  547. }
  548.  
  549. //消息体
  550. if (_extend.Length == )
  551. {
  552. _content = new byte[_header.Length];
  553. Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + , _content, , _content.Length);
  554. }
  555. else if (_extend.Length == )
  556. {
  557. int contentLength = (int)_extend[] * + (int)_extend[];
  558. _content = new byte[contentLength];
  559. Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + , _content, , contentLength > * ? * : contentLength);
  560. }
  561. else
  562. {
  563. long len = ;
  564. int n = ;
  565. for (int i = ; i >= ; i--)
  566. {
  567. len += (int)_extend[i] * n;
  568. n *= ;
  569. }
  570. _content = new byte[len];
  571. Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + , _content, , _content.Length);
  572. }
  573.  
  574. if (_header.HasMask) _content = Mask(_content, _mask);
  575.  
  576. }
  577.  
  578. public DataFrame(string content)
  579. {
  580. _content = Encoding.UTF8.GetBytes(content);
  581. int length = _content.Length;
  582.  
  583. if (length < )
  584. {
  585. _extend = new byte[];
  586. _header = new DataFrameHeader(true, false, false, false, , false, length);
  587. }
  588. else if (length < )
  589. {
  590. _extend = new byte[];
  591. _header = new DataFrameHeader(true, false, false, false, , false, );
  592. _extend[] = (byte)(length / );
  593. _extend[] = (byte)(length % );
  594. }
  595. else
  596. {
  597. _extend = new byte[];
  598. _header = new DataFrameHeader(true, false, false, false, , false, );
  599.  
  600. int left = length;
  601. int unit = ;
  602.  
  603. for (int i = ; i > ; i--)
  604. {
  605. _extend[i] = (byte)(left % unit);
  606. left = left / unit;
  607.  
  608. if (left == )
  609. break;
  610. }
  611. }
  612. }
  613.  
  614. public byte[] GetBytes()
  615. {
  616. byte[] buffer = new byte[ + _extend.Length + _mask.Length + _content.Length];
  617. Buffer.BlockCopy(_header.GetBytes(), , buffer, , );
  618. Buffer.BlockCopy(_extend, , buffer, , _extend.Length);
  619. Buffer.BlockCopy(_mask, , buffer, + _extend.Length, _mask.Length);
  620. Buffer.BlockCopy(_content, , buffer, + _extend.Length + _mask.Length, _content.Length);
  621. return buffer;
  622. }
  623.  
  624. public string Text
  625. {
  626. get
  627. {
  628. if (_header.OpCode != )
  629. return string.Empty;
  630.  
  631. return Encoding.UTF8.GetString(_content);
  632. }
  633. }
  634.  
  635. private byte[] Mask(byte[] data, byte[] mask)
  636. {
  637. for (var i = ; i < data.Length; i++)
  638. {
  639. data[i] = (byte)(data[i] ^ mask[i % ]);
  640. }
  641.  
  642. return data;
  643. }
  644.  
  645. }
  646.  
  647. public class DataFrameHeader
  648. {
  649. private bool _fin;
  650. private bool _rsv1;
  651. private bool _rsv2;
  652. private bool _rsv3;
  653. private sbyte _opcode;
  654. private bool _maskcode;
  655. private sbyte _payloadlength;
  656.  
  657. public bool FIN { get { return _fin; } }
  658.  
  659. public bool RSV1 { get { return _rsv1; } }
  660.  
  661. public bool RSV2 { get { return _rsv2; } }
  662.  
  663. public bool RSV3 { get { return _rsv3; } }
  664.  
  665. public sbyte OpCode { get { return _opcode; } }
  666.  
  667. public bool HasMask { get { return _maskcode; } }
  668.  
  669. public sbyte Length { get { return _payloadlength; } }
  670.  
  671. public DataFrameHeader(byte[] buffer)
  672. {
  673. if (buffer.Length < )
  674. throw new Exception("无效的数据头.");
  675.  
  676. //第一个字节
  677. _fin = (buffer[] & 0x80) == 0x80;
  678. _rsv1 = (buffer[] & 0x40) == 0x40;
  679. _rsv2 = (buffer[] & 0x20) == 0x20;
  680. _rsv3 = (buffer[] & 0x10) == 0x10;
  681. _opcode = (sbyte)(buffer[] & 0x0f);
  682.  
  683. //第二个字节
  684. _maskcode = (buffer[] & 0x80) == 0x80;
  685. _payloadlength = (sbyte)(buffer[] & 0x7f);
  686.  
  687. }
  688.  
  689. //发送封装数据
  690. public DataFrameHeader(bool fin, bool rsv1, bool rsv2, bool rsv3, sbyte opcode, bool hasmask, int length)
  691. {
  692. _fin = fin;
  693. _rsv1 = rsv1;
  694. _rsv2 = rsv2;
  695. _rsv3 = rsv3;
  696. _opcode = opcode;
  697. //第二个字节
  698. _maskcode = hasmask;
  699. _payloadlength = (sbyte)length;
  700. }
  701.  
  702. //返回帧头字节
  703. public byte[] GetBytes()
  704. {
  705. byte[] buffer = new byte[] { , };
  706.  
  707. if (_fin) buffer[] ^= 0x80;
  708. if (_rsv1) buffer[] ^= 0x40;
  709. if (_rsv2) buffer[] ^= 0x20;
  710. if (_rsv3) buffer[] ^= 0x10;
  711.  
  712. buffer[] ^= (byte)_opcode;
  713.  
  714. if (_maskcode) buffer[] ^= 0x80;
  715.  
  716. buffer[] ^= (byte)_payloadlength;
  717.  
  718. return buffer;
  719. }
  720. }
  721. }

WebSocket客户端:

  1. <html>
  2. <head>
  3. <meta http-equiv="Content-Type" content="text/html;charset=gb2312">
  4. <title>Web sockets test</title>
  5. <style type="text/css">
  6. .container { font-family: "Courier New"; width: 680px; height: 300px; overflow: auto; border: 1px solid black; }
  7.  
  8. .LockOff { display: none; visibility: hidden; }
  9.  
  10. .LockOn { display: block; visibility: visible; position: absolute; z-index: 999; top: 0px; left: 0px; width: 1024%; height: 768%; background-color: #ccc; text-align: center; padding-top: 20%; filter: alpha(opacity=75); opacity: 0.75; }
  11. </style>
  12.  
  13. <script src="jquery-min.js" type="text/javascript"></script>
  14. <script type="text/javascript">
  15. var ws;
  16. var SocketCreated = false;
  17. var isUserloggedout = false;
  18.  
  19. function lockOn(str) {
  20. var lock = document.getElementById('skm_LockPane');
  21. if (lock)
  22. lock.className = 'LockOn';
  23. lock.innerHTML = str;
  24. }
  25.  
  26. function lockOff() {
  27. var lock = document.getElementById('skm_LockPane');
  28. lock.className = 'LockOff';
  29. }
  30.  
  31. function ToggleConnectionClicked() {
  32. if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {
  33. lockOn("离开聊天室...");
  34. SocketCreated = false;
  35. isUserloggedout = true;
  36. ws.close();
  37. } else {
  38. lockOn("进入聊天室...");
  39. Log("准备连接到聊天服务器 ...");
  40. try {
  41. if ("WebSocket" in window) {
  42. ws = new WebSocket("ws://" + document.getElementById("Connection").value);
  43. }
  44. else if ("MozWebSocket" in window) {
  45. ws = new MozWebSocket("ws://" + document.getElementById("Connection").value);
  46. }
  47.  
  48. SocketCreated = true;
  49. isUserloggedout = false;
  50. } catch (ex) {
  51. Log(ex, "ERROR");
  52. return;
  53. }
  54. document.getElementById("ToggleConnection").innerHTML = "断开";
  55. ws.onopen = WSonOpen;
  56. ws.onmessage = WSonMessage;
  57. ws.onclose = WSonClose;
  58. ws.onerror = WSonError;
  59. }
  60. };
  61.  
  62. function WSonOpen() {
  63. lockOff();
  64. Log("连接已经建立。", "OK");
  65. $("#SendDataContainer").show();
  66. ws.send("login:" + document.getElementById("txtName").value);
  67. };
  68.  
  69. function WSonMessage(event) {
  70. Log(event.data);
  71. };
  72.  
  73. function WSonClose() {
  74. lockOff();
  75. if (isUserloggedout)
  76. Log("【" + document.getElementById("txtName").value + "】离开了聊天室!");
  77. document.getElementById("ToggleConnection").innerHTML = "连接";
  78. $("#SendDataContainer").hide();
  79. };
  80.  
  81. function WSonError() {
  82. lockOff();
  83. Log("远程连接中断。", "ERROR");
  84. };
  85.  
  86. function SendDataClicked() {
  87. if (document.getElementById("DataToSend").value.trim() != "") {
  88. ws.send(document.getElementById("txtName").value + "说 :\"" + document.getElementById("DataToSend").value + "\"");
  89. document.getElementById("DataToSend").value = "";
  90. }
  91. };
  92.  
  93. function Log(Text, MessageType) {
  94. if (MessageType == "OK") Text = "<span style='color: green;'>" + Text + "</span>";
  95. if (MessageType == "ERROR") Text = "<span style='color: red;'>" + Text + "</span>";
  96. document.getElementById("LogContainer").innerHTML = document.getElementById("LogContainer").innerHTML + Text + "<br />";
  97. var LogContainer = document.getElementById("LogContainer");
  98. LogContainer.scrollTop = LogContainer.scrollHeight;
  99. };
  100.  
  101. $(document).ready(function () {
  102. $("#SendDataContainer").hide();
  103. var WebSocketsExist = true;
  104. try {
  105. var dummy = new WebSocket("ws://localhost:8989/test");
  106. } catch (ex) {
  107. try {
  108. webSocket = new MozWebSocket("ws://localhost:8989/test");
  109. }
  110. catch (ex) {
  111. WebSocketsExist = false;
  112. }
  113. }
  114.  
  115. if (WebSocketsExist) {
  116. Log("您的浏览器支持WebSocket. 您可以尝试连接到聊天服务器!", "OK");
  117. document.getElementById("Connection").value = "192.168.1.108:4141/chat";
  118. } else {
  119. Log("您的浏览器不支持WebSocket。请选择其他的浏览器再尝试连接服务器。", "ERROR");
  120. document.getElementById("ToggleConnection").disabled = true;
  121. }
  122.  
  123. $("#DataToSend").keypress(function (evt) {
  124. if (evt.keyCode == 13) {
  125. $("#SendData").click();
  126. evt.preventDefault();
  127. }
  128. })
  129. });
  130.  
  131. </script>
  132. </head>
  133. <body>
  134. <div id="skm_LockPane" class="LockOff"></div>
  135. <form id="form1" runat="server">
  136. <h1>Web Socket 聊天室</h1>
  137. <br />
  138. <div>
  139. 按下连接按钮,会通过WebSocket发起一个到聊天浏览器的连接。
  140. </div>
  141. 服务器地址:
  142. <input type="text" id="Connection" />
  143. 用户名:
  144. <input type="text" id="txtName" value="黄晓安" />
  145. <button id='ToggleConnection' type="button" onclick='ToggleConnectionClicked();'>连接</button>
  146. <br />
  147. <br />
  148. <div id='LogContainer' class='container'></div>
  149. <br />
  150. <div id='SendDataContainer'>
  151. <input type="text" id="DataToSend" size="88" />
  152. <button id='SendData' type="button" onclick='SendDataClicked();'>发送</button>
  153. </div>
  154. <br />
  155. </form>
  156. </body>
  157. </html>

WebSocket 的局限性

WebSocket 的优点已经列举得很多了,但是作为一个正在演变中的 Web 规范,我们也要看到目前用 Websocket 构建应用程序的一些风险。首先,WebSocket 规范目前还处于草案阶段,也就是它的规范和 API 还是有变动的可能,另外的一个风险就是微软的 IE 作为占市场份额最大的浏览器,和其他的主流浏览器相比,对 HTML5 的支持是比较差的,这是我们在构建企业级的 Web 应用的时候必须要考虑的一个问题。

具体详解地址:http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/

HTML5学习之WebSocket通讯(六)的更多相关文章

  1. HTML5学习笔记(十六):原型、类和继承【JS核心知识点】

    理解原型 在JavaScript中,只要声明了一个函数,就会为该函数创建一个名为prototype的属性,该属性指向当前函数的原型对象. 而函数的原型对象有一个constructor属性,该属性指向刚 ...

  2. HTML5学习总结-08 WebSocket 服务器推送

    一 WebSocket 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展 ...

  3. HTML5 学习总结(一)——HTML5概要与新增标签

    一.HTML5概要 1.1.为什么需要HTML5 HTML4陈旧不能满足日益发展的互联网需要,特别是移动互联网.为了增强浏览器功能Flash被广泛使用,但安全与稳定堪忧,不适合在移动端使用(耗电.触摸 ...

  4. 感谢各位亲们的大力支持,免费的HTML5学习课程《HTML5网页开发实例具体解释》连载已经结束了!

    感谢各位亲们的大力支持,免费的HTML5学习课程<HTML5网页开发实例具体解释>连载已经结束了.  有兴趣的读者能够看我的博客,也能够看以下的链接逐个学习: 当里个当.免费的HTML5连 ...

  5. HTML5学习总结——canvas绘制象棋(canvas绘图)

    一.HTML5学习总结——canvas绘制象棋 1.第一次:canvas绘制象棋(笨方法)示例代码: <!DOCTYPE html> <html> <head> & ...

  6. HTML5 学习笔记(一)——HTML5概要与新增标签

    目录 一.HTML5概要 1.1.为什么需要HTML5 1.2.什么是HTML5 1.3.HTML5现状及浏览器支持 1.4.HTML5特性 1.5.HTML5优点与缺点 1.5.1.优点 1.5.2 ...

  7. [HTML5] 飞龙天惊-HTML5学习系列

    飞龙天惊 cnblog URL:http://www.cnblogs.com/fly_dragon/ Html5 学习系列(一)认识HTML5 http://www.cnblogs.com/fly_d ...

  8. HTML5 学习笔记--------》HTML5概要与新增标签!

      一.HTML5概要 1.1.为什么需要HTML5 HTML4陈旧不能满足日益发展的互联网需要,特别是移动互联网.为了增强浏览器功能Flash被广泛使用,但安全与稳定堪忧,不适合在移动端使用(耗电. ...

  9. 带你认识HTML5中的WebSocket

    这篇文章主要介绍了带你认识HTML5中的WebSocket,本文讲解了HTML5 中的 WebSocket API 是个什么东东.HTML5 中的 WebSocket API 的用法.带Socket. ...

随机推荐

  1. JS判断设备的类型

    利用JS判断浏览器的用户代理,从而获得设备类型.利用这个方法可以获得用户的设备是移动设备还是PC设备.     JS代码如下:       function browerType() {       ...

  2. JQGrid 参数、属性API

    JQGrid是一个在jquery基础上做的一个表格控件,以ajax的方式和服务器端通信. JQGrid Demo 是一个在线的演示项目.在这里,可以知道jqgrid可以做什么事情. 下面是转自其他人b ...

  3. Opencv,腐蚀,膨胀,轮廓检测,轮廓外接多边形

    //形态学腐蚀 cvErode(pDstImage,pDstImage,,); //形态学膨胀 cvDilate(pDstImage,pDstImage,,); //中值滤波 cvSmooth(pDs ...

  4. static小结

    1.隐藏:编译多个文件时,所有未加static的全局变量.全局函数都具有全局可见性. 如果加了static,就会对其他源文件隐藏,利用这一特性可以在不同文件中定义相同的 变量名或函数名,而不用担心冲突 ...

  5. mac 搭建APK反编译环境[转]

    APKtool 用途:获取mainifest.xml res等资源文件 下载:http://ibotpeaches.github.io/Apktool/install/ 使用:apktool d te ...

  6. Android客户端的图形化拖放操作的设计实现

    为什么要拖放?拖放在某些UI交互中可以简化用户操作. 拖放的步骤包括哪些?“Drag and Drop”,拖放,顾名思义,总共就分三步:1, 开始拖起来:2, 正在拖:3, 放下,进行操作:在这三步里 ...

  7. 项目: 推送水木文章到Kindle

    代码在github:https://github.com/Viyu/PushNewsmth2Mail 当年买Kindle的时候,想再Kindle上阅读水木的帖子,但Kindle的Web上网体验太差,想 ...

  8. 基于用户相似性的协同过滤——Python实现

    代码基本来自项亮的<推荐系统实践>,把书上的伪代码具体实现,还参考了https://www.douban.com/note/336280497/ 还可以加入对用户相似性的归一化操作,效果会 ...

  9. POCO浅探

    POCO作用类似于POJO.POJO的内在含义是指那些从来没有任何类继承.也没有实现任何接口,更是没有被其他框架侵入的JAVA对象.POCO的含义与此是一致的.POJO的意义就在于它的简单灵活性,能够 ...

  10. ios NSURLSession completeHandler默认调用quque

    注意 , [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSU ...