B/S系统架构模式只支持单点操作,如果我们要建立双向通道就得借助WebSocket。本文将会用一个简单的例子来阐述。
一、页面实现
var Chat = {};
Chat.socket = null;
Chat.connect = (function(host) {
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
//如果支持则创建WebSocket JS类
Chat.socket = new WebSocket(host);
} else if ('MozWebSocket' in window) {
Chat.socket = new MozWebSocket(host);
} else {
Console.log('WebSocket is not supported by this browser.');
return;
}
//回调函数,当和服务器的WebSocket连接建立起来后,浏览器会回调这个方法
Chat.socket.onopen = function () {
Console.log('Info: WebSocket connection opened.');
document.getElementById('chat').onkeydown = function(event) {
if (event.keyCode == 13) {
Chat.sendMessage();
}
};
};
//回调函数,当和服务器的WebSocket连接关闭后,浏览器会回调这个方法
Chat.socket.onclose = function () {
document.getElementById('chat').onkeydown = null;
Console.log('Info: WebSocket closed.');
};
//回调函数,当服务器有新消息发送到浏览器,浏览器会回调这个方法
Chat.socket.onmessage = function (message) {
Console.log(message.data);
};
});
二、后台实现
//Tomcat端的实现类加上@ServerEndpoint注解,里面的value是URL路径
@ServerEndpoint(value = "/websocket/chat")
public class ChatEndpoint {
private static final String GUEST_PREFIX = "Guest";
//记录当前有多少个用户加入到了聊天室,它是static全局变量。为了多线程安全使用原子变量AtomicInteger
private static final AtomicInteger connectionIds = new AtomicInteger(0);
//每个用户用一个CharAnnotation实例来维护,请你注意它是一个全局的static变量,所以用到了线程安全的CopyOnWriteArraySet
private static final Set<ChatEndpoint> connections =
new CopyOnWriteArraySet<>();
private final String nickname;
private Session session;
public ChatEndpoint() {
nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
}
//新连接到达时,Tomcat会创建一个Session,并回调这个函数
@OnOpen
public void start(Session session) {
this.session = session;
connections.add(this);
String message = String.format("* %s %s", nickname, "has joined.");
broadcast(message);
}
//浏览器关闭连接时,Tomcat会回调这个函数
@OnClose
public void end() {
connections.remove(this);
String message = String.format("* %s %s",
nickname, "has disconnected.");
broadcast(message);
}
//浏览器发送消息到服务器时,Tomcat会回调这个函数
@OnMessage
public void incoming(String message) {
// Never trust the client
String filteredMessage = String.format("%s: %s",
nickname, HTMLFilter.filter(message.toString()));
broadcast(filteredMessage);
}
//WebSocket连接出错时,Tomcat会回调这个函数
@OnError
public void onError(Throwable t) throws Throwable {
log.error("Chat Error: " + t.toString(), t);
}
//向聊天室中的每个用户广播消息
private static void broadcast(String msg) {
for (ChatAnnotation client : connections) {
try {
synchronized (client) {
client.session.getBasicRemote().sendText(msg);
}
} catch (IOException e) {
...
}
}
}
}
三、总结
通过以上简单的例子实现就能启用一个简单的WebSocket服务,而具体的配置和业务环境契合则需要更深入的学习,希望能够为读者提供一定的帮助。
参考资料:
如何实现一个简单的RPC