websocket总结
一、WebSocket简介
WebSocket protocol是HTML5一种新的协议,WebSocket 是目前唯一真正实现全双工通信的服务器向客户端推送的互联网技术。WebSocket的出现使得浏览器提供对Socket的支持成为可能,从而在浏览器和服务器之间提供了一个基于 TCP 连接的双向通道。
HTML5 WebSocket 设计出来的目的就是要取代轮询和 Comet 技术,使客户端浏览器具备像 C/S 架构下桌面系统的实时通讯能力。 浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。因为 WebSocket 连接本质上就是一个 TCP 连接,所以在数据传输的稳定性和数据传输量的大小方面,和轮询以及 Comet 技术比较,具有很大的性能优势。
二、WebSocket出现之前实时web应用
Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种 机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏、在线证券、设备监控、新闻在线播报、RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了。所以保持客户端和服务器端的信息同步是实时 Web 应用的关键要素,对 Web 开发人员来说也是一个难题。在 WebSocket 规范出来之前,开发人员想实现这些实时的 Web 应用,不得不采用一些折衷的方案,其中最常用的就是轮询 (Polling) 和 Comet 技术,而 Comet 技术实际上是轮询技术的改进,又可细分为两种实现方式,一种是长轮询机制,一种称为流技术
轮询:
这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是,当客户端以固定频率向 服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。
长轮询:
长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。
流:
流技术方案通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户 端和服务器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方 案来改进用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。
综合这几种方案,您会发现这些目前我们所使用的 所谓的实时技术并不是真正的实时技术,它们只是在用 Ajax 方式来模拟实时的效果,在每次客户端和服务器端交互的时候都是一次 HTTP 的请求和应答的过程,而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量,而且这些方案中客户端和服务器端的编程实现都比较复杂,在实际的应用中,为了模拟比较真实的实时效果,开发人员往往 需要构造两个 HTTP 连接来模拟客户端和服务器之间的双向通讯,一个连接用来处理客户端到服务器端的数据传输,一个连接用来处理服务器端到客户端的数据传输,这不可避免地增加了编程实现的复杂度,也增加了服务器端的负载,制约了应用系统的扩展性。
三、WebSocket规范
WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表 明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
客户端到服务端:
GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5
Origin: http://example.com
[8-byte security key]
服务端到客户端:
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://example.com
WebSocket-Location: ws://example.com/demo
[16-byte hash response]
”Upgrade:WebSocket”表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。从客户端到服务器端请求的信息里包含有”Sec-WebSocket-Key1”、“Sec-WebSocket-Key2”和”[8-byte securitykey]”这样的头信息。这是客户端浏览器需要向服务器端提供的握手信息,服务器端解析这些头信息,并在握手的过程中依据这些信息生成一 个 16 位的安全密钥并返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接。一旦连接建立,客户端和服务器端就可以通过这个通道双向传输数据了。
摘抄自
http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/
四、Websocket实现:
Tomcat7.0.5以后开始支持websocket。下面根据tomcat7.0.63+jdk1.7.0.79构建了一个小例子
(必须配置jdk环境变量)
第一种方式,继承org.apache.catalina.WebSocketServlet,该方法在tomcat7.0.63中已过时。
这种方式没有做demo,可以参考下面这篇文章
http://www.alixixi.com/web/a/2014032492868.shtml
第二种方式:注解方式实现
客户端:
- <script type="text/javascript">
- var socket = null;
- $(function(){
- //建立websocket连接
- socket = new WebSocket("ws://"+window.location.host+"${pageContext.request.contextPath}/mySocket");
- //打开websocket时触发
- socket.onopen = function(){
- $("#showMsg").append("连接成功..<br/>");
- console.log("websocket open");
- }
- //服务端有消息推送到客户端时触发
- socket.onmessage = function(message){
- console.log(message.data);
- var dataJson = JSON.parse(message.data);
- }
- //websocket关闭时触发
- socket.onclose = function(event){
- console.log("websocket close");
- }
- //websocket出错时触发
- socket.onerror = function(event){
- socket.close();
- console.log("websocket error");
- }
- $("#sendButton").click(function(){
- //通过websocket对象的send方法发送数据到服务端,该方法不能直接传送json对象,//可以先将json对象转换成json格式字符串发送给服务端
- var obj = {
- message:’客户端发送消息,
- type:
- };
- //或者使用JSON.stringify(obj);如果js报错找不到该方法,可以自定义一个简单的//jquery插件,功能就是将简单json对象转换成json格式字符串
- socket.send(obj.toJSONString());
- //socket.send($.simpleJsonToString(obj));
- });
- })
- </script">
- $.extend({
- simpleJsonToString : function(jsonObj){
- var jsonStr = “”;
- for(var I in jsonObj){
- jsonStr = jsonStr+i+”:’”+jsonObj[i]+”’”;
- }
- jsonStr = jsonStr.substring(,jsonStr.length-);
- jsonStr = jsonStr+”}”;
- return jsonStr;
- }
- });
服务端:
- package com.h3c.socket;
- import java.io.IOException;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Set;
- import javax.servlet.http.HttpSession;
- import javax.websocket.CloseReason;
- import javax.websocket.EndpointConfig;
- import javax.websocket.OnClose;
- import javax.websocket.OnError;
- import javax.websocket.OnMessage;
- import javax.websocket.OnOpen;
- import javax.websocket.Session;
- import javax.websocket.server.ServerEndpoint;
- import com.google.gson.Gson;
- import net.sf.json.JSONObject;
- //value为访问websocket的路径,configurator为websocket初始化配//置器
- @ServerEndpoint(value="/mySocket",configurator=HttpSessionConfigurator.class)
- public class MySocket {
- static Map<String,Session> sessionMap = new HashMap<String, Session>();
- private HttpSession httpSession;
- @OnOpen
- public void onOpen(Session session,EndpointConfig config){
- System.out.println("websocket open");
- this.httpSession = (HttpSession) config.getUserProperties().get("httpSession");
- sessionMap.put((String) httpSession.getAttribute("userAccount"),session);
- }
- @OnMessage
- public void onMessage(String msg,Session session) throws IOException{
- //将客户端发送的json格式字符串转换成实体对象,客户端徐传送json格式数据
- //需要jar包:json-lib-2.4-jdk15.jar
- JSONObject jsonObject = JSONObject.fromObject(msg);
- transferClassMessage transfer = (transferClassMessage)
- jsonObject.toBean(jsonObject, transferClassMessage.class);
- Gson gson = new Gson();
- Set<Map.Entry<String, Session>> set = sessionMap.entrySet();
- for(Map.Entry<String,Session> sessionEntry:set){
- //向指定客户端发送消息
- if("fw1778".equals(sessionEntry.getKey())){
- sessionEntry.getValue().getBasicRemote().sendText(gson.toJson("服务端返回消息"));
- }
- }
- }
- @OnClose
- public void onClose(Session session,CloseReason closeReason){
- sessionMap.remove(session.getId());
- System.out.println("websocket close");
- }
- @OnError
- public void error(Session session,Throwable ta){
- sessionMap.remove(session.getId());
- System.out.println(ta);
- }
- }
Websocket通过javax.websocket.Session向客户端发送数据,
- package com.h3c.socket;
- import javax.servlet.http.HttpSession;
- import javax.websocket.HandshakeResponse;
- import javax.websocket.server.HandshakeRequest;
- import javax.websocket.server.ServerEndpointConfig;
- public class HttpSessionConfigurator extends ServerEndpointConfig.Configurator{
- @Override
- public void modifyHandshake(ServerEndpointConfig config,
- HandshakeRequest request, HandshakeResponse response) {
- HttpSession httpSession = (HttpSession)request.getHttpSession();
- if(null!=httpSession){
- config.getUserProperties().put("httpSession", httpSession);
- }
- }
- }
四、Websocket与spring整合(spring版本为4.0或者以上)
第一种,配置方式。
http://www.tuicool.com/articles/QvUjUf
第二种:注解方式
Spring相关配置文件(略) spring配置出错,可能会出现handshake的错误,切勿忘记配置spring的自动扫描:<context:component-scan base-package="com.h3c.itac" />
Spring websocket配置类
- package com.h3c.itac.websocket;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.EnableWebMvc;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
- import org.springframework.web.socket.config.annotation.EnableWebSocket;
- import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
- import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
- /**
- * spring websocket配置类,在tomcat启动时会调用该类注册websockethandlers的方法注册handler
- * @author lfw1950
- */
- @Configuration
- @EnableWebMvc
- @EnableWebSocket
- public class TranferWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{
- //注册handler,可以注册多个handler
- @Override
- public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- /**
- * addHandler方法第一个参数为处理websocket的handler,第二个参数为访问路径,addInterceptors方法徐传入一个handshake拦截器
- * 该拦截器会在建立wensocket连接之前执行
- */
- registry.addHandler(new TransferWebSocketHandler(), "/transfer").addInterceptors(new TransferHandshakeInterceptor());
- }
- }
Spring websocket拦截器
- package com.h3c.itac.websocket;
- import java.util.Map;
- import javax.servlet.http.HttpSession;
- import org.springframework.http.server.ServerHttpRequest;
- import org.springframework.http.server.ServerHttpResponse;
- import org.springframework.http.server.ServletServerHttpRequest;
- import org.springframework.web.socket.WebSocketHandler;
- import org.springframework.web.socket.server.HandshakeInterceptor;
- public class TransferHandshakeInterceptor implements HandshakeInterceptor{
- @Override
- public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
- WebSocketHandler handler, Exception e) {
- System.out.println("after handshake");
- }
- @Override
- public boolean beforeHandshake(ServerHttpRequest request,
- ServerHttpResponse response, WebSocketHandler handler,
- Map<String, Object> attr) throws Exception {
- System.out.println("before handshake");
- if(request instanceof ServletServerHttpRequest){
- ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
- HttpSession httpSession = servletRequest.getServletRequest().getSession();
- if(null!=httpSession){
- String userName = (String) httpSession.getAttribute("userAccount");
- /**
- * map中存放的数据可以通过session.getAttributes().get(key)获取
- * 如:session.getAttributes().get("wsUserAccount")
- */
- attr.put("wsUserAccount", userName);
- }
- }
- /**
- * 默认为false,要修改为true,否则无法建立websocket连接
- * WebSocket connection to 'ws://localhost:8888/ITAC_Cloud/transfer' failed: Error during WebSocket handshake: Unexpected response code: 200
- */
- return true;
- }
- }
Spring websocket处理器
- package com.h3c.itac.websocket;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Set;
- import org.springframework.web.socket.CloseStatus;
- import org.springframework.web.socket.TextMessage;
- import org.springframework.web.socket.WebSocketHandler;
- import org.springframework.web.socket.WebSocketMessage;
- import org.springframework.web.socket.WebSocketSession;
- public class TransferWebSocketHandler implements WebSocketHandler{
- private Map<String, WebSocketSession> wsSessions = new HashMap<String, WebSocketSession>();
- @Override
- public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1)
- throws Exception {
- System.out.println("websocket close");
- wsSessions.remove(session.getAttributes().get("wsUserAccount"));
- }
- /**
- * 打开连接时执行
- */
- @Override
- public void afterConnectionEstablished(WebSocketSession session)
- throws Exception {
- System.out.println("websocket open");
- wsSessions.put((String) session.getAttributes().get("wsUserAccount"), session);
- }
- @Override
- public void handleMessage(WebSocketSession session, WebSocketMessage<?> message)
- throws Exception {
- String receiveData = (String) message.getPayload();//从客户端接收到的数据
- Set<Map.Entry<String, WebSocketSession>> set = wsSessions.entrySet();
- for(Map.Entry<String,WebSocketSession> sessionEntry:set){
- /**
- * 模拟交接班中被交接的坐席的域账号为fw1950,则只有登陆的账号为fw1950的客户端会收到服务端传送过去的数据
- */
- if("fw1950".equals(sessionEntry.getKey())){
- sessionEntry.getValue().sendMessage(new TextMessage("服务端返回数据"));
- }
- }
- }
- @Override
- public void handleTransportError(WebSocketSession session, Throwable e)
- throws Exception {
- System.out.println("websocket error");
- wsSessions.put((String) session.getAttributes().get("wsUserAccount"), session);
- }
- /**
- * 暂时不清楚该方法有什么作用
- */
- @Override
- public boolean supportsPartialMessages() {
- return false;
- }
- }
页面和普通的websocket实现基本一样
- <%@ 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 type="text/javascript" src="${pageContext.request.contextPath }/resources/js/jquery-1.11.2.js "></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="send"/>
- </div>
- <div id="toNext" title="接班详情" style="display: none;">
- </div>
- </body>
- <script type="text/javascript">
- var socket = null;
- $(function(){
- socket = new WebSocket("ws://"+window.location.host+"${pageContext.request.contextPath}/transfer");
- socket.onopen = function(){
- $("#showMsg").append("连接成功..<br/>");
- console.log("websocket open");
- }
- socket.onmessage = function(message){
- console.log(message.data);
- }
- socket.onclose = function(event){
- console.log("websocket close");
- }
- socket.onerror = function(event){
- socket.close();
- console.log("websocket error");
- }
- $("#sendButton").click(function(){
- socket.send("客户端发送消息");
- });
- })
- </script>
- </html>
可以参考:
http://my.oschina.net/ldl123292/blog/304360
websocket总结的更多相关文章
- 漫扯:从polling到Websocket
Http被设计成了一个单向的通信的协议,即客户端发起一个request,然后服务器回应一个response.这让服务器很为恼火:我特么才是老大,我居然不能给小弟发消息... 轮询 老大发火了,小弟们自 ...
- 细说WebSocket - Node篇
在上一篇提高到了 web 通信的各种方式,包括 轮询.长连接 以及各种 HTML5 中提到的手段.本文将详细描述 WebSocket协议 在 web通讯 中的实现. 一.WebSocket 协议 1. ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- WebSocket - ( 一.概述 )
说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...
- php+websocket搭建简易聊天室实践
1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...
- Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...
- Cowboy 开源 WebSocket 网络库
Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...
- 借助Nodejs探究WebSocket
文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...
- 细说websocket - php篇
下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket ...
- webSocket and LKDBHelper的使用说明
socketket与lkdbhelper来处理数据 客户需求: 当我们有需要从自己的后台推送消息给我们的用户时,用户需要实时的接收到来自我们的推送消息.前提是没有使用第三方的推送框架,那么这个使用we ...
随机推荐
- Linux IO模型和网络编程模型
术语概念描述: IO有内存IO.网络IO和磁盘IO三种,通常我们说的IO指的是后两者. 阻塞和非阻塞,是函数/方法的实现方式,即在数据就绪之前是立刻返回还是等待. 以文件IO为例,一个IO读过程是文件 ...
- POJ 1200 字符串HASH
题目链接:http://poj.org/problem?id=1200 题意:给定一个字符串,字符串只有NC个不同的字符,问这个字符串所有长度为N的子串有多少个不相同. 思路:字符串HASH,因为只有 ...
- HDU 4162 最小表示法
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4162 题意:给定一个只有0-7数字组成的串.现在要由原串构造出一个新串,新串的构造方法:相邻2个位置的数字 ...
- BZOJ 3542 [Poi2014]Couriers ——可持久化线段树
[题目分析] 查找区间内出现次数大于一半的数字. 直接用主席树,线段树上维护区间大小,由于要求出现次数大于一半,每到一个节点可以分治下去. 时间复杂度(N+Q)logN [代码] #include & ...
- js-原型以及继承小案例
function human(name,tall){ this.name=name; this.tall=tall; this.toSleep=function(){ alert('no sleep' ...
- 原生 js 模拟 alert 弹窗
复制头部的 js 代码到你的 js 文件的任何地方,调用Chef.alert方法传入相应的参数即可并没有什么功能,只是一个提示的作用,可能样式比 alert 的弹窗好看点,css是写在js里的,只要你 ...
- 使用GDB 修改MySQL参数不重启
link:http://blog.chinaunix.net/uid-20785090-id-4016315.html mysql很多参数都需要重启才能生效,有时候条件不允许,可以使用gdb作为最后的 ...
- JDBC 数据库异常 Exception 关闭的(语句,连接,ResultSet)
如果在rs.next()之前关闭了Statement或PreparedStatement,会导致下面的异常: java.sql.SQLException: 关闭的语句: next 如果在rs.next ...
- ACM 兄弟郊游问题
兄弟郊游问题 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描述 兄弟俩骑车郊游,弟弟先出发,每分钟X米,M分钟后,哥哥带一条狗出发.以每分钟Y米的速度去追弟弟,而狗则以 ...
- UVA 11609 - Teams(二项式系数)
题目链接 想了一会,应该是跟二项式系数有关系,无奈自己推的式子,构不成二项式的系数. 选1个人Cn1*1,选2个人Cn2*2....这样一搞,以为还要消项什么的... 搜了一下题解,先选队长Cn1,选 ...