WebSocket学习与实现

1、WebSocket Protocol

WebSocket协议标准是在2014年8月13日被JCP确认发布,也就是JSR356,具体详情,请点击这里

Websocket协议它的描述如下:

websocket协议允许在客户端和服务端之间建立一条双向传递信息的通道,它是建立在TCP协议之上的,首先通过”握手“来确认和建立通道,之后客户端和服务端可以通过这个通道传递信息,而不需要再次发起请求,而且客户端和服务端都可以主动的发送消息。这种技术不依赖于HTTP连接(比如XMLHttpRequest,iframe),可以实现实时消息传递。

Websocket的建立过程如下:

客户端客户端服务端服务端shankehand(握手)对请求消息头进行识别和签名握手确认通道建立成功消息传递消息传递

整个过程大体分为两个阶段:握手和数据传输

客户端向服务端发起的握手消息头如下:

    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

服务端对握手请求成功的回应为:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    Sec-WebSocket-Protocol: chat

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

至此,握手成功,也就是通道已经成功建立,可以进行数据传输。

查看网络连接如下:

Request URL:ws://server.example.com/chat 
Request Method:GET 
Status Code:101 Switching Protocols

请求协议为ws,方法为GET,成功返回状态为101。消息头中的Sec-WebSocket-Key和Sec-WebSocket-Accept为base-64编码的,且Sec-WebSocket-Key为随机生成的,Sec-WebSocket-Accept则为服务端对Sec-WebSocket-Key进行SHA-1 hash计算后返回的base-64编码的字符串。

接下来,我们通过程序来研究一下Websocket,工具:

JDK 7, Eclipse Kepler, Tomcat 7.0.67, Chrome 47, Window 10

2、WebSocket 服务端实现

我们将使用到websocket-api,这个JAR包则在Tomcat的lib下面,新建工程后,需要将Server Library添加到构建路径中(期间我尝试过只引用这个包,而使用低版本的Tomcat,websocket则无法成功),而在同目录下有个tomcat7-websocket包,具体作用未探知。

在websocket-api中,我们使用注解来完成服务端的配置可实现,用到的注解有@ServerEndpoint@OnOpen@OnMessage@OnClose
在写本案例时,新建的是Maven工程,工程名为websocket(此工程已提交到Github,地址见底部),目录结构为:

这里写图片描述

WebSocketServer的代码为:

@ServerEndpoint("/chat")
public class WebSocketServer {

    private Session session;
    private Logger logger = Logger.getLogger(WebSocketServer.class);

    @OnOpen
    public void open(Session session){
        this.session = session;
        logger.info("WebSocket is opening...");
        logger.info("Sesson id: " + this.session.getId());
        logger.info("Query string: " + this.session.getQueryString());
    }

    @OnMessage
    public void onMessage(String message){
        logger.info("Client send message: " + message);
        try {
            this.session.getBasicRemote().sendText("Server message form Websocket server");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnClose
    public void onClose(){
        logger.info("Websocket closed");
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

为了探究请求,还特意添加一个过滤器,DispatcherFilter.java,代码如下:


@WebFilter(urlPatterns={"/*"})
public class DispatcherFilter implements Filter {

    private Logger logger = Logger.getLogger(DispatcherFilter.class);

    public void destroy() {
        // Auto-generated method stub

    }

    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain chain) throws IOException, ServletException {
        // Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) arg0;
        logger.info("Request URL: " + request.getRequestURL());
        chain.doFilter(arg0, arg1);
    }

    public void init(FilterConfig arg0) throws ServletException {
        // Auto-generated method stub

    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

同时也别忘了日志的配置信息:

log4j.rootLogger=DEBUG,STDOUT 
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender 
log4j.appender.STDOUT.Threshold=DEBUG 
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout 
log4j.appender.STDOUT.layout.ConversionPattern= %d %5p (%c:%L) - %m%n

服务端的实现代码就是这些。

3、WebSocket客户端实现

这里所说的客户端就指的是浏览器,在浏览器端的实现则是需要对HTML5的支持,当然,谷歌浏览器为首选,毋庸置疑,他是对这些支持最好的而且调试很方便(顺便鄙视一下某E浏览器)。

var webSocket = new WebSocket('ws://localhost:8080/websocket/chat?query=Picasso');
webSocket.onerror = function(event) {
    //TODO
};

webSocket.onopen = function(event) {
    //TODO
};
//接受消息
webSocket.onmessage = function(event) {
    //TODO
    console.log(event.data)
};
//发送消息
webSocket.send('hello ' + Math.random());
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这些代码已经完成WebSocket的建立和消息传输,这些代码位于src\main\webapp\index.jsp中,其余样式和布局代码,不再这里贴出,请查看源码。

4、WebSocket调试

首先,将此工程添加到Eclipse Servers视图新建的tomcat服务器中,启动。打开浏览器输入地址:

http://localhost:8080/websocket/

当然,tomcat的默认端口为8080,如有冲突或其他,可在Server.xml中修改配置。

记得同时打开Google的开发工具F12,待会儿要查看请求头:

这里写图片描述

查看网络请求状况:

这里写图片描述

清楚的可以看见请求头和响应头,而此时查看服务器的控制台,则有如下信息:

2015-12-20 22:12:03,913 INFO (cc.eabour.websocket.filter.DispatcherFilter:30) - Request URL: http://localhost:8080/websocket/ 
2015-12-20 22:12:03,931 INFO (cc.eabour.websocket.filter.DispatcherFilter:30) - Request URL: http://localhost:8080/websocket/chat 
2015-12-20 22:12:03,933 INFO (cc.eabour.websocket.WebSocketServer:22) - WebSocket is opening… 
2015-12-20 22:12:03,933 INFO (cc.eabour.websocket.WebSocketServer:24) - Sesson id: 6 
2015-12-20 22:12:03,933 INFO (cc.eabour.websocket.WebSocketServer:25) - Query string: query=Picasso

从过滤器拦截日志可以看出,websocket的建立显示通过http请求来进行握手,之后则在建立好的通道上传输数据。

5、后记

通过简单的实现来了解一下WebSocket的工作原理和实现方法,后续将进一步强化和增加实现案例。

如有问题,欢迎指教

参考资料:

本案例项目地址:https://github.com/FlowerBirds/WebSocketExplorer

首次使用Github,Maven也不是很熟,还请多多包涵。

(不得不说,优快云的Markdown编辑器真不赖)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值