您的位置:首页 > Web前端 > HTML5

HTML5下的WebSocket学习笔记

2015-09-21 15:59 513 查看

基础知识

1.WebSocket API   --- 基于JavaScript,包括 WebSocket原始对象,事件,方法和属性,通过WebSocket API可以连接到本地货远程的Server服务器,发送和接收消息,关闭连接
2.使用WebSocket的典型流程:
1.检查浏览器是否支持WebSocket                 --- Check for WebSocket browser support
2.创建WebSocket的js对象                       --- Create an instance of the WebSocket JS Object
3.连接WebSocket服务器                         --- Connect to the WebSocket Server
4.注册WebSocket事件                           --- Register for the WebSocket events
5.根据用户交互进行数据传输                    --- Perform the proper data transmission according to users' actions
6.关闭连接                                    --- Close the connection

1.Browser Support ---  检测浏览器是否支持WebSocket
if(window.WebSocket){
console.log("WebSocket supported");
//通过WebSocket进行操作
}else{
//不支持WebSocket,给出相应的提示和处理策略
alert("Consider updating your browser for a richer experience");
}

2.The WebSocket Object    --- WebSocket的JavaScript对象     new WebSocket(url)   --- url:表示WebSocket要连接的服务器
var socket = new WebSocket("ws://echo.websocket.org");
说明:
当创建一个WebSocket的JS对象后,它会立即连接到指定的服务器

3.WebSocket 事件        --- WebSocket Events
WebSocket的四个主要事件:
事件              对应方法
Open              onopen
Message           onmessage
Close             onclose
Error             onerror

事件处理的两种方式
1.通过事件的对应方法     ---  推荐:简单结构清晰
2.addEventListener方法去实现事件处理

说明:当不同的事件发生时,会触发相应的方法,执行逻辑代码

4.WebSocket的事件处理

onopen      --  事件在连接成功时触发,达成第一个握手,准备传输数据
eg:
socket.onopen = function(event){
console.log("Connection established");
//在这类可以初始化资源,并展示一些用户友好提示信息
var label = document.getElementById("status-label");
label.innerHTML="Connection established";
}

onmessage   -- 数据传输事件,客户端随时监听Server,当服务器端发送给客户端数据时触发  --- 数据包括:文本、图片、二进制数据等
eg:
socket.onmessage = function(event){
console.log("Data received");
if(typeof event.data == "string"){
var label = document.getElementById("status-label");
label.innerHTML = event.data;
}
}

onclose    --  关闭客户端与服务器的连接,不能在进行数据的传输。
关闭连接的原因有多种:服务器关闭,客户端调用close()方法关闭,或者TCP错误  --- 可通过 code,reason,wasClean参数检测连接关闭的原因
code    ---    用一个唯一的数字,表示连接中断的原因
reason  ---    连接中断的原因
wasClean  ---  用于判断连接关闭,是因为服务器的原因,还是未知的网络错误
eg:
socket.onclose = function(event){
console.log("Connections closed");
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;

var label = document.getElementById("status-label");

if(wasClean){
label.innerHTML = "Connection closed normally";
}else{
label.innerHTML ="Connection closed with message "+reason+"(Code: "+code+")";
}
}

onerror    --  出错时触发的事件,该事件出发后,会接着触发close事件,终止连接  ---  注意出错时给出提示,并尝试重新建立连接
eg:
socket.onerror = function(event){
console.log("Error occurred");
//出错提示
var label = document.getElementById("status-label");
label.innerHTML = "Error: "+event;
}

5.WebSocket的操作处理Actions  即:处理方法
两种  send()和close()

1.send()    ---    当连接成功后,客户端可与服务端进行数据传输,send()方法用于传输数据到服务器
eg:
var textView = document.getElementById("text-view");
var buttonSend = document.getElementById("send-button");
buttonSend.onClick = function(){
//发送数据
if(socket.readyState == WebSocket.OPEN){
socket.send(textView.value);
}
}
特别注意:
只有在连接成功时才能发送数据,因此,要么把send()方法放在onopen事件中处理,要么在发送数据是进行判断:当前连接是否成功

