WebSocket是下一代客户端-服务器的异步通信方法.
WebSocket最伟大之处在于服务器和客户端可以在任意时刻相互推送信息
WebSocket允许跨域通信
Ajax技术需要客户端发起请求,WebSocket服务器和客户端可以彼此相互推送信息
 
下面实现一个简单的实时多人聊天系统
WebSocket服务端:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks; namespace WebSocketServer
{
class Program
{
static void Main(string[] args)
{
WebSocketServerTest WSServerTest = new WebSocketServerTest();
WSServerTest.Start();
}
} public class WebSocketServerTest : IDisposable
{
private WebSocketServer WSServer;
public WebSocketServerTest()
{
//使用默认的设置
WSServer = new WebSocketServer();
} public void Dispose()
{
Close();
} private void Close()
{
WSServer.Dispose();
GC.SuppressFinalize(this);
} ~WebSocketServerTest()
{
Close();
} public void Start()
{
WSServer.NewConnection += new NewConnectionEventHandler(WSServer_NewConnection);
WSServer.Disconnected += new DisconnectedEventHandler(WSServer_Disconnected);
WSServer.StartServer();
} void WSServer_Disconnected(Object sender, EventArgs e)
{
} void WSServer_NewConnection(string loginName, EventArgs e)
{
}
} public class Logger
{
public bool LogEvents { get; set; } public Logger()
{
LogEvents = true;
} public void Log(string Text)
{
if (LogEvents) Console.WriteLine(Text);
}
} public enum ServerStatusLevel { Off, WaitingConnection, ConnectionEstablished }; public delegate void NewConnectionEventHandler(string loginName, EventArgs e);
public delegate void DataReceivedEventHandler(Object sender, string message, EventArgs e);
public delegate void DisconnectedEventHandler(Object sender, EventArgs e);
public delegate void BroadcastEventHandler(string message, EventArgs e); public class WebSocketServer : IDisposable
{
private bool AlreadyDisposed;
private Socket Listener;
private int ConnectionsQueueLength;
private int MaxBufferSize;
private string Handshake;
private StreamReader ConnectionReader;
private StreamWriter ConnectionWriter;
private Logger logger;
private byte[] FirstByte;
private byte[] LastByte;
private byte[] ServerKey1;
private byte[] ServerKey2; List<SocketConnection> connectionSocketList = new List<SocketConnection>(); public ServerStatusLevel Status { get; private set; }
public int ServerPort { get; set; }
public string ServerLocation { get; set; }
public string ConnectionOrigin { get; set; }
public bool LogEvents
{
get { return logger.LogEvents; }
set { logger.LogEvents = value; }
} public event NewConnectionEventHandler NewConnection;
public event DataReceivedEventHandler DataReceived;
public event DisconnectedEventHandler Disconnected; private void Initialize()
{
AlreadyDisposed = false;
logger = new Logger(); Status = ServerStatusLevel.Off;
ConnectionsQueueLength = ;
MaxBufferSize = * ;
FirstByte = new byte[MaxBufferSize];
LastByte = new byte[MaxBufferSize];
FirstByte[] = 0x00;
LastByte[] = 0xFF;
logger.LogEvents = true;
} public WebSocketServer()
{
ServerPort = ;
ServerLocation = string.Format("ws://{0}:4141/chat", getLocalmachineIPAddress());
Initialize();
} public WebSocketServer(int serverPort, string serverLocation, string connectionOrigin)
{
ServerPort = serverPort;
ConnectionOrigin = connectionOrigin;
ServerLocation = serverLocation;
Initialize();
} ~WebSocketServer()
{
Close();
} public void Dispose()
{
Close();
} private void Close()
{
if (!AlreadyDisposed)
{
AlreadyDisposed = true;
if (Listener != null) Listener.Close();
foreach (SocketConnection item in connectionSocketList)
{
item.ConnectionSocket.Close();
}
connectionSocketList.Clear();
GC.SuppressFinalize(this);
}
} public static IPAddress getLocalmachineIPAddress()
{
string strHostName = Dns.GetHostName();
IPHostEntry ipEntry = Dns.GetHostEntry(strHostName); foreach (IPAddress ip in ipEntry.AddressList)
{
//IPV4
if (ip.AddressFamily == AddressFamily.InterNetwork)
return ip;
} return ipEntry.AddressList[];
} public void StartServer()
{
Char char1 = Convert.ToChar(); Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
Listener.Bind(new IPEndPoint(getLocalmachineIPAddress(), ServerPort)); Listener.Listen(ConnectionsQueueLength); logger.Log(string.Format("聊天服务器启动。监听地址:{0}, 端口:{1}", getLocalmachineIPAddress(), ServerPort));
logger.Log(string.Format("WebSocket服务器地址: ws://{0}:{1}/chat", getLocalmachineIPAddress(), ServerPort)); while (true)
{
Socket sc = Listener.Accept(); if (sc != null)
{
System.Threading.Thread.Sleep();
SocketConnection socketConn = new SocketConnection();
socketConn.ConnectionSocket = sc;
socketConn.NewConnection += new NewConnectionEventHandler(socketConn_NewConnection);
socketConn.DataReceived += new DataReceivedEventHandler(socketConn_BroadcastMessage);
socketConn.Disconnected += new DisconnectedEventHandler(socketConn_Disconnected); socketConn.ConnectionSocket.BeginReceive(socketConn.receivedDataBuffer,
, socketConn.receivedDataBuffer.Length,
, new AsyncCallback(socketConn.ManageHandshake),
socketConn.ConnectionSocket.Available);
connectionSocketList.Add(socketConn);
}
}
} void socketConn_Disconnected(Object sender, EventArgs e)
{
SocketConnection sConn = sender as SocketConnection;
if (sConn != null)
{
Send(string.Format("【{0}】离开了聊天室!", sConn.Name));
sConn.ConnectionSocket.Close();
connectionSocketList.Remove(sConn);
}
} void socketConn_BroadcastMessage(Object sender, string message, EventArgs e)
{
if (message.IndexOf("login:") != -)
{
SocketConnection sConn = sender as SocketConnection;
sConn.Name = message.Substring(message.IndexOf("login:") + "login:".Length);
message = string.Format("欢迎【{0}】来到聊天室!", message.Substring(message.IndexOf("login:") + "login:".Length));
}
Send(message);
} void socketConn_NewConnection(string name, EventArgs e)
{
if (NewConnection != null)
NewConnection(name, EventArgs.Empty);
} public void Send(string message)
{
foreach (SocketConnection item in connectionSocketList)
{
if (!item.ConnectionSocket.Connected) return;
try
{
if (item.IsDataMasked)
{
DataFrame dr = new DataFrame(message);
item.ConnectionSocket.Send(dr.GetBytes());
}
else
{
item.ConnectionSocket.Send(FirstByte);
item.ConnectionSocket.Send(Encoding.UTF8.GetBytes(message));
item.ConnectionSocket.Send(LastByte);
}
}
catch (Exception ex)
{
logger.Log(ex.Message);
}
}
}
} public class SocketConnection
{
private Logger logger; private string name;
public string Name
{
get { return name; }
set { name = value; }
} private Boolean isDataMasked;
public Boolean IsDataMasked
{
get { return isDataMasked; }
set { isDataMasked = value; }
} public Socket ConnectionSocket; private int MaxBufferSize;
private string Handshake;
private string New_Handshake; public byte[] receivedDataBuffer;
private byte[] FirstByte;
private byte[] LastByte;
private byte[] ServerKey1;
private byte[] ServerKey2; public event NewConnectionEventHandler NewConnection;
public event DataReceivedEventHandler DataReceived;
public event DisconnectedEventHandler Disconnected; public SocketConnection()
{
logger = new Logger();
MaxBufferSize = * ;
receivedDataBuffer = new byte[MaxBufferSize];
FirstByte = new byte[MaxBufferSize];
LastByte = new byte[MaxBufferSize];
FirstByte[] = 0x00;
LastByte[] = 0xFF; Handshake = "HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine;
Handshake += "Upgrade: WebSocket" + Environment.NewLine;
Handshake += "Connection: Upgrade" + Environment.NewLine;
Handshake += "Sec-WebSocket-Origin: " + "{0}" + Environment.NewLine;
Handshake += string.Format("Sec-WebSocket-Location: " + "ws://{0}:4141/chat" + Environment.NewLine, WebSocketServer.getLocalmachineIPAddress());
Handshake += Environment.NewLine; New_Handshake = "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;
New_Handshake += "Upgrade: WebSocket" + Environment.NewLine;
New_Handshake += "Connection: Upgrade" + Environment.NewLine;
New_Handshake += "Sec-WebSocket-Accept: {0}" + Environment.NewLine;
New_Handshake += Environment.NewLine;
} private void Read(IAsyncResult status)
{
if (!ConnectionSocket.Connected) return;
string messageReceived = string.Empty;
DataFrame dr = new DataFrame(receivedDataBuffer); try
{
if (!this.isDataMasked)
{
// Web Socket protocol: messages are sent with 0x00 and 0xFF as padding bytes
System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
int startIndex = ;
int endIndex = ; // Search for the start byte
while (receivedDataBuffer[startIndex] == FirstByte[]) startIndex++;
// Search for the end byte
endIndex = startIndex + ;
while (receivedDataBuffer[endIndex] != LastByte[] && endIndex != MaxBufferSize - ) endIndex++;
if (endIndex == MaxBufferSize - ) endIndex = MaxBufferSize; // Get the message
messageReceived = decoder.GetString(receivedDataBuffer, startIndex, endIndex - startIndex);
}
else
{
messageReceived = dr.Text;
} if ((messageReceived.Length == MaxBufferSize && messageReceived[] == Convert.ToChar()) ||
messageReceived.Length == )
{
logger.Log("接受到的信息 [\"" + string.Format("logout:{0}", this.name) + "\"]");
if (Disconnected != null)
Disconnected(this, EventArgs.Empty);
}
else
{
if (DataReceived != null)
{
logger.Log("接受到的信息 [\"" + messageReceived + "\"]");
DataReceived(this, messageReceived, EventArgs.Empty);
}
Array.Clear(receivedDataBuffer, , receivedDataBuffer.Length);
ConnectionSocket.BeginReceive(receivedDataBuffer, , receivedDataBuffer.Length, , new AsyncCallback(Read), null);
}
}
catch (Exception ex)
{
logger.Log(ex.Message);
logger.Log("Socket连接将会被终止。");
if (Disconnected != null)
Disconnected(this, EventArgs.Empty);
}
} private void BuildServerPartialKey(int keyNum, string clientKey)
{
string partialServerKey = "";
byte[] currentKey;
int spacesNum = ;
char[] keyChars = clientKey.ToCharArray();
foreach (char currentChar in keyChars)
{
if (char.IsDigit(currentChar)) partialServerKey += currentChar;
if (char.IsWhiteSpace(currentChar)) spacesNum++;
}
try
{
currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey) / spacesNum));
if (BitConverter.IsLittleEndian) Array.Reverse(currentKey); if (keyNum == ) ServerKey1 = currentKey;
else ServerKey2 = currentKey;
}
catch
{
if (ServerKey1 != null) Array.Clear(ServerKey1, , ServerKey1.Length);
if (ServerKey2 != null) Array.Clear(ServerKey2, , ServerKey2.Length);
}
} private byte[] BuildServerFullKey(byte[] last8Bytes)
{
byte[] concatenatedKeys = new byte[];
Array.Copy(ServerKey1, , concatenatedKeys, , );
Array.Copy(ServerKey2, , concatenatedKeys, , );
Array.Copy(last8Bytes, , concatenatedKeys, , ); // MD5 Hash
System.Security.Cryptography.MD5 MD5Service = System.Security.Cryptography.MD5.Create();
return MD5Service.ComputeHash(concatenatedKeys);
} public void ManageHandshake(IAsyncResult status)
{
string header = "Sec-WebSocket-Version:";
int HandshakeLength = (int)status.AsyncState;
byte[] last8Bytes = new byte[]; System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
String rawClientHandshake = decoder.GetString(receivedDataBuffer, , HandshakeLength); Array.Copy(receivedDataBuffer, HandshakeLength - , last8Bytes, , ); //现在使用的是比较新的Websocket协议
if (rawClientHandshake.IndexOf(header) != -)
{
this.isDataMasked = true;
string[] rawClientHandshakeLines = rawClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
string acceptKey = "";
foreach (string Line in rawClientHandshakeLines)
{
Console.WriteLine(Line);
if (Line.Contains("Sec-WebSocket-Key:"))
{
acceptKey = ComputeWebSocketHandshakeSecurityHash09(Line.Substring(Line.IndexOf(":") + ));
}
} New_Handshake = string.Format(New_Handshake, acceptKey);
byte[] newHandshakeText = Encoding.UTF8.GetBytes(New_Handshake);
ConnectionSocket.BeginSend(newHandshakeText, , newHandshakeText.Length, , HandshakeFinished, null);
return;
} string ClientHandshake = decoder.GetString(receivedDataBuffer, , HandshakeLength - ); string[] ClientHandshakeLines = ClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries); logger.Log("新的连接请求来自" + ConnectionSocket.LocalEndPoint + "。正在准备连接 ..."); // Welcome the new client
foreach (string Line in ClientHandshakeLines)
{
logger.Log(Line);
if (Line.Contains("Sec-WebSocket-Key1:"))
BuildServerPartialKey(, Line.Substring(Line.IndexOf(":") + ));
if (Line.Contains("Sec-WebSocket-Key2:"))
BuildServerPartialKey(, Line.Substring(Line.IndexOf(":") + ));
if (Line.Contains("Origin:"))
try
{
Handshake = string.Format(Handshake, Line.Substring(Line.IndexOf(":") + ));
}
catch
{
Handshake = string.Format(Handshake, "null");
}
}
// Build the response for the client
byte[] HandshakeText = Encoding.UTF8.GetBytes(Handshake);
byte[] serverHandshakeResponse = new byte[HandshakeText.Length + ];
byte[] serverKey = BuildServerFullKey(last8Bytes);
Array.Copy(HandshakeText, serverHandshakeResponse, HandshakeText.Length);
Array.Copy(serverKey, , serverHandshakeResponse, HandshakeText.Length, ); logger.Log("发送握手信息 ...");
ConnectionSocket.BeginSend(serverHandshakeResponse, , HandshakeText.Length + , , HandshakeFinished, null);
logger.Log(Handshake);
} public static String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
{
const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
String secWebSocketAccept = String.Empty;
// 1. Combine the request Sec-WebSocket-Key with magic key.
String ret = secWebSocketKey + MagicKEY;
// 2. Compute the SHA1 hash
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
// 3. Base64 encode the hash
secWebSocketAccept = Convert.ToBase64String(sha1Hash);
return secWebSocketAccept;
} private void HandshakeFinished(IAsyncResult status)
{
ConnectionSocket.EndSend(status);
ConnectionSocket.BeginReceive(receivedDataBuffer, , receivedDataBuffer.Length, , new AsyncCallback(Read), null);
if (NewConnection != null) NewConnection("", EventArgs.Empty);
}
} public class DataFrame
{
DataFrameHeader _header;
private byte[] _extend = new byte[];
private byte[] _mask = new byte[];
private byte[] _content = new byte[]; public DataFrame(byte[] buffer)
{
//帧头
_header = new DataFrameHeader(buffer); //扩展长度
if (_header.Length == )
{
_extend = new byte[];
Buffer.BlockCopy(buffer, , _extend, , );
}
else if (_header.Length == )
{
_extend = new byte[];
Buffer.BlockCopy(buffer, , _extend, , );
} //是否有掩码
if (_header.HasMask)
{
_mask = new byte[];
Buffer.BlockCopy(buffer, _extend.Length + , _mask, , );
} //消息体
if (_extend.Length == )
{
_content = new byte[_header.Length];
Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + , _content, , _content.Length);
}
else if (_extend.Length == )
{
int contentLength = (int)_extend[] * + (int)_extend[];
_content = new byte[contentLength];
Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + , _content, , contentLength > * ? * : contentLength);
}
else
{
long len = ;
int n = ;
for (int i = ; i >= ; i--)
{
len += (int)_extend[i] * n;
n *= ;
}
_content = new byte[len];
Buffer.BlockCopy(buffer, _extend.Length + _mask.Length + , _content, , _content.Length);
} if (_header.HasMask) _content = Mask(_content, _mask); } public DataFrame(string content)
{
_content = Encoding.UTF8.GetBytes(content);
int length = _content.Length; if (length < )
{
_extend = new byte[];
_header = new DataFrameHeader(true, false, false, false, , false, length);
}
else if (length < )
{
_extend = new byte[];
_header = new DataFrameHeader(true, false, false, false, , false, );
_extend[] = (byte)(length / );
_extend[] = (byte)(length % );
}
else
{
_extend = new byte[];
_header = new DataFrameHeader(true, false, false, false, , false, ); int left = length;
int unit = ; for (int i = ; i > ; i--)
{
_extend[i] = (byte)(left % unit);
left = left / unit; if (left == )
break;
}
}
} public byte[] GetBytes()
{
byte[] buffer = new byte[ + _extend.Length + _mask.Length + _content.Length];
Buffer.BlockCopy(_header.GetBytes(), , buffer, , );
Buffer.BlockCopy(_extend, , buffer, , _extend.Length);
Buffer.BlockCopy(_mask, , buffer, + _extend.Length, _mask.Length);
Buffer.BlockCopy(_content, , buffer, + _extend.Length + _mask.Length, _content.Length);
return buffer;
} public string Text
{
get
{
if (_header.OpCode != )
return string.Empty; return Encoding.UTF8.GetString(_content);
}
} private byte[] Mask(byte[] data, byte[] mask)
{
for (var i = ; i < data.Length; i++)
{
data[i] = (byte)(data[i] ^ mask[i % ]);
} return data;
} } public class DataFrameHeader
{
private bool _fin;
private bool _rsv1;
private bool _rsv2;
private bool _rsv3;
private sbyte _opcode;
private bool _maskcode;
private sbyte _payloadlength; public bool FIN { get { return _fin; } } public bool RSV1 { get { return _rsv1; } } public bool RSV2 { get { return _rsv2; } } public bool RSV3 { get { return _rsv3; } } public sbyte OpCode { get { return _opcode; } } public bool HasMask { get { return _maskcode; } } public sbyte Length { get { return _payloadlength; } } public DataFrameHeader(byte[] buffer)
{
if (buffer.Length < )
throw new Exception("无效的数据头."); //第一个字节
_fin = (buffer[] & 0x80) == 0x80;
_rsv1 = (buffer[] & 0x40) == 0x40;
_rsv2 = (buffer[] & 0x20) == 0x20;
_rsv3 = (buffer[] & 0x10) == 0x10;
_opcode = (sbyte)(buffer[] & 0x0f); //第二个字节
_maskcode = (buffer[] & 0x80) == 0x80;
_payloadlength = (sbyte)(buffer[] & 0x7f); } //发送封装数据
public DataFrameHeader(bool fin, bool rsv1, bool rsv2, bool rsv3, sbyte opcode, bool hasmask, int length)
{
_fin = fin;
_rsv1 = rsv1;
_rsv2 = rsv2;
_rsv3 = rsv3;
_opcode = opcode;
//第二个字节
_maskcode = hasmask;
_payloadlength = (sbyte)length;
} //返回帧头字节
public byte[] GetBytes()
{
byte[] buffer = new byte[] { , }; if (_fin) buffer[] ^= 0x80;
if (_rsv1) buffer[] ^= 0x40;
if (_rsv2) buffer[] ^= 0x20;
if (_rsv3) buffer[] ^= 0x10; buffer[] ^= (byte)_opcode; if (_maskcode) buffer[] ^= 0x80; buffer[] ^= (byte)_payloadlength; return buffer;
}
}
}

WebSocket客户端:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312">
<title>Web sockets test</title>
<style type="text/css">
.container { font-family: "Courier New"; width: 680px; height: 300px; overflow: auto; border: 1px solid black; } .LockOff { display: none; visibility: hidden; } .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; }
</style> <script src="jquery-min.js" type="text/javascript"></script>
<script type="text/javascript">
var ws;
var SocketCreated = false;
var isUserloggedout = false; function lockOn(str) {
var lock = document.getElementById('skm_LockPane');
if (lock)
lock.className = 'LockOn';
lock.innerHTML = str;
} function lockOff() {
var lock = document.getElementById('skm_LockPane');
lock.className = 'LockOff';
} function ToggleConnectionClicked() {
if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {
lockOn("离开聊天室...");
SocketCreated = false;
isUserloggedout = true;
ws.close();
} else {
lockOn("进入聊天室...");
Log("准备连接到聊天服务器 ...");
try {
if ("WebSocket" in window) {
ws = new WebSocket("ws://" + document.getElementById("Connection").value);
}
else if ("MozWebSocket" in window) {
ws = new MozWebSocket("ws://" + document.getElementById("Connection").value);
} SocketCreated = true;
isUserloggedout = false;
} catch (ex) {
Log(ex, "ERROR");
return;
}
document.getElementById("ToggleConnection").innerHTML = "断开";
ws.onopen = WSonOpen;
ws.onmessage = WSonMessage;
ws.onclose = WSonClose;
ws.onerror = WSonError;
}
}; function WSonOpen() {
lockOff();
Log("连接已经建立。", "OK");
$("#SendDataContainer").show();
ws.send("login:" + document.getElementById("txtName").value);
}; function WSonMessage(event) {
Log(event.data);
}; function WSonClose() {
lockOff();
if (isUserloggedout)
Log("【" + document.getElementById("txtName").value + "】离开了聊天室!");
document.getElementById("ToggleConnection").innerHTML = "连接";
$("#SendDataContainer").hide();
}; function WSonError() {
lockOff();
Log("远程连接中断。", "ERROR");
}; function SendDataClicked() {
if (document.getElementById("DataToSend").value.trim() != "") {
ws.send(document.getElementById("txtName").value + "说 :\"" + document.getElementById("DataToSend").value + "\"");
document.getElementById("DataToSend").value = "";
}
}; function Log(Text, MessageType) {
if (MessageType == "OK") Text = "<span style='color: green;'>" + Text + "</span>";
if (MessageType == "ERROR") Text = "<span style='color: red;'>" + Text + "</span>";
document.getElementById("LogContainer").innerHTML = document.getElementById("LogContainer").innerHTML + Text + "<br />";
var LogContainer = document.getElementById("LogContainer");
LogContainer.scrollTop = LogContainer.scrollHeight;
}; $(document).ready(function () {
$("#SendDataContainer").hide();
var WebSocketsExist = true;
try {
var dummy = new WebSocket("ws://localhost:8989/test");
} catch (ex) {
try {
webSocket = new MozWebSocket("ws://localhost:8989/test");
}
catch (ex) {
WebSocketsExist = false;
}
} if (WebSocketsExist) {
Log("您的浏览器支持WebSocket. 您可以尝试连接到聊天服务器!", "OK");
document.getElementById("Connection").value = "192.168.1.108:4141/chat";
} else {
Log("您的浏览器不支持WebSocket。请选择其他的浏览器再尝试连接服务器。", "ERROR");
document.getElementById("ToggleConnection").disabled = true;
} $("#DataToSend").keypress(function (evt) {
if (evt.keyCode == 13) {
$("#SendData").click();
evt.preventDefault();
}
})
}); </script>
</head>
<body>
<div id="skm_LockPane" class="LockOff"></div>
<form id="form1" runat="server">
<h1>Web Socket 聊天室</h1>
<br />
<div>
按下连接按钮,会通过WebSocket发起一个到聊天浏览器的连接。
</div>
服务器地址:
<input type="text" id="Connection" />
用户名:
<input type="text" id="txtName" value="黄晓安" />
<button id='ToggleConnection' type="button" onclick='ToggleConnectionClicked();'>连接</button>
<br />
<br />
<div id='LogContainer' class='container'></div>
<br />
<div id='SendDataContainer'>
<input type="text" id="DataToSend" size="88" />
<button id='SendData' type="button" onclick='SendDataClicked();'>发送</button>
</div>
<br />
</form>
</body>
</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. Struts2 Action 动态传参数

    Struts2的两个Action之间传参的问题. 需求功能是这样:Action1 获取数据库配置内容,得到相应Model的 动态URL ,这里的URL 有的是Action有的是JSP页面. 1.使用r ...

  2. BSON 1.0版本规范(翻译)

    BSON 1.0版本规范 本文翻译自 http://bsonspec.org/spec.html BSON是用于存储零个或多个键/值对为一个单一的实体的一个二进制格式.我们称这个实体为文档(Docum ...

  3. php之CI框架多语言的用法

    public function index() { // 加载语言包,可以加载多个 $this->lang->load('email'); echo $this->lang-> ...

  4. Colorful tree

    cnbb 我被数组清零卡了一天.. 子树改色询问子树颜色数.. 先考虑颜色为x的节点对祖先答案的贡献,那么我们考虑把所有这些节点都搞出来,按dfs序排序,然后考虑每个节点a掌管的祖先是它和按dfs序的 ...

  5. ASC47B borderless

    题目描述 border集合为{NULL,str}的串str称为borderless串. border即KMP里的那个. 字符集{'a','b'},给定长度n,求第k(给定)小的borderless串. ...

  6. jq鼠标隐藏显示的方法

    <div style="width:300px; float:left;">        <div onmouseover="testOnmouseO ...

  7. 【leetcode】Convert Sorted Array to Binary Search Tree

    Convert Sorted Array to Binary Search Tree Given an array where elements are sorted in ascending ord ...

  8. 【leetcode】Unique Paths II

    Unique Paths II Total Accepted: 22828 Total Submissions: 81414My Submissions Follow up for "Uni ...

  9. unity3d项目文件目录发布后,对应的ios/android应用目录[转]

    Unity3d的Resource.AssetBundle与手游动态更新的报告,在这里分享一下,希望能够对各位用Unity的朋友有些许帮助.目录:1.Unity的资源数据加载2.Resource.Str ...

  10. 【python】An Introduction to Interactive Programming in Python(week two)

    This is a note for https://class.coursera.org/interactivepython-005 In week two, I have learned: 1.e ...