这学期有一门课需要做一款可以联网的游戏,游戏使用Godot引擎。由于WebSocket简单易懂并且支持双向通信,所以我选择了它作为游戏客户端之间的交流协议。
需求分析:
每个客户端可以创建游戏房间,系统会随机生成房间号。另一个客户端在游戏界面输入房间号后即可加入房间。由于游戏是回合制问答比赛(类似Kahoot),所以每回合要求所有用户同时开始。
使用库:
javax.websocket
WebSocket Server部分部署在远程服务器上,用于处理各本地客户端发来的请求并且在客户端之间传递信息。Java库提供了很多直观的API。
以下是定义WebScoket服务器的例子。
- 定义服务器类(如下),其中
@ServerEndpoint
表示这个类是同服务器端相关联,参数表示该WebSocket应用的地址,比如服务器的地址是127.0.0.1:8000
,那么对于用户名为t170815518的用户客户端来说这个WebSocket应用的地址是127.0.0.1:8000/multi_quiz/t170815518
,用户名部分会作为该请求的一个参数。
@ServerEndpoint(value = "/multi_quiz/{username}")
public class WebSocketServer {
...
}
- 定义WebSocket周期中的各个函数,其中包括——连接、通信和断连,并分别通过注解
@OnOpen
,@OnMessage
,@OnClose
来表示.接下来是详解:
- 每当新用户与WebSocket服务器连接时,都会触发该函数,函数的参数包括
Session
,若地址中包括参数,则通过@PathParam("username") String username
解析地址中的对应位置的变量并存储在对应变量username
中。
@OnOpen
public void OnOpen(Session session, @PathParam("username") String username) {
logger.info(String.format("Session created, User %s joins the game.", username));
// add online user
Client client = new Client(username, session);
users.add(client);
}
- 每当服务器收到客户端传来的消息时,消息以字节数组的形式传递,因此在接收消息后,可以通过
String s = new String(message, StandardCharsets.UTF_8);
解码为常用字符串格式,然后转换为json文件。我们可以规定json文件的格式,在这里我规定json文件中必须包括method
这个键对应不同类型的消息,比如创建游戏的请求对应method: "create"
, 而每回合客户端作出动作可以对应method: "action"
. 之后在函数体中,我们可以用switch语句处理不同种类的请求。
@OnMessage
public void OnMessage(byte[] message) {
String s = new String(message, StandardCharsets.UTF_8);
if (s.charAt(0) != '{') {
s = s.substring(1, s.length() - 1).replace("\\", ""); // clean-up data for parse
}
logger.info(String.format("Message got from Session: %s", s));
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(s);
} catch (JSONException e) {
logger.warning(String.format("Cannot parse %s", s));
return;
}
...
@OnClose
函数更直接,表示客户端断开连接后触发的服务器端函数。比如下列函数会在客户端断连后在在线用户列表里删去该用户。
@OnClose
public void OnClose(Session session) {
for (Client user: users) {
if (user.getSession().equals(session)) {
users.remove(user);
logger.info(String.format("User %s leaves the game.", user.getUsername()));
break;
}
}
}
- 服务器端向客户端发送以字节串表示的信息json字符串:
Session session;
session.getBasicRemote().sendBinary(ByteBuffer.wrap(feedback.toBytes()));
为了方便测试,我们也可以用javax.websocket
定义客户端,具体格式如下, 和服务器端类似:
@ClientEndpoint
public class ClientTestServer {
@OnOpen
public void OnOpen(Session session) {
...
}
@OnMessage
public void OnMessage(byte[] bytes) {
...
}
...
}
完整的项目代码:https://github.com/t170815518/Game_WebSocket
参考资料:
Coward, D., 2013. Java WebSocket Programming. [Place of publication not identified]: Oracle Press.