2.close()   ---   中断连接
eg:
通过点击按钮事件来中断当前连接
var textView = document.getElementById("text-view");
var buttonStop = document.getElementById("stop-button");
buttonSend.onClick = function(){
if(socket.readyState == WebSocket.OPEN){
socket.close();
//也可之前提到的传递code和reason参数
//socket.close(1000,"Deliberate disconnection");
}
}

6.WebSocket的属性  Properties
url          ---  返回WebSocket的URL
protocol     ---  返回Server服务器使用的协议
readyState   ---  连接状态
WebSocket.OPEN           --- 已连接
WebSocket.CLOSED         --- 已关闭
WebSocket.CONNECTING     --- 正在连接
WebSocket.CLOSING        --- 正在关闭
bufferedAmount   ---  返回send()方法被调用时,已进入队列但尚未发送到服务器的总字节数
binaryType       ---  返回接收到的二进制数据  -- (onmessage事件被触发时)

7.WebSocket的Server
1.流程
Server                                                               Client
Create a server on localhost:8080
Start running
initial handshake: establish connection       <------------          Request connection
Handle the incoming message                   <------------          Send message

2.Server处理
1.初始化WebSocket的地址
2.处理客户端的各类事件   OnOpen,OnClose,OnMessage,以及向客户端发送数据

3.使用Java实现的WebSocket服务器   --- 需要JDK1.7+
--- 需要Tomcat7+下的lib中Tomcat-websocket.jar和websocket-api.jar

完整的例子  WebSocketTest1.java

package com.jay.websocket;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

/**
* Java 实现的WebSocket服务器,
* 实现客户端的onopen,onmessage,onclose事件的处理方法
*  @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址。
onOpen 和 onClose 方法分别被@OnOpen和@OnClose 所注解。这两个注解的作用不言自明:他们定义了当一个新用户连接和断开的时候所调用的方法。
onMessage 方法被@OnMessage所注解。这个注解定义了当服务器接收到客户端发送的消息时所调用的方法。注意:这个方法可能包含一个javax.websocket.Session可选参数(在我们的例子里就是session参数)。如果有这个参数,容器将会把当前发送消息客户端的连接Session注入进去。
本例中我们仅仅是将客户端消息内容打印出来,然后首先我们将发送一条开始消息,之后间隔5秒向客户端发送1条测试消息,共发送3次,最后向客户端发送最后一条结束消息。
* Created by Jay He on 2015/9/19.
*/
@ServerEndpoint("/websocket")
public class WebSocketTest1 {

/**
* 这里用于接收客户端的数据和发送给客户端数据
* @param message
* @param session
* @throws IOException
* @throws InterruptedException
*/
@OnMessage
public void onMessage(String message, Session session)
throws IOException, InterruptedException {

// Print the client message for testing purposes
System.out.println("Received: " + message);

// Send the first message to the client
session.getBasicRemote().sendText("This is the first server message");

// Send 3 messages to the client every 5 seconds
int sentMessages = 0;
while(sentMessages < 3){
Thread.sleep(5000);
session.getBasicRemote().
sendText("This is an intermediate server message. Count: "
+ sentMessages);
sentMessages++;
}

// Send a final message to the client
session.getBasicRemote().sendText("This is the last server message");
}

//客户端连接
@OnOpen
public void onOpen() {
System.out.println("Client connected");
}

//断开连接
@OnClose
public void onClose() {
System.out.println("Connection closed");
}
}

页面显示:websocket.html

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Testing WebSocket</title>
</head>
<body>
<div>
<input type="submit" value="点击请求WebSocket服务器" onclick="start()" />
</div>
<div id="messages"></div>
<script type="text/javascript">
var webSocket =
new WebSocket('ws://localhost:8080/JayEEDemo1/websocket');

webSocket.onerror = function(event) {
onError(event)
};

webSocket.onopen = function(event) {
onOpen(event)
};

webSocket.onmessage = function(event) {
onMessage(event)
};

function onMessage(event) {
document.getElementById('messages').innerHTML
+= '<br />' + event.data;
}

