这次给大家介绍一个项目中用户实现聊天或操作同步的协议:WebSocket,接下来将带大家使用WebSocket实现一个简易的聊天程序和同步操作
首先WebSocket是什么,能做什么?
WebSocket是一种持久化协议,传统的HTTP协议做不到服务器主动发送数据给浏览器,换句话说,浏览器不主动请求,服务器是没法主动发数据给浏览器的。HTML5推出了WebSocket标准,让浏览器和服务器之间可以建立通信,可以在浏览器不主动请求的情况下主动发送数据给浏览器,实现实时聊天,操作同步之类的功能;
WebSocket(ws)协议
webSocket是利用了HTTP协议来建立连接的,连接由浏览器发起,请求协议是一个标准的
HTTP请求,但是请求头是WebSoket的连接。
一但WebSocket连接就建立成功,浏览器和服务器就可以随时主动发送消息给对方
WebSocket主要方法
需要注意的方法共有五种,四种类似于对当前状态的监控,另一种是对另一端的主动推送,类似于四个被动方法一个主动方法
网页部分使用WebSocket对象.对方法进行调用,服务器端则使用注解对方法进行声明,详情代码后续展示,
onOpen:连接成功响应的方法
onClose:连接关闭响应的方法
onError:连接异常响应的方法
onMessage:收到另一方消息响应的方法
send:推送给另一方消息(主动技能)
WebSocket实现
接下来带大家直接实现一个简单的WebSocket的Web示例
WebSocket由HTML5提出的标准,JavaEE 7中出了JSR-356:Java API for WebSocket规范。如Tomcat,Nginx,Jetty等Web容器都支持WebSocket。Tomcat7.0.47开始支持JSR-356,所以Tomcat版本在7.0.47以上的话也可以不用导入其他jar包的情况实现WebSocket
//接下来的实现代码由普通Web项目实现
实现一个WebSocket服务类
import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;/*** WebSocket服务类* @author SAKURA* */@ServerEndpoint("/ws/{id}")public class WebSocket {/*** 连接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("id") String id) {}/*** 连接关闭触发*/@OnClosepublic void onClose() {}/*** 浏览器send消息触发* @param message*/@OnMessagepublic void onMessage(String message, Session session) {}/*** 连接异常触发* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {}}
WebSocket服务类的声明可以使用注解进行声明,核心类注解为,方法注解则可以给方法标记为WebSocket所用方法
@ServerEndpoint("请求地址")@OnOpen,onClose...等,对方法进行声明
对Websocket服务类进行包装
服务器端对浏览器进行发送数据需要使用当前WebSocket的session对方法调用发送数据
this.session.getBasicRemote().sendText(数据信息);
因此需要对当前Session进行存储,对服务类进行封装,存储此次连接的信息并且加入至所有用户集合
新增变量//存储每个会话的对象private static Map<String, WebSocket> websocketsession=new HashMap<String, WebSocket>();//当前会话标识private String id;//当前会话private Session session;/*** 连接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("id") String id) {this.id=id;//根据请求连接获取当前会话标识(当前登录用户Id)this.session = session;//存储此次连接的会话对象websocketsession.put(id, this);//将此次连接添加至用户集合中System.out.println("[WebSocket.onOpen]新窗口开始监听:" + id + ",当前在线人数为" + getOnlineCount());}
除了存储会话信息以外,还可以根据需求自定义其余方法以简化操作,例如
/*** 获取连接数* @return*/public static int getOnlineCount() {return websocketsession.size();//通过集合长度获取连接数量}/*** 实现服务器推送消息*/public void sendMessage(String message) throws IOException {System.out.println("[WebSocket.sendMessage]>>>开始推送消息:" + message + ">>>至:"+this.id);this.session.getBasicRemote().sendText(message);}/*** 群发消息*/public static void sendInfo(String message, String toid,String myid) {System.out.println("[WebSocket.sendInfo]推送消息到窗口" + myid + ",推送内容:" + message);for (WebSocket item : websocketsession.values()) {try {if ((toid == null || toid == "")&&!item.id.equals(myid)) {item.sendMessage(message);} else if (item.id.equals(toid)) {item.sendMessage(message);}} catch (IOException e) {continue;}}}
页面WebSocket对象创建
js中可以之间创建WebSocket对象,对象创建时将会向指定地址请求,如果服务器端地址存在则和服务器端进行连接,一旦连接成功,浏览器将会和服务器进行通信
<body><h1>Hello WebSocket ${id}</h1><div><!--页面部分--><input type="text" id="text" />内容 <input type="text" id="id" />接收人 <inputtype="button" value="发送" id="send" /> <br /><textarea id="textarea"></textarea><br> 请选择: <select id="select"><option value="1">01</option><option value="2">02</option><option value="3">03</option><option value="4">04</option></select><ul id="ul"></ul><!--页面部分--></div></body><script type="text/javascript" src="jquery-3.2.1.js"></script><script type="text/javascript">var socket;if (typeof (WebSocket) == "undefined") {//验证浏览器是否支持wsconsole.log("您的浏览器不支持Websocket");} else {console.log("您的浏览支持WebSocket");//实现WebSocket对象,指定要连接的服务器地址与端口//请求地址为刚刚创建的服务类,当前用户ID根据个人情况设置socket = new WebSocket("ws://localhost:8080/WebSockets/ws/${id}");//打开事件socket.onopen = function() {console.log("Socket 已打开");}//获得消息事件socket.onmessage = function(msg) {//发现消息进入,开始处理签到触发逻辑}//关闭事件socket.onclose = function() {console.log("Socket已关闭")}//发生了错误事件socket.onerror = function() {alert("Socket发生了错误");}}</script>
服务器端与浏览器进行连接
使用页面对控制器进行访问,尝试连接



请求成功之后,将会在控制台页进行提示输出
WebSocket的执行大概步骤为:访问首页>执行首页JS文件>创建WebSocket对象>请求对象指定的服务地址进行连接>连接成功触发服务器端@OnOpen声明的方法,执行js中socket.onopen()方法
数据传递
根据以上代码,大家可能会发现,服务器端的注解声明的方法与浏览器中对象调用的方法都是相互对应的;
浏览器中使用的socket.send()方法发送的数据将会由服务器端的onmessage()方法进行接收,服务器端使用的session.getBasicRemote().sendText()方法发送的数据反之则会由socket.onmessage()方法进行接收;
1.使用js直接使用send()方法发送数据改变其他用户的页面状态:
var select = document.getElementById("select");//1.操作触发事件,传递数据至服务器select.onchange = function() {//下拉框改变事件//对操作进行封装var obj = {"type" : "select",//操作类型"id" : "${id}",//操作用户"index" : select.value//操作的值}//向socket另一端发送数据socket.send(JSON.stringify(obj));}//2.服务类onMessage方法接收数据并进行处理和重新发出至其他人@OnMessagepublic void onMessage(String message, Session session) {System.out.println("[WebSocket.onMessage]收到来自窗口" + id + "的信息:" + message);//遍历当前所有用户调用方法将数据传递回浏览器for (WebSocket item : websocketsession.values()) {try {// item.sendMessage(message, item.id);//调用方法发送数据item.session.getBasicRemote().sendText(message);//直接发送数据} catch (Exception e) {e.printStackTrace();}}}//3.浏览器接收到消息并进行处理socket.onmessage = function(msg) {//发现消息进入,开始处理签到触发逻辑/msg.data为传递的数据var obj = JSON.parse(msg.data);if (obj.type == "select") {select.value = obj.index;//下拉框选中//增加记录var li = document.createElement("li");li.innerText = "用户:" + obj.id + "选中了:" + obj.index;ul.appendChild(li)}}
此时用户A改变下拉框时,将会发送当前操作数据至服务器,服务器再将此操作通知给其他注册在WebSocket会话中的用户,其他用户就可以在不刷新的状态下页面进行变化

2.浏览器使用ajax发送数据交给控制器处理
还有一种发送数据的方法是,通过js使用ajax发送数据至控制器后,使用控制器调用WebSocket服务对注册过的用户进行推送消息,数据的封装也可以交给控制器进行操作;
var send = document.getElementById("send");//1.点击按钮使用AJAX将消息直接发送至控制器处理send.onclick = function() {var url = "/WebSockets/index?opr=msg&message=" + textmsg.value + "&myid=${id}&toid=" + cid.value;$.get(url, function(flag) {if(flag){info.value += "我:" + textmsg.value + "\n"cid.value = "";textmsg.value = "";}})}//2.控制器接收Ajax数据进行处理if (opr.equals("msg")){//发送消息//拿到前台ajax发送的数据String toid=request.getParameter("toid");String message=request.getParameter("message");String myid=request.getParameter("myid");//封装数据String msg="{\"type\":\"msg\",\"id\":\""+myid+"\",\"textinfo\":\""+message+"\"}";//手动调用WebSocket服务类自定义方法发送数据至其他用户WebSocket.sendInfo(msg, toid, myid);out.print(true);//ajax返回结果}//3.浏览器接收到消息并进行处理socket.onmessage = function(msg) {//发现消息进入,开始处理签到触发逻辑var obj = JSON.parse(msg.data);if (obj.type == "select") {//下拉框操作select.value = obj.index;//下拉框选中//增加记录var li = document.createElement("li");li.innerText = "用户:" + obj.id + "选中了:" + obj.index;ul.appendChild(li)}if (obj.type == "msg") {//接收信息//显示数据信息info.value += obj.id + ":" + obj.textinfo + "\n"}}

至此,使用WebSocket实现的示例已经完成了
最后总结
1.浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求
2.服务器建立连接后可以把当前会话对象存储起来
3.浏览器可以通过send()向控制器一方推送信息,控制器的onmessage对信息作出
相应并重新试验send把信息推送给其他存储的浏览器(会话对象)
4.其他的浏览器(会话对象)的onmessage通用也会对推送的信息作出响应显示在页面上
//浏览器并非一定要使用send推送信息,服务器必须使用send推送信息,只需要服
务器能把信息用websocket.send推送给其他对象就可以
或者大家可以抽象理解为这样:
"班级来了新的同学(需要记录的浏览会话),这位同学找到老师(请求WebSocket服务类建立连接),把自己的个人信息让老师记在了点名表上(存储Socket会话信息);某同学修改了班级教室(用户进行操作),然后通知老师(发送消息至WebSocket服务类),老师根据点名表依次把这个消息通知给了其他同学(服务类发送数据至浏览器);至于聊天部分就可以理解为通过老师(WebSocket)进行传话;(ಥ_ಥ)"
WebSocket在实际使用中,可以自行的根据个人需求对部分代码进行修改或拓展,初次尝试,如有错误欢迎大家提出以便我加以改正,共同进步
本次示例源码以供参考(包含Spring boot+Maven版本) https://gitee.com/blacktechbox/WebSocket.git




