1)WebSocket介紹 javascript
HTML5 Web Sockets規範定義了Web Sockets API,支持頁面使用Web Socket協議與遠程主機進行全雙工的通訊。它引入了WebSocket接口而且定義了一個全雙工的通訊通道,經過一個單一的套接字在Web上進行操做。HTML5 Web Sockets以最小的開銷高效地提供了Web鏈接。相較於常常須要使用推送實時數據到客戶端甚至經過維護兩個HTTP鏈接來模擬全雙工鏈接的舊的輪詢或長輪詢(Comet)來講,這就極大的減小了沒必要要的網絡流量與延遲。css
要使用HTML5 Web Sockets從一個Web客戶端鏈接到一個遠程端點,你要建立一個新的WebSocket實例併爲之提供一個URL來表示你想要鏈接到的遠程端點。該規範定義了ws://以及wss://模式來分別表示WebSocket和安全WebSocket鏈接。一個WebSocket鏈接是在客戶端與服務器之間HTTP協議的初始握手階段將其升級到Web Socket協議來創建的,其底層還是TCP/IP鏈接。 html
2)優勢 html5
a)、服務器與客戶端之間交換的標頭信息很小,大概只有2字節;java
b)、客戶端與服務器均可以主動傳送數據給對方;jquery
c)、不用頻率建立TCP請求及銷燬請求,減小網絡帶寬資源的佔用,同時也節省服務器資源;android
3)WebSocket數據幀的介紹web
a)、草案版本00到草案版本05之間,詳細能夠查看草案文檔,解碼編碼能夠看見Netty的WebSocketFrameDecoder和WebSocketFrameEncoder實現;apache
b)、草案版本06到如今最新的草案17,介紹參見文章:http://blog.csdn.net/fenglibing/article/details/6852497後端
4)WebSocket不一樣版本的幾種握手方式
a)、無安全key、最老的WebSocket握手協議的實現(Flash);
b)、帶兩個安全key請求頭的後端握手實現;
c)、帶一個安全key請求頭的後端握手實現;
參見:http://blog.csdn.net/fenglibing/article/details/7100070
5)WebSocket能夠穿越防火牆嗎?
WebSocket使用標準的80及443端口,這兩個都是防火牆友好協議,Web Sockets使用HTTP Upgrade機制升級到Web Socket協議。HTML5 Web Sockets有着兼容HTTP的握手機制,所以HTTP服務器能夠與WebSocket服務器共享默認的HTTP與HTTPS端(80和443)。
6)、Web Sockets與代理服務器交互
代理服務器的問題:
a)、HTTP代理服務器可能會選擇關閉流或閒置的WebSocket鏈接,由於它們看起好像是嘗試鏈接一個沒有迴應的HTTP服務器;
b)、代理服務器可能會緩衝未加密的HTTP響應,這將會對HTTP響應流帶來不可估計的延遲;
c)、未加密的WebSocket鏈接(ws://開頭的請求)服務器時,若是中間存在透明代理服務器,鏈接可能會失敗,或者發送消息會失敗;而加密的WebSocket鏈接在存在透明代理服務器的狀況下成功的機率會比較大;
關於代理詳細參見:http://www.infoq.com/cn/articles/Web-Sockets-Proxy-Servers
WebSocket,並不是HTML 5獨有,WebSocket是一種協議。只是在handshake的時候,發送的連接信息頭和HTTP類似。HTML 5只是實現了WebSocket的客戶端。其實,難點在於服務端,服務端相對仍是比較複雜的。
websocket 的協議在RFC6455中 http://tools.ietf.org/html/rfc6455#section-5.1
目前支持webSocket客戶端有Firefox4、Chrome4、Opera10.70以及Safari5等,android手機上的webkit等支持html5的都支持,在客戶端除了直接使用websocket的api也可使用jquery.socket.jsatmosphere.js socket.io等
Java服務器支持webSocket有tomcat 7.0.27, Netty 3.3.x,Jetty 7.x,GlassFish 3.1.2,比她們更高的版本固然也支持了。
基於HTML5和Tomcat WebSocketServlet以及android聊天室簡單實現
index.jsp
<%@ page language=
"
java
" contentType=
"
text/html; charset=UTF-8
"
pageEncoding=
"
UTF-8
"
%>
<%
String path = request.getContextPath();
String WsBasePath =
"
ws://
" + request.getServerName() +
"
:
"
+ request.getServerPort() + path +
"
/
";
%>
<
html
>
<
head
>
<
meta
http-equiv
="Content-Type"
content
="text/html; charset=UTF-8"
>
<
title
>websocket聊天室
</
title
>
<
style
type
="text/css"
>
#chat {
text-align:
left;
width:
600px;
height:
500px;
width:
600px;
}
#up {
text-align:
left;
width:
100%;
height:
400px;
border:
1px solid green;
OVERFLOW-Y:
auto;
}
#down {
text-align:
left;
height:
100px;
width:
100%;
}
</
style
>
</
head
>
<
body
>
<
h2
align
="center"
>基於HTML5的聊天室
</
h2
>
<
div
align
="center"
style
="width: 100%; height: 700px;"
>
<
div
id
="chat"
>
<
div
id
="up"
></
div
>
<
div
id
="down"
>
<
textarea
style
="width: 602px; height: 100%;"
id
="send"
></
textarea
>
</
div
>
</
div
>
<
br
/>
<
input
type
="button"
value
="鏈接"
onclick
="chat(this);"
>
<
input
type
="button"
value
="發送"
onclick
="send(this);"
disabled
="disabled"
id
="send_btn"
title
="Ctrl+Enter發送"
>
</
div
>
</
body
>
<
script
type
="text/javascript"
>
var socket;
var receive_text = document.getElementById("up");
var send_text = document.getElementById("send");
function addText(msg) {
receive_text.innerHTML += "<br/>" + msg;
receive_text.scrollTop = receive_text.scrollHeight;
}
var chat =
function(obj) {
obj.disabled = "disabled";
try{
socket =
new WebSocket('<%=WsBasePath + "chat"%>');
receive_text.innerHTML += '<%=WsBasePath + "chat"%>';
receive_text.innerHTML += "<font color=green>正在鏈接服務器……</font>";
}
catch(e){
receive_text.innerHTML += "<font color=red>抱歉,您的瀏覽器不支持html5,請使用IE10或者最新版本的谷歌、火狐等瀏覽器!</font>";
}
//
打開Socket
socket.onopen =
function(event) {
falg=
false;
addText("<font color=green>鏈接成功!</font>");
document.getElementById("send_btn").disabled =
false;
send_text.focus();
document.onkeydown =
function(event) {
if (event.keyCode == 13 && event.ctrlKey) {
send();
}
};
};
socket.onmessage =
function(event) {
addText(event.data);
};
socket.onclose =
function(event) {
addText("<font color=red>鏈接斷開!</font>");
obj.disabled = "";
};
};
var send =
function(obj) {
if (send_text.value == "") {
return;
}
socket.send(send_text.value);
send_text.value = "";
send_text.focus();
};
</
script
>
</
html
>
ChatWebSocketServlet.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
@WebServlet("/chat")
public
class ChatWebSocketServlet
extends WebSocketServlet {
private
final Map<Integer, WsOutbound> map =
new HashMap<Integer, WsOutbound>();
//
private static final long serialVersionUID = -1058445282919079067L;
private
static
final
long serialVersionUID = 911879078000755859L;
public
class ChatMessageInbound
extends MessageInbound {
@Override
protected
void onBinaryMessage(ByteBuffer arg0)
throws IOException {
//
TODO Auto-generated method stub
}
@Override
protected
void onTextMessage(CharBuffer arg0)
throws IOException {
//
TODO Auto-generated method stub
String msg = arg0.toString();
Date date =
new Date();
SimpleDateFormat sdf =
new SimpleDateFormat("HH:mm:ss");
msg = " <font color=green>匿名用戶 " + sdf.format(date) + "</font><br/> " + msg;
broadcast(msg);
}
@Override
protected
void onClose(
int status) {
//
TODO Auto-generated method stub
map.remove(getWsOutbound().hashCode());
super.onClose(status);
}
@Override
protected
void onOpen(WsOutbound outbound) {
//
TODO Auto-generated method stub
map.put(outbound.hashCode(), outbound);
System.out.println("上線" + outbound.hashCode());
super.onOpen(outbound);
}
}
@Override
protected StreamInbound createWebSocketInbound(String arg0,
HttpServletRequest arg1) {
//
TODO Auto-generated method stub
return
new ChatMessageInbound();
}
private
void broadcast(String msg) {
Set<Integer> set = map.keySet();
for (Integer integer : set) {
WsOutbound outbound = map.get(integer);
CharBuffer buffer = CharBuffer.wrap(msg);
try {
outbound.writeTextMessage(buffer);
outbound.flush();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
基於android客戶端和websocket協議實現的有autobahn庫,包含The WebSocket Protocol and The WebSocket Application Messaging Protocol (WAMP),具體見連接 http://autobahn.ws/android。
package com.example.testwebsocket;
import de.tavendo.autobahn.WebSocketConnection;
import de.tavendo.autobahn.WebSocketConnectionHandler;
import de.tavendo.autobahn.WebSocketException;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public
class MainActivity
extends Activity {
@Override
protected
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("開始鏈接websocket///");
final WebSocketConnection wsc =
new WebSocketConnection();
try {
wsc.connect(
"ws://10.204.79.19:8080/chat",
new WebSocketConnectionHandler() {
@Override
public
void onOpen() {
//
TODO Auto-generated method stub
System.out.println("onOpen");
wsc.sendTextMessage("Hello!");
//
wsc.disconnect();
//
super.onOpen();
}
@Override
public
void onClose(
int code, String reason) {
//
TODO Auto-generated method stub
System.out.println("onClose reason=" + reason);
//
super.onClose(code, reason);
}
@Override
public
void onTextMessage(String payload) {
//
TODO Auto-generated method stub
System.out.println("onTextMessage" + payload);
//
super.onTextMessage(payload);
}
@Override
public
void onRawTextMessage(
byte[] payload) {
//
TODO Auto-generated method stub
System.out.println("onRawTextMessage size="
+ payload.length);
//
super.onRawTextMessage(payload);
}
@Override
public
void onBinaryMessage(
byte[] payload) {
//
TODO Auto-generated method stub
System.out.println("onBinaryMessage size="
+ payload.length);
//
super.onBinaryMessage(payload);
}
});
}
catch (WebSocketException e) {
e.printStackTrace();
}
}
@Override
public
boolean onCreateOptionsMenu(Menu menu) {
//
Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return
true;
}
}
相關websocket協議實現的android版本庫見截圖