勿以恶小而为之,勿以善小而不为--------------------------刘备

劝诸君,多行善事积福报,莫作恶

上一章简单介绍了WebSocket的了解(一),如果没有看过,​​请观看上一章​​

本文代码参考了李刚老师编写的 轻量级Java EE 企业应用实战(Struts2+Spring4+Hibernate 整合开发) 一书 中的"Servlet 3.0 的Web 模块支持" 章节代码。

一. 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

Servlet实现WebSocket的简单聊天室(二)_ServerEndpoint

Servlet实现WebSocket的简单聊天室(二)_Servlet的四个方法注解_02

Servlet实现WebSocket的简单聊天室(二)_ServerEndpoint_03

有图片

有图片

二.二 服务器端代码

创建 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显示:

Servlet实现WebSocket的简单聊天室(二)_实现WebSocket代码_04

操作2: 再打开一个窗口2, 输入网址: http://localhost/WebSocket/

窗口2 显示:

Servlet实现WebSocket的简单聊天室(二)_Servlet实现WS_05

此时,窗口1 显示:

Servlet实现WebSocket的简单聊天室(二)_ServerEndpoint_06

操作3: 在窗口1发送消息, “你好,我是两个蝴蝶飞”,点击发送按钮

此时,窗口1显示:

Servlet实现WebSocket的简单聊天室(二)_Servlet的WS注解_07

窗口2显示:

Servlet实现WebSocket的简单聊天室(二)_Servlet的四个方法注解_08

可以发现,自动推送消息到各个客户端。

操作4: 再打开一个窗口3, 输入网址: http://localhost/WebSocket/

窗口3显示:

Servlet实现WebSocket的简单聊天室(二)_ServerEndpoint_09

并不会接收到他参与之前的消息记录,保证了消息的隐私性。

窗口1 显示:

Servlet实现WebSocket的简单聊天室(二)_Servlet的WS注解_10

窗口2 显示:

Servlet实现WebSocket的简单聊天室(二)_实现WebSocket代码_11

操作5: 点击窗口2的 退出登录按钮,点击之后

此时窗口2 显示:

Servlet实现WebSocket的简单聊天室(二)_Servlet的四个方法注解_12

窗口1显示:

Servlet实现WebSocket的简单聊天室(二)_ServerEndpoint_13

窗口3 显示:

Servlet实现WebSocket的简单聊天室(二)_实现WebSocket代码_14

操作6: 直接点击 窗口3的 X ,直接关闭网页:

窗口1 显示:

Servlet实现WebSocket的简单聊天室(二)_实现WebSocket代码_15

可以接收到 窗口3退出的事件

窗口2 已经被关闭了,所以没有任何接收信息。

Servlet实现WebSocket的简单聊天室(二)_Servlet实现WS_16

本章节代码链接为:

链接:https://pan.baidu.com/s/1DIOzdoLIS-jhIwZKIq3sDA 
提取码:txrs

谢谢您的观看!!!