Skip to main content

Websocket

本框架对Websocket进行了封装,力求简洁易用,只需要实现一个ChannelListener即可。本框架中Websocket服务和常规Http服务是兼容的,也就是二者可以混合在一起使用。

定义通讯数据格式#

通常Websocket之间交互会对通讯数据格式进行统一定义,也可以对客户端和服务端分开定义不同对格式。当然,这不是必须的,也可以直接用String文本,再对文本进行解析。

public class ChatMessage {
public String text;
}

上面定义了一个简单的通讯对象,里面只有一个字符串字段,可以根据需要添加其他字段。如果不定义ChatMessage,也可以直接用String字符串通讯。

实现ChannelListener#

实现一个ChannelListener通讯类,至少需要实现2个方法,onConnect和onMessage,分别对应客户端连接和收到消息。

@WebSocketListener("/web/chat") // 映射通讯路径为/web/chat
public class ChatListener implements ChannelListener<ChatMessage> { // 注意这里明确了通讯格式为上面定义的ChatMessage
// 定义一个通讯组,每个channel可以属于不同的组或多个组,这里所有的channel都在private组
private String group = "private";
// 连接时
@Override
public void onConnect(Request request, Channel channel) {
channel.context().put("name", request.getSession().getAttribute("name"));
channel.join(group);
}
// 收到消息时
@Override
public void onMessage(Channel channel, ChatMessage message) {
if ("stop".equals(message.text)) {
channel.close();
return;
}
if(message.text.startsWith("group::")) {
// 将收到的消息包装一下广播给整个组
ChatMessage response = new ChatMessage();
response.text = "mirror back: " + message.text.substring(7) + " by " + channel.context().get("name");
channel.broadcast(group, response);
} else {
// 将收到的消息包装一下再返回给发送端
ChatMessage response = new ChatMessage();
response.text = "mirror back: " + message.text + " by " + channel.context().get("name");
channel.send(response);
}
}
}

映射通讯路径#

有两种方式进行通讯路径映射,一种是上面例子中通过@WebSocketListener("/web/chat")注解进行映射,另一种方式是通过程序动态映射,见下面实例:

public class Main extends Application {
@Override
protected void initialize() {
// 指定html页面模版文件路径
applicationConfig().setViewPath("/chat");
// 上面已经加了 @WebSocketListener("/web/chat") 注解,下面就不需要手工注册了
//webSocket().addListener("/web/chat", ChatMessage.class, ChatMessage.class, ChatListener.class);
}
public static void main(String[] args) {
Ready.For(Main.class).Work(args);
}
}

综合实例#

通过上面的定义,Websocket服务就已经可以正常工作了,为了演示完整的效果,这里进一步加入前端模版代码:

resources/chat/chat.html
<html>
<head>
<title>chat example</title>
</head>
<body>
<div class="page">
<div class="center">
<h1>WebSocket Chat</h1>
<div id="chat">
</div>
<form id="form" class="chatform" action="">
<label for="message">Message</label>
To Server: <input type="text" name="message" id="message">
To Group: <input type="text" name="group_message" id="group_message">
</form>
</div>
</div>
<script language="JavaScript">
if (!window.WebSocket) {
alert("the browser does not support WebSocket");
}
const socket = new WebSocket("ws://localhost:8080/web/chat");
socket.onmessage = function (event) {
const chat = document.getElementById("chat");
chat.innerHTML = chat.innerHTML + event.data + "<br />";
};
function send(message) {
if (socket.readyState === WebSocket.OPEN) {
const bean = {text: message};
socket.send(JSON.stringify(bean));
} else {
alert("socket is not open.");
}
return false;
}
document.getElementById("message").onkeypress = function () {
if (event.keyCode === 13) {
const input = document.getElementById("message");
send(input.value);
input.value = ''
}
};
document.getElementById("group_message").onkeypress = function () {
if (event.keyCode === 13) {
const input = document.getElementById("group_message");
send("group::" + input.value);
input.value = ''
}
};
document.getElementById("form").onsubmit = function () {
return false;
};
</script>
</body>
</html>

由于上面的模版路径在resources/chat/chat.html,所有需要配置一下模版路径:

public class Main extends Application {
@Override
protected void initialize() {
// 默认模版路径是/view,这里配置模版路径为/chat
applicationConfig().setViewPath("/chat");
// 除了可以在ChannelListener上通过@WebSocketListener("/web/chat")注解进行映射,还可以像下面这里进行映射
//webSocket().addListener("/web/chat", ChatMessage.class, ChatMessage.class, ChatListener.class);
}
public static void main(String[] args) {
Ready.For(Main.class).Work(args);
}
}

下面是Controller,渲染上面的html模版:

@RequestMapping("/")
public class ChatController extends Controller {
@RequestMapping
public void index() {
// 下面是随机生成一个用户,通过session共享给websocket服务
String name = (String)getSession().getAttribute("name");
if(name == null) {
getSession().setAttribute("name", "User_" + StrUtil.randomString(5).toLowerCase());
}
render("chat"); // 渲染上面的模版
}
}
注意事项

需要注意的是,上面模版中的javascript里面的websocket已经固定了URL为ws://localhost:8080/web/chat,所以请务必通过http://localhost:8080方式访问,如果通过http://127.0.0.1:8080方式访问,会因为域名不同而无法在http服务与websocket服务之间共享session。