概述
众所周知,http协议没发起一次请求都需要三次握手两次挥手,并且结束后自动断开连接.
如果我们想要制作一款联机游戏/即时通讯这种模式的网站程序的话,我们需要不停地发起请求,从而消耗了大量的性能. 因此当我们遇到这种场景的时候,一般采用webSocket作为通信协议. 在之前学习了Python socket实现局域网聊天 这样一个功能. 实际上流程都是差不多.
一般情况下我们使用后端语言编写服务端,然后前端使用Js进行接收. 代码中前端前端框架使用了Vue.js.
演示网站: wzq.ku-m.cn
前端代码: https://gitee.com/kumuuuu/websocket_online_gobang__vue
后端代码: https://gitee.com/kumuuuu/websocket_online_gobang_java
SpringBoot配置webSocket
引入Springboot内置好的启动器~(太贴心了...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建配置文件. 后面解释它的作用~
@Configuration
public class webSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
自此,Springboot配置完毕啦~ 哈哈哈哈,没错就是这么简单.
然后我们需要需要编写一下,当客户端连接成功到我们服务端执行什么方法呀,连接关闭执行什么呀,当收到客户端信息执行什么呀,这些都是需要我们自己进行注册的.
首先我们需要创建一个servce~ 然后加上注解 @ServerEndpoint(value = "/test")
通过这个注解,就是声明我们webSocket协议的相应地址为: ws://localhost:8080/test ,注意! 这里就不再是http开头啦,我们使用的是webSocket协议,端口由自己而决定.
当然,不要忘记再加 @Component 将它交给spring进行管理.
@Slf4j (注册log4J日志)
@Component
@ServerEndpoint(value = "/test")
public class MySocket {}
接下来就是注册上面我们说到的,打开连接,关闭连接,对应的方法了.
值得注意的是,当发起请求的时候,客户端会携带一个Session,我们可以把它保存起来,然后通过它就可以实现服务端给客户端发送信息了!
@OnOpen
public void onOpen(Session session) {
this.session = session;
}
其他的也是大同小异,注解分别为: @OnClose, @OnMessage(拥有一个string参数,即用户传来的文本).很显然,分别是声明关闭方法和收到信息的方法.
前面说了,我们可以通过session进行给客户端发送信息,那么只要我们把这个session用map记录下来,我们事后想给某个人发送信息,不就可以直接取出来发送了嘛.
所以我们一般使用ConcurrentHashMap进行记录,它与hashmap的区别是,它是属于线程安全的~
接下来我们就来看看如何使用session进行发送信息.
public void sendMessage(Session session, String message) throws IOException {
session.getBasicRemote().sendText(message);
}
我们可以直接使用session下的getBasicRemote()方法进行sendText() ,即可发送. 实际上还有另外一种方法,为
session.getAsyncRemote().sendText(message);
他们的区别是,前者是属于同步发送,后者属于异步发送. 百度得知一般我们都采用异步发送,但在编写Demo的途中发现使用异步发送会出错,具体原因没有深究.
写到这里,已经得知信息如何发送,可是这样是不是有一些单一? 我们不可能只是单一的发送一些消息什么的,我们需要的是传输数据. 而数据又理应分类! 比如五子棋项目,首先我们需要连接双方的信息/游戏状况的信息...等!
所以我们一般采用Json数据形式,由此来整理分类各种信息.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class infoMsg {
private int mode;
private String msg;
public String toJson(){
return JSON.toJSONString(this);
}
}
像五子棋项目,我定义了一共9个类型.
mode 0 ~ 2 == 以此为前端发送普通消息/成功消息/警告信息. 3是获取列表,4是通知各个用户新增的用户~ ..等
自此,后端项目的基本结构已经阐述完毕.
Js连接webSocket
前端很简单,直接连接我们创建的地址就ok.
ws = new WebSocket("ws://121.199.52.206:5003/test")
同后端一样,我们只需要创建对应的事件方法就好了~ 连接后(onopen)/连接错误(onerror)/收到信息(onmessage).
ws.onopen = () => {
_this.$notify({
title: '成功',
message: '服务器连接成功!',
type: 'success'
})
}
ws.onerror = () => {
_this.$notify({
title: '警告',
message: '服务器连接失败!',
type: 'warning'
});
}
二者大同小异.
主要业务代码
向所有在线用户发送信息
用意是提醒用户有新用户上线,其实就是遍历存储用户session的hashmap然后sendMessage.
public void sendUserAll(String name) throws IOException {
for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
sendMessage(entry.getValue(), new infoMsg(4, name).toJson());
}
}
判断游戏是否结束
就是判断用户当前的操作是否已达成五子连珠. 本来打算从网上复制一份的,但总感觉网上别人写的好智障23333...
思路很简单,只需要判断用户当前下的那个棋的上,下,左,右,上左,上右,下左,下右,八个方向与相连的方向一起是否构成五子连珠.
public boolean GameCheck(int x, int y, int[][] arr) {
int base = arr[x][y];
int[][] pn = {{-1,-1},{-1,0},{-1,1},{1,-1},{1,0},{1,1},{0,-1},{0,1}};
int[] result = new int[8];
for (int i = 0; i < 8; i++) {
int count = 0;
int x1 = x;
int y1 = y;
while (true) {
x1 += pn[i][0];
y1 += pn[i][1];
if (x1 < 0 || y1 < 0 || x1 >= arr.length || y1 >= arr.length
|| arr[x1][y1] != base) {
result[i] = count;
break;
} else {
count++;
}
}
}
if (result[0] + result[5] == 4 || result[1] + result[4] == 4 ||
result[2] + result[3] == 4 || result[6] + result[7] == 4) {
return true;
}
return false;
}