后端篇
这是从朋友那得到的一个 socket类
。
package com.communication.firstjob.util;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint("/webSocket/{sid}")
@Component
public class WebSocketUtil {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static AtomicInteger onlineNum = new AtomicInteger();
//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
//发送消息
public void sendMessage(Session session, String message) throws IOException {
if(session != null){
//表示该session对象相同时锁起作用,synchronized
synchronized (session) {
// System.out.println("发送数据:" + message);
session.getBasicRemote().sendText(message);
}
}
}
//给指定用户发送信息
public void sendInfo(String userName, String message){
Session session = sessionPools.get(userName);
try {
sendMessage(session, message);
}catch (Exception e){
e.printStackTrace();
}
}
//建立连接成功调用
@OnOpen
public void onOpen(Session session, @PathParam(value = "sid") String userName){
sessionPools.put(userName, session); //存放信息
addOnlineCount(); //onlineNum+1
System.out.println(userName + "加入webSocket!当前人数为" + onlineNum);
try {
sendMessage(session, "欢迎" + userName + "加入连接!");
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接时调用
@OnClose
public void onClose(@PathParam(value = "sid") String userName){
sessionPools.remove(userName);
subOnlineCount();
System.out.println(userName + "断开webSocket连接!当前人数为" + onlineNum);
}
//收到客户端信息
@OnMessage
public void onMessage(String message) throws IOException{
message = "客户端:" + message + ",已收到";
System.out.println(message);
for (Session session: sessionPools.values()) {
try {
sendMessage(session, message);
} catch(Exception e){
e.printStackTrace();
continue;
}
}
}
//错误时调用
@OnError
public void onError(Session session, Throwable throwable){
System.out.println("发生错误");
throwable.printStackTrace();
}
public static void addOnlineCount(){
onlineNum.incrementAndGet();
}
public static void subOnlineCount() {
onlineNum.decrementAndGet();
}
}
1.@Component是个啥?
链接: 注解诠释大全.
@component (把普通pojo实例化到spring容器中,相当于配置文件中的)
泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类
2.ConcurrentHashMap<String, Session> 是个啥?
链接: 各种map的介绍.
3.那你为什么用ConcurrentHashMap不用别的map?
关键在于这句话:
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
而且比起hashmap线程不安全 ConcurrentHashMap线程安全
当时我就想,独占很简单:就是我能改别人不能改;
允许多个修改:是我能改别人也能改。
现在场景是我登录账号A,你登陆账号B,如果是hashmap,那么我发信息,你得等我发完,你才能发,这谁能干,所以用ConcurrentHashMap
项目 | hashmap | ConcurrentHashMap |
---|---|---|
安全性 | 不安全 | 安全 |
线程 | 独占 | 并发 |
4.锁是怎么玩的
看下这段代码
public void sendMessage(Session session, String message) throws IOException {
if(session != null){
synchronized (session) {
session.getBasicRemote().sendText(message);
}
}
}
链接: synchronized同步锁.
说实话,没咋看明白这个
主要作用:当锁住的内容执行完或者在执行过程中抛出异常,才会自动释放锁。
当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
哦,那就是如果你用户不为空了,我再把你设定成并发锁,也就是当我发送消息(调用该方法时),我得等我发完这条我才能发下一条。
所以总而言之:ConcurrentHashMap实现的是A和B可以同时调用系统同一函数
synchronized是A(或B)内部发消息的一种并发等待锁,实现效果是让你一条一条的发
5.如何实现【我给你发一条】的效果的?
//给指定用户发送信息
public void sendInfo(String userName, String message){
Session session = sessionPools.get(userName);
try {
sendMessage(session, message);
}catch (Exception e){
e.printStackTrace();
}
}
比如user A
user.sendInfo(“B”,“HELLO B”);
A通过调取sendInfo中的sendMessage()函数将话扔给B
B接收,并显示( 显示由代码session.getBasicRemote().sendText(message);实现)
public void sendMessage(Session session, String message) throws IOException {
if(session != null){
//表示该session对象相同时锁起作用,synchronized
synchronized (session) {
// System.out.println("发送数据:" + message);
session.getBasicRemote().sendText(message);
}
}
}
前端篇
<template>
<div class="box">
<div class="titleBg">
Let's talk here
</div>
<el-row>
<em-col :span="8">
<el-button type="info" @click="openScoket">上线</el-button></em-col
>
<em-col :span="8">
<el-button type="info" @click="closeScoket">下线</el-button></em-col
>
<em-col :span="8">
<el-button type="info" @click="sendToAll">广播</el-button></em-col
>
</el-row>
</div>
</template>
<script>
export default {
name: "IndexView",
data() {
return {
socket: {},
readyState: this.socket.readyState
};
},
props: {},
components: {},
mounted() {},
methods: {
openScoket() {
console.log("你点击了上线");
if (typeof WebSocket == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
//建立socket
this.socket = new WebSocket("ws://127.0.0.1:9000/webSocket/123");
console.log(this.socket.readyState);
if (this.socket.readyState == 1) {
console.log("websocket已建立");
}
}
},
closeScoket() {
console.log("你点击了下线");
this.socket.close();
},
sendToAll() {
console.log("你点击了广播");
this.socket.send("你好");
this.socket.message();
}
},
watch: {
readyState(newValue, oldVaue) {
console.log(oldVaue);
console.log(newValue);
}
}
};
</script>
1.与后端有什么关联?
sendToAll() {
console.log("你点击了广播");
this.socket.send("你好");
this.socket.message();
}
socket.send 方法哪来的????
后端也没写这个方法啊
首先发现了一个事
this.socket = new WebSocket("ws://127.0.0.1:9000/webSocket/123");
这哥们真正类型是websocket, 哦,那我百度websocket
链接: websocket的readystate.
方法:
WebSocket.send(data)对要传输的数据进行排队。
事件: | 使用 addEventListener() 或将一个事件监听器赋值给本接口的 oneventname 属性,来监听下面的事件。 |
---|---|
close | 当一个 WebSocket 连接被关闭时触发。也可以通过 onclose 属性来设置。 |
error | 当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。也可以通过 onerror 属性来设置. |
message | 当通过 WebSocket 收到数据时触发。也可以通过 onmessage 属性来设置。 |
open | 当一个 WebSocket 连接成功时触发。也可以通过 onopen 属性来设置。 |
示例:
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
噢,此时我恍然大悟,原来前端的.message()就规定,让你触发后端的onmessage
之后还是不咋明白,怎么建立连接,怎么传的,系统:我咋知道我啥时候接到数据了?
this.socket = new WebSocket("ws://127.0.0.1:9000/webSocket/123");
这段代码就是建立连接
然后触发后端onopen
this.socket.send("你好");
this.socket.message();
WebSocket.send(“你好”)对要传输的“你好”数据进行排队
官网说:通过 WebSocket 先收到数据后触发onmessage
那系统傻啊,我哪知道我啥时候收到数据?
快递员放包裹到门口了(send)后大喊一声快递!(事件:message),然后我听着声我才能去取
这里不像上一个系统知道我建连接了,这里系统不知道他接收了信息,所以,系统不会自己触发onmessage,而是我强制你去.message方法,来让你走onmessage这个函数。