function onOpen(event) {
document.getElementById('messages').innerHTML
= 'Connection established';
}

function onError(event) {
alert(event.data);
}

function start() {
webSocket.send('hello');
return false;
}
</script>
</body>
</html>

8.使用WebSocket传输不同类型的数据
文本文件(String)和二进制文件(ArrayBuffer或Blob)
特别注意:WebSocket传输二进制文件时,只能选择ArrayBuffer或Blob中的一种,不能混用
eg:
ArrayBuffer方式:
socket.binaryType="arraybuffer";
Blob方式:
socket.binaryType="blob";
1.文本文件传输  --- 根据typeof event.data判断类型
1.String
socket.onmessage=function(event){
if(typeof event.data=="string"){
console.log("Received string data");
}
}
2.JSON
if(typeof event.data=="string"){
//创建JSON对象
var jsonObject = JSON.parse(event.data);
//根据key获取JSON值
var username=jsonObject.name;
var userMessage = jsonObject.message;
}

2.二进制文件传输
1.ArrayBuffer  --- 用于传输小型二进制文件,图片等
1.发送二进制文件

通过拖拽事件,处理图片,每次拖进一条图片,通过js的FileReader来读取文件生成一个ArrayBuffer,
当reader处理完file后,在onload事件中,使用WebSocket将数据发送给服务器

1.通过ArrayBuffer方式发送图片给服务器
document.ondrop = function(event){
var file = event.dataTransfer.files[0];
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function(){
socket.send(reader.result);
}
return false;
}

2.通过Blob方式发送图片给服务器
document.ondrop = function(event){
var file = event.dataTransfer.files[0];
//直接发送以blob方式
socket.send(file);
return false;
}

特别说明:
使用浏览器的拖拽事件时,要自定义方法,覆盖浏览器默认的事件处理,防止浏览器默认事件处理影响自己写的处理方法
eg:
//覆写拖动对象事件
document.ondragover = function(event){
event.preventDefault();
}

说明:
dragStart      ---  拖动开始
dragOver       ---  停放位置
drop           ---  拖动结束

2.接收二进制文件

js接收二进制文件时,通过instanceof判断是否是ArrayBuffer
socket.onmessage = function(event){
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
}
}

2.Blobs     ---   用于传输大的二进制文件,视频和其他二进制大文件等
需要在展示时判断Server传递过来的是否是Blob

eg:通过blob形式接收Server传递的图片
socket.message=function(event){
if(event.data instanceof blob){
//1.获取二进制文件
var blob = event.data;
//2.为Blob对象创建新的URL
window.URL = window.URL || window.webkitURL;
var source = window.URL.createObjectURL(blob);
//3.创建image标签
var image = document.createElement("img");
image.src = source;
image.alt = "Image generated from blob";
//4.将创建的标签插入到document尾部
document.body.appendChild(image);
}
}

eg:通过blob接收视频流   --   Video的本质是连续的图片,又帧组成后形成了视频
<img id="video" src="" alt="Video streaming"/>

var video = document.getElementById("video");
socket.onmessage = function(event){
if(event.data instanceof Blob){
//1.获取二进制数据
var blob = event.blob;
//2.为blog对象创建新的的URL
window.URL = window.URL || window.webkitURL;
var source = window.URL.createObjectURL(blob);
//3.更新资源
video.src = source;
//4.释放申请的内存
window.URL.revokeObjectURL(source);
}
}

代码说明:
1.类似上一个图片传输,但这里预先在html中引用了一个<img>标签,在通讯时,不停的改变其src属性
2.每次设置src后,通过revokeObject方法释放URL所在内存

3.通过JSON传递数据
eg:
<label id="status-label">Status...</label>
<input type="text" id="name-view" placeholder="Your name" />
<input type="text" id="text-view" placeholder="Type your message..." />
js:
发送JSON数据
var nameView = document.getElementById("name-view");
buttonSend.onclick = function(event){
if(socket.readyState == WebSocket.OPEN){
//构造json,并发送
var json = "{'name':'"+nameView.value+"','message':'"+textView.value+"'}";
socket.send(json);
textView.value="";
}
}

接收服务端的JSON数据
socket.onmessage = function(event){
if(typeof event.data == "String"){
//显示信息
var jsonObject = eval('('+event.data+')');
var userName = jsonObject.name;
var userMessage = jsonObject.message;
chatArea.innerHTML = charArea.innerHTML+"<p><strong>"+userName+"</strong>:"+userMessage+"</p>"
}
}

9.WebSocket的安全问题:
客户端发送的Header
The following is an example of WebSocket header sent from a client:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Origin: http://example.com Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: AAf/gvkPw6szicrMH3Rwbg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame

服务器端返回的Header
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

常见的攻击类型:
DoS攻击(Denial of Service)  拒绝服务攻击,通过一定的手段使得请求资源对用户不可用     eg:循环攻击,导致服务器繁忙或无法响应
中间人攻击(Man-in-the-middle) ,通常是信息未加密导致
XSS跨站脚本攻击  ,通过植入脚本实现攻击,让脚本在客户端机器上执行      ---  通过传输数据的转码可以避免这类攻击

WebSocket协议自带的   wss  可保证安全的传输,防止入侵

10.异常的处理与反馈
1.浏览器的支持问题
2.不支持的浏览器,通过一定技术手段去完成WebSocket的处理  eg:socketio.js

常见的错误处理:
1.内部异常
能够控制的代码或逻辑异常
2.外部异常
网络连接问题    ---   检查网络可用性      通过HTML5的navigator对象,判断网络是否可用
eg:1.检查网络可用性
if(navigator.onLine){
alert("You are OnLine");
}else{
alert("You are OffLine");
}
eg:2.通知用户网络不可用,并尝试重新连接
socket.onclose = function(event){
//先检测原因,再重新连接
if(event.code!=1000){
//event.code==1000表示是连接正常关闭
if(!navigator.onLine){
alert("You are offline. Please connect to the Internet and try again");
}
}
}

处理浏览器HTML5不支持的问题
使用polyfills方案的插件库,来解决老版本浏览器不支持HTML5的问题
常用的js库: sockJS,socket.io和jQuery的插件库  Graceful WebSocket   ---  https://github.com/ffdead/jquery-graceful-websocket eg:
使用Graceful WebSocket库,来实现HTML5的功能
1.引入两个库
<script src=" jquery-1.9.1.min.js"></script>
<script src="jquery.gracefulWebSocket.js"></script>
2.使用 $.gracefulWebSocket(address) 代替 new WebSocket(address) 创建WebSocket对象
var socket = $.gracefulWebSocket("ws://localhost:8181")
3.实现4个常用事件函数
socket.onopen = function(event){
//处理连接事件
}
socket.onclose = function(event){
//处理连接关闭事件
}
socket.onmessage = function(event){
//处理消息接收事件
}
socket.onerror = function(event){
//处理异常事件
}

发送消息:
socket.send("Hello server! I'm a WebSocket polyfill.");

一个WebSocket的实践:
HTML5 WebSocket实现实时视频文字传输

客户端
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
//1.初始化WebSocket对象
var webSocket = new WebSocket("ws://localhost:8080/webSocket");
//向服务器发送消息
//webSocket.send("hell0");
//查看WebSocket当前状态
//alert(webSocket.readyState);

//2.定义WebSocket的事件方法
webSocket.onopen = function(event){
alert("已经建立连接");
}
webSocket.onmessage = function(event){
//收到服务器消息,使用event.data提取消息内容   -- 可以是文本,也可能是二进制文件
writeToScreen(event.data);
}
webSocket.onclose = function(event){
alert("已经关闭连接");
}
webSocket.onerror = function(event){
alert("产生异常");
//将异常信息打印在屏幕上
writeToScreen(event.message);
}

//发送消息给服务器
function sendMsg(){
if(webSocket.readyState == WebSocket.OPEN){
msg = document.getElementById("msg").value;
webSocket.send(msg);
writeToScreen("发送成功!");
}else{
writeToScreen("发送失败!");
}
}

//将信息写到屏幕
function writeToScreen(message){
var pre = document.createElement("p");
pre.style.wordWrap="break-word";
pre.innerHTML+=message;
output.appendChild(pre);
}

</script>
</head>
<body>
<div>
<input type="text" id="msg" value="beyond is number one!" />
<button onclick="sendMsg()">send</button>
</div>
<div id="output"></div>
</body>
</html>

代码说明:
1.readyState表示的四种状态:
CONNECTING(0)          --- 还未建立连接
OPEN(1)                --- 已建立连接,可进行通讯
CLOSING(3)             --- 正在关闭连接
CLOSED(4)              --- 连接已经关闭或无法打开
2.WebSocket的协议,通常是  ws  或  wss(加密通信)
3.send()方法将数据发送到服务器端
4.close()方法关闭连接
5.几个触发事件:
onopen          ---   连接建立,即:握手成功触发的事件
onmessage       ---   收到服务器消息时触发的事件
onerror         ---   异常触发的事件
onclose         ---   关闭连接触发的事件

服务器端


基于WebSocket的即时通讯:

Server端

<pre>import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
//注意此访问地址格式如:"ws://"+ window.location.host+"/${pageContext.request.contextPath}/game"是ws开头的,而不是以http:开头的.
@ServerEndpoint(value = "/game")
public class Scoket {

    private Logger logger = Logger.getLogger(this.getClass().getName());

    static Map<String,Session> sessionMap = new Hashtable<String,Session>();
    
    @OnOpen
    public void onOpen(Session session) {
    	sessionMap.put(session.getId(), session);
    }

    @OnMessage
    public void onMessage(String unscrambledWord, Session session) {
    	broadcastAll("message",unscrambledWord);
    }
    /**
     * 广播给所有人
     * @param message
     */
    public static void broadcastAll(String type,String message){
        Set<Map.Entry<String,Session>> set = sessionMap.entrySet();
        for(Map.Entry<String,Session> i: set){
            try {
            	i.getValue().getBasicRemote().sendText("{type:'"+type+"',text:'"+message+"'}");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
    	sessionMap.remove(session.getId());
        logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
    }
    
    @OnError
    public void error(Session session, java.lang.Throwable throwable){
    	sessionMap.remove(session.getId());
        System.err.println("session "+session.getId()+" error:"+throwable);
    }
}



注解说明:

@ServerEndpoint 将POJO转换为服务器端点

@ClientEndpoint 将Pojo转换为客户端端点

@OnMessage 处理WebSocket的onMessage事件

@PathParam 方法参数,标记匹配的URI模板路径

@OnOpen 处理WebSocket的open事件

@OnClose 处理WebSocket的close事件

@OnError 处理WebSocket的error事件

Client端

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
var socket =null;
$(function(){
function parseObj(strData){//转换对象
return (new Function( "return " + strData ))();
};
//创建socket对象
socket = new WebSocket("ws://"+ window.location.host+"/${pageContext.request.contextPath}/game");
//连接创建后调用
socket.onopen = function() {
$("#showMsg").append("连接成功...<br/>");
};
//接收到服务器消息后调用
socket.onmessage = function(message) {
var data=parseObj(message.data);
if(data.type=="message"){
$("#showMsg").append("<span style='display:block'>"+data.text+"</span>");
}else if(data.type=="background"){
$("#showMsg").append("<span style='display:block'>系统改变背景地址,背景地址是:"+data.text+"</span>");
$("body").css("background","url("+data.text+")");
}
};
//关闭连接的时候调用
socket.onclose = function(){
alert("close");
};
//出错时调用
socket.onerror = function() {
alert("error");
};
$("#sendButton").click(function() {
socket.send($("#msg").val());
});
$("#abcde").click(function(){
$.post("${pageContext.request.contextPath}/backgroundimg");
});
});
</script>
</head>
<body>
<div id="showMsg" style="border: 1px solid; width: 500px; height: 400px; overflow: auto;"></div>
<div>
<input type="text" id="msg" />
<input type="button" id="sendButton" value="发送" />
<input type="button" value="改变背景" id="abcde" />
</div>
</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: