一. Servlet 实现 WebSocket
Servlet 实现 WebSocket 需要 @ServerEndpoint 注解, 该注解可以实现Web Socket, 需要用 3.1 版本,Tomcat服务器最好是 8.0+ 版本。
通过@ServerEndPoint 注解的Servlet 类中需要提供四个方法, 服务器连接时操作,服务器断开时操作,服务器接收到客户端消息时操作,异常错误操作 来分别对应前端浏览器的四个事件。 这四个方法,要想起作用,需要分别添加 @OnOpen, @OnClose, @OnMessage,@OnError 四个注解。
即:
- 被 @OnOpen 注解标识的方法,可以处理连接时操作
- 被 @OnClose 注解标识的方法,可以处理 断开时操作
- 被 @OnMessage 注解标识的方法, 可以处理接收消息时操作
- 被 @OnError 注解标识的方法,可以处理异常错误时操作
二. Servlet 实现 WebSocket 的详细开发步骤
二.一 创建动态项目(Dynamic Web Project) WebSocket
Servlet 版本号是 3.1, Tomcat 版本号是 8.0
二.二 服务器端代码
创建 ChatEntPoint.java 类, 用 @ServerEndPoint 注解标记起来
package com.yjl.socket; import java.io.IOException; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; 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; //用注解 @ServerEndPoint @ServerEndPoint @ServerEndpoint(value="/websocket/chat") public class ChatEntPoint { //用于构建用户名称,是前缀 private static final String GUEST_PREFIX="访客"; //定义后面的名称,是个不重复的索引值,从0开始。 AtomicInteger AtomicInteger private static final AtomicInteger connectionIds=new AtomicInteger(0); //定义Set 集合,用于存放客户端连接, 不重复 private static final Set<ChatEntPoint> clientSet=new CopyOnWriteArraySet<>(); //显示的昵称 private String nickName; //session 对象,用于接收 private Session session; //构造方法,初始化 每一个 ChatEntPoint 对象的 nickName,构建成 访问+数字的形式 public ChatEntPoint(){ //后面生成一个不重复的数字 nickName=GUEST_PREFIX+connectionIds.getAndIncrement(); } // OnOpen ,表示每一个客户端连接时的事件 @OnOpen public void start(Session session){ //单独接收一个客户端与服务器端的Session this.session=session; //添加到这里面 clientSet.add(this); //设置消息 String message=String.format("【%s %s 】",nickName,"加入聊天室"); //用于发送消息 broadcast(message); } //OnClose 注解, 每一个关闭时的事件 @OnClose public void end(){ //移除掉 clientSet.remove(this); //格式化消息 String message=String.format("【%s %s】",nickName,"离开聊天室"); //发送消息 broadcast(message); } //OnMessage, 接收客户端发送过来的消息的事件 OnMessage OnMessage @OnMessage public void incoming(String message){ String filteredMessage=String.format("【%s:%s】",nickName,filter(message)); //发送消息 broadcast(filteredMessage); } // 服务器端错误时的,事件处理 @OnError public void onError(Throwable t) throws Throwable{ System.out.println("WebScoket 服务端错误"+t.getMessage()); } //发送消息的方法 private static void broadcast(String message){ //遍历每一个客户端 for(ChatEntPoint client:clientSet){ try{ //同步操作 synchronized(client){ //通过 session.getBasicRemote() .sendText() 发送消息 getBasicRemote(). sendText() client.session.getBasicRemote().sendText(message); } }catch(IOException e){ System.out.println("聊天错误,向客户端 "+client+"发送消息出现错误 "); //移除这个不存在的客户端 clientSet.remove(client); try{ //关闭这个session client.session.close(); }catch(IOException e2){ e2.printStackTrace(); } //发送消息, 也检测一下,是否还有其他死客户端 String msg=String.format("【%s %s 】",client.nickName,"已经断开连接"); broadcast(msg); } } } //格式化前端传递过来的消息 private static String filter(String message){ if(null==message){ return null; } //定义数据 char[] content=new char[message.length()]; //往 char 数据里面放置数据 message.getChars(0,message.length(), content, 0); //判断后,进行转换 StringBuilder result=new StringBuilder(content.length+50); for(int i=0;i<content.length;i++){ switch(content[i]){ case '<':{ result.append("<"); break; } case '>':{ result.append(">"); break; } case '&':{ result.append("&"); break; } case '"':{ result.append("""); break; } default:{ result.append(content[i]); } } } return result.toString(); } }
二.三 客户端代码
在 index.jsp 页面,添加客户端代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>主页</title> </head> <body> <script> //定义WebSocket 对象 new WebSocket () 协议是 ws 协议 // 注意 url 的地址 ,暂时是固化了 var webSocket=new WebSocket("ws://127.0.0.1:80/WebSocket/websocket/chat"); //发送消息 var sendMsg=function(){ //获取元素 var inputElement=document.getElementById("msg"); //调用 webSocket.send(text) 方法,发送给服务器端消息 webSocket.send(inputElement.value); //清空输入框 inputElement.value=""; } //回车事件,发送消息 var send=function(event){ if(event.keyCode==13){ sendMsg(); } } //退出按钮事件 var closeWS=function(){ //调用close 方法,进行关闭 webSocket.close(); //清空消息 document.getElementById("show").innerHTML=""; //清空输入框内的消息 document.getElementById("msg").value=""; } //webSocket.onopen 当连接时,绑定事件,避免出来未连接,就点击按钮. webSocket.onopen=function(){ //回车事件 document.getElementById('msg').onkeydown=send; //发送按钮事件 document.getElementById("sendBn").onclick=sendMsg; //退出事件 document.getElementById("closeBn").onclick=closeWS; console.log("WebSocket 连接成功!!"); } //接收消息 onmessage webSocket.onmessage=function(event){ var show=document.getElementById("show"); //event.data 用于获取消息 event.data 用于获取消息,并且拼装 show.innerHTML+=event.data+"<br/>"; //滚动条处理 show.scrollTop=show.scrollHeight; } //webSocket 关闭 onclose 事件 webSocket.onclose=function(){ //去掉事件 document.getElementById("msg").onkeydown=null; document.getElementById("sendBn").onclick=null; //退出 document.getElementById("closeBn").onclick=null; //提示已经被关闭了 console.log("WebSocket 已经被关闭了!!"); } </script> <!--展示的div --> <div style="width:600px;height:400px; overflow-y:auto;border:1px solid #333;" id="show"> </div> <br/> <!-- 填入内容的框 --> <input type="text" size="80" id="msg" name="msg" placeholder="请输入聊天内容"> <!-- 发送按钮框 --> <input type="button" value="发送" id="sendBn" name="sendBn"> <br/> <br/> <!-- 退出登录按钮框 --> <input type="button" value="退出登录" id="closeBn" name="closeBn"> </body> </html>
二.四 重启服务器,验证 WebSocket
操作1: 打开窗口1, 输入网址: http://localhost/WebSocket/
窗口1显示:
操作2: 再打开一个窗口2, 输入网址: http://localhost/WebSocket/
窗口2 显示:
此时,窗口1 显示:
操作3: 在窗口1发送消息, “你好,我是两个蝴蝶飞”,点击发送按钮
此时,窗口1显示:
窗口2显示:
可以发现,自动推送消息到各个客户端。
操作4: 再打开一个窗口3, 输入网址: http://localhost/WebSocket/
窗口3显示:
并不会接收到他参与之前的消息记录,保证了消息的隐私性。
窗口1 显示:
窗口2 显示:
操作5: 点击窗口2的 退出登录按钮,点击之后
此时窗口2 显示:
窗口1显示:
窗口3 显示:
操作6: 直接点击 窗口3的 X ,直接关闭网页:
窗口1 显示:
可以接收到 窗口3退出的事件
窗口2 已经被关闭了,所以没有任何接收信息。
谢谢您的观看!!!