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. Qt5 程序启动画面动图效果

    2333终于实现动图,先弄了一个窗口去掉标题栏假装就是启动画面了,还是那只萌萌的猫这次会动了! 基类用的是QWidget  类名称MainView #ifndef MAINVIEW_H #define ...

  2. OpenGL官方教程——着色器语言概述

    OpenGL官方教程——着色器语言概述 OpenGL官方教程——着色器语言概述 可编程图形硬件管线(流水线) 可编程顶点处理器 可编程几何处理器 可编程片元处理器 语言 可编程图形硬件管线(流水线) ...

  3. BZOJ 1041

    题目描述 给出\(n\),求\(x^2+y^2=n^2,x,y,z\in \mathbb{Z}\)的解数. 复杂度 \(O\left(T_{\mathtt{factorization}}(n)\rig ...

  4. phpcms前台退出登录的时候提示信息'退出成功0'

    问题背景: phpcms前台退出登录的时候,提示了一个退出成功0 让我很困惑为啥有个0呢? 问题分析: 进入 ./phpcms/modules/member/index.php 找到logout方法, ...

  5. PhpExcel笔记,phpExcel中文帮助手册

    下面是总结的几个使用方法 include 'PHPExcel.php'; include 'PHPExcel/Writer/Excel2007.php'; //或者include 'PHPExcel/ ...

  6. Java计时器Timer和TimerTask用法

    package com.sy.game.test; import java.util.Timer; import java.util.TimerTask; public class TimeTask ...

  7. iOS 关于UIWindow的理解

    Every iOS app has a window that handles the presentation of the app’s user interface. Although the w ...

  8. ffmpeg-20160506-git-bin

    ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...

  9. 13.SpringMVC和Spring集成(一) && 14.SpringMVC和Spring集成(二)

    1.概念 Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,Spring致力于J2EE应用的各层的解决方案,Spring是企业应用开发的“一站式”选择,并贯 ...

  10. 配置tomcat的虚拟路径

    配置tomcat的虚拟路径有两个地方需要配置,以eclipse为例: ①在tomcat的server.xml中的host节点内添加 <Context path="/meims/user ...