环境
- tomcat 9
- JDK8
- 带框架的web项目
使用
Jar包的选择
由于网上的教程所使用的包很多很杂,可能一般人看不出到底怎么用,其实选择很简单
- J2EE:有j2ee的javaee-api
- 其他:javax.websocket
- tomcat:自带websocket-api
前端源码
var ws = null;
if('WebSocket' in window) {
ws = new WebSocket("ws://localhost:8080/xx/websocket");
ws.onopen = function(){
};
ws.onmessage = function(evt){
console.log(evt);
var received_msg = evt.data;
};
ws.onclose = function(){
alert("连接已关闭...");
};
ws.onerror = function(){
alert("发生错误");
};
}
后端代码
import java.io.IOException;
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("/websocket")
public class WebsocketTest {
//静态变量,用来记录当前在线连接数。
private static AtomicInteger onlineCount = new AtomicInteger(0);
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
protected static CopyOnWriteArraySet<WebsocketTest> webSocketSet = new CopyOnWriteArraySet<WebsocketTest>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
public WebsocketTest() {
System.out.println(" WebSocket init~~~");
}
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for(WebsocketTest item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public static int getOnlineCount() {
return onlineCount.get();
}
public static void addOnlineCount() {
onlineCount.incrementAndGet();
}
public static void subOnlineCount() {
onlineCount.decrementAndGet();
}
}
问题解析
404错误
- Jar包没有成功调用:可将tomcat的websocket-api包配置到项目Build Path作为外部包,同时保证只有一种websocket包在使用
- 路径被拦截:例如框架自动拦截路径的,先放开对应的websocket路径规则,然后注意完整路径是localhost:8080/项目名/websocket注解路径
302错误
- 后端没有定义正确,注解没有完全生效
设计模式的引入
- 简单的可以分拆成websocket类和管理会话的SessionManager单例类,符合单一职责原则
- 复杂的可以搞成类似观察者模式的形式