WebSocket
是 HTML5
新增的一种在单个 TCP
连接上进行全双工通讯的协议,与 HTTP
协议没有太大关系….
在 WebSocket API
中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript
向服务器发出建立 WebSocket
连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 WebSocket
连接后,你可以通过 send()
方法来向服务器发送数据,并通过 onmessage()
事件来接收服务器返回的数据..
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketUtils
public final class WebSocketUtils {
/**
* 模拟存储 websocket session 使用
*/
public static final Map<String, Session> LIVING_SESSIONS_CACHE = new ConcurrentHashMap<>();
public static void sendMessageAll(String message) {
LIVING_SESSIONS_CACHE.forEach((sessionId, session) -> sendMessage(session, message));
}
/**
* 发送给指定用户消息
*
* @param session 用户 session
* @param message 发送内容
*/
public static void sendMessage(Session session, String message) {
if (session == null) {
return;
}
final RemoteEndpoint.Basic basic = session.getBasicRemote();
if (basic == null) {
return;
}
try {
basic.sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
HTML
onopen
建立 WebSocket 连接时触发。message
客户端监听服务端事件,当服务端向客户端推送消息时会被监听到。error
WebSocket 发生错误时触发。close
关闭 WebSocket 连接时触发。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>battcn websocket</title>
<script src="jquery-3.2.1.min.js" ></script>
</head>
<body>
<label for="message_content">聊 天 室 </label><textarea id="message_content" readonly="readonly" cols="57" rows="10">
</textarea>
<br/>
<label for="in_user_name">用户姓名 </label><input id="in_user_name" value=""/>
<button id="btn_join">加入聊天室</button>
<button id="btn_exit">离开聊天室</button>
<br/><br/>
<label for="in_room_msg">群发消息 </label><input id="in_room_msg" value=""/>
<button id="btn_send_all">发送消息</button>
<br/><br/><br/>
好友聊天
<br/>
<label for="in_sender">发送者 </label><input id="in_sender" value=""/><br/>
<label for="in_receive">接受者 </label><input id="in_receive" value=""/><br/>
<label for="in_point_message">消息体 </label><input id="in_point_message" value=""/><button id="btn_send_point">发送消息</button>
</body>
<script type="text/javascript">
$(document).ready(function(){
var urlPrefix ='ws://localhost:8080/chat-room/';
var ws = null;
$('#btn_join').click(function(){
var username = $('#in_user_name').val();
var url = urlPrefix + username;
ws = new WebSocket(url);
ws.onopen = function () {
console.log("建立 websocket 连接...");
};
ws.onmessage = function(event){
//服务端发送的消息
$('#message_content').append(event.data+'\n');
};
ws.onclose = function(){
$('#message_content').append('用户['+username+'] 已经离开聊天室!');
console.log("关闭 websocket 连接...");
}
});
//客户端发送消息到服务器
$('#btn_send_all').click(function(){
var msg = $('#in_room_msg').val();
if(ws){
ws.send(msg);
}
});
// 退出聊天室
$('#btn_exit').click(function(){
if(ws){
ws.close();
}
});
$("#btn_send_point").click(function() {
var sender = $("#in_sender").val();
var receive = $("#in_receive").val();
var message = $("#in_point_message").val();
$.get("/chat-room/"+sender+"/to/"+receive+"?message="+message,function() {
alert("发送成功...")
})
})
})
</script>
</html>
Main
@ServerEndpoint
中的内容就是 WebSocket
协议的地址,其实仔细看会发现与 @RequestMapping 也是异曲同工的…
HTTP 协议:http://localhost:8080/path
WebSocket 协议:ws://localhost:8080/path
@OnOpen
、@OnMessage
、@OnClose
、@OnError
注解与 WebSocket
中监听事件是相对应的…
@RestController
@ServerEndpoint("/chat-room/{username}")
@SpringBootApplication
public class Chapter21Application {
public static void main(String[] args) {
SpringApplication.run(Chapter21Application.class, args);
}
private static final Logger log = LoggerFactory.getLogger(Chapter21Application.class);
@OnOpen
public void openSession(@PathParam("username") String username, Session session) {
LIVING_SESSIONS_CACHE.put(username, session);
String message = "欢迎用户[" + username + "] 来到聊天室!";
log.info(message);
sendMessageAll(message);
}
@OnMessage
public void onMessage(@PathParam("username") String username, String message) {
log.info(message);
sendMessageAll("用户[" + username + "] : " + message);
}
@OnClose
public void onClose(@PathParam("username") String username, Session session) {
//当前的Session 移除
LIVING_SESSIONS_CACHE.remove(username);
//并且通知其他人当前用户已经离开聊天室了
sendMessageAll("用户[" + username + "] 已经离开聊天室了!");
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@OnError
public void onError(Session session, Throwable throwable) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
throwable.printStackTrace();
}
@GetMapping("/chat-room/{sender}/to/{receive}")
public void onMessage(@PathVariable("sender") String sender, @PathVariable("receive") String receive, String message) {
sendMessage(LIVING_SESSIONS_CACHE.get(receive), "[" + sender + "]" + "-> [" + receive + "] : " + message);
}
}