1. WebSocket介紹
-
WebSocket 是一種網路通信協議,RFC6455 定義了它的通信標準,
-
WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議,
-
HTTP 協議是一種無狀態的、無連接的、單向的應用層協議,它采用了請求/回應模型,通信請求只能由客戶端發起,服務端對請求做出應答處理,
-
這種通信模型有一個弊端:HTTP 協議無法實作服務器主動向客戶端發起訊息,
-
這種單向請求的特點,注定了如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩,大多數 Web 應用程式將通過頻繁的異步 AJAX 請求實作長輪詢,輪詢的效率低,非常浪費資源(因為必須不停連接,或者 HTTP 連接始終打開),
2. websocket協議
-
本協議有兩部分:握手和資料傳輸,
-
握手是基于http協議的,
- 客戶端(瀏覽器)實作
3.1 websocket物件
實作 WebSockets 的 Web 瀏覽器將通過 WebSocket 物件公開所有必需的客戶端功能(主要指支持 Html5 的瀏覽器),
以下 API 用于創建 WebSocket 物件:
var ws = new WebSocket(url);
引數url格式說明: ws://ip地址:埠號/資源名稱
3.2 websocket事件
WebSocket 物件的相關事件
| 事件 | 事件處理程式 | 描述 |
|---|---|---|
| onopen | websocket物件.onopen | 連接建立時觸發 |
| onmessage | websocket物件.onmessage | 客戶端接收服務端資料時觸發 |
| onerror | websocket物件.onerror | 通信發生錯誤時觸發 |
| onclose | websocket物件.onclose | 連接關閉時觸發 |
3.3 WebSocket方法
WebSocket 物件的相關方法:
| 方法 | 描述 |
|---|---|
| send | 使用連接時發送訊息 |
服務器實作
Java WebSocket應用由一系列的WebSocketEndpoint組成,Endpoint 是一個java物件,代表WebSocket鏈接的一端,對于服務端,我們可以視為處理具體WebSocket訊息的介面, 就像Servlet之與http請求一樣,
我們可以通過兩種方式定義Endpoint:
· 第一種是編程式, 即繼承類 javax.websocket.Endpoint并實作其方法,
· 第二種是注解式, 即定義一個POJO, 并添加 @ServerEndpoint相關注解,
實作流程

服務端如何接收資料
通過為 Session 添加 MessageHandler 訊息處理器來接收訊息,當采用注解方式定義Endpoint時,我們還可以通過 @OnMessage 注解指定接收訊息的方法,
服務端如何推送資料
發送訊息則由RemoteEndpoint完成,其實體由Session維護,根據使用情況,我們可以通過
Session.getBasicRemote獲取同步訊息發送的實體,然后呼叫其sendXxx()方法就可以發送訊息,可以通過
Session.getAsyncRemote獲取異步訊息發送實體,
實作一個簡單的聊天室功能
步驟:
1.首先匯入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
private Long id;
@TableField(value = "https://www.cnblogs.com/ItChenjl/archive/2023/06/09/from_user_id")
private User fromUser;
@TableField(value = "https://www.cnblogs.com/ItChenjl/archive/2023/06/09/to_user_id")
private User toUser;
private String content;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime updateTime;
private int messageType;
@TableField(exist = false)
private String toName;
@TableField(exist = false)
private String fromName;
@TableField(exist = false)
private String message;
//添加好友碼
public static final int ADD_FRIEND = 2;
//好友串列訊息碼
public static final int FRIEND_LIST_TYPE = 3;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T>{
/**
* 狀態碼
*/
private Integer code;
/**
* 提示資訊,如果有錯誤時,前端可以獲取該欄位進行提示
*/
private boolean flag;
private String message;
private T data; //資料
public static <T> Result<T> success(T object) {
Result<T> r = new Result<T>();
r.data = https://www.cnblogs.com/ItChenjl/archive/2023/06/09/object;
r.code = 1;
r.flag = true;
return r;
}
public static Result error(String message) {
Result r = new Result();
r.message = message;
r.code = 0;
r.flag = false;
return r;
}
}
@PostMapping("/login")
public Result<User> login(@RequestBody User user ,HttpSession session){
String password = user.getPassword();
String username = user.getUsername();
log.info("用戶登錄操作");
//MD5加密
password = DigestUtils.md5DigestAsHex(password.getBytes());
//根據用戶名查找資料庫
LambdaQueryWrapper<User> wrapper =new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername,username);
User one = userService.getOne(wrapper);
if (one == null){
return Result.error("用戶不存在,請先注冊");
}if (! one.getPassword().equals(password)){
return Result.error("用戶名或者密碼有誤");
}
// 登錄成功,將用戶的ID存盤到WebSocket連接的Session中
session.setAttribute("userId", one.getId()); // 假設用戶ID為one.getId()
String sessionId = session.getId();
return Result.success(one);
}
@GetMapping("/getUsername")
private String getUsername(HttpSession session) {
// 從 HttpSession 中獲取用戶資訊
User user = (User) session.getAttribute("user");
if (user != null) {
return user.getUsername();
}
return null;
}
@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
@Bean
//注入ServerEndpointExporter bean.物件,自動注冊使用了@ServerEndpoint
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 定義一個 WebSocket 入口,客戶端需要連接到它才能接收推送訊息
registry.addEndpoint("/websocket").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 啟用推送的訊息代理(即使用 STOMP 實作 WebSocket 的代理)
config.enableSimpleBroker("/topic");
// 開啟基于用戶的 WebSocket 會話
config.setUserDestinationPrefix("/user");
}
}
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {
/**
* 獲取session物件
* @param sec
* @param request
* @param response
*/
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
//獲取HttpSession.物件
HttpSession httpsession = (HttpSession) request.getHttpSession();
//將httpSession存盤到配置物件
sec.getUserProperties().put(HttpSession.class.getName(), httpsession);
}
}
@ServerEndpoint(value = "https://www.cnblogs.com/chat",configurator = GetHttpSessionConfig.class)
@Component
@Slf4j
public class ChatEndpoint {
private Session session;
private static HttpSession httpSession;
//用來存盤每一個客戶端物件對應的ChatEndpoint物件
private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();
@OnOpen
public void onopen(Session session, EndpointConfig config) {
//將區域的session物件賦值給成員session
this.session = session;
//獲取Httpsession物件 ,鍵值對集合,得到鍵獲取值
this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
String user = (String) this.httpSession.getAttribute("user");
onlineUsers.put(user,session);
//廣播訊息,獲取在線的所有好友
String message = MessageUtils.getMessage(true, null, getFriendsName());
broadcastAllUsers(message);
}
/**
* 獲取所有在線的好友資訊,名稱
* @return
*/
public Set getFriendsName(){
Set<String> set = onlineUsers.keySet();
return set;
}
/**
* 發給所有人的廣播
* @param message
*/
private void broadcastAllUsers(String message){
// 拿到所有的用戶的chatEndpoint物件 //存盤用戶的session資訊
Set<Map.Entry<String,Session>> entries = onlineUsers.entrySet();
//遍歷map集合
for (Map.Entry<String, Session> entry : entries) {
//獲取所有用戶對應的session物件 //擁有getBasicRemote發送訊息的方法
Session session =entry.getValue();
//發送訊息
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@OnMessage
public void onMessage(String message, @PathParam("username") String username){
try {
log.info("服務端收到用戶username={}的訊息:{}", username, message);
//將訊息轉換成message物件
Message msg = JSON.parseObject(message,Message.class);
//獲取接收方的用戶名
String toName = msg.getToName();
//獲取訊息資料
String message1 = msg.getMessage();
//獲取接收方的用戶的session物件
Session session = onlineUsers.get(toName);
if (session != null) {
// 獲取當前登錄的用戶 從session中獲取
String user = (String) httpSession.getAttribute("user"); //user代表的是發送方
String message2 = MessageUtils.getMessage(false, user, message1);
session.getBasicRemote().sendText(message2);
}else {
log.info("未找到用戶username{}的session",toName);
}
if (msg.ADD_FRIEND ==2 && session != null){
//獲取發送發
String user = (String) httpSession.getAttribute("user");
//獲取訊息
String message2 = MessageUtils.getMessage(false, user, message1);
//發送
session.getBasicRemote().sendText(message2);
//將好友添加到相應的串列中,例如用Map存盤好友串列
Map<String, List<String>> friendLists = (Map<String, List<String>>) httpSession.getAttribute("friendLists");
List<String> friendList=friendLists.get(user);
friendList.add(toName);
friendLists.put(user,friendList);
httpSession.setAttribute("friendLists", friendLists);
//發送好友串列
Message friendListMessage = new Message();
friendListMessage.setMessageType(3);
friendListMessage.setFromName("System");
friendListMessage.setToName(user);
friendListMessage.setMessage(JSON.toJSONString(friendList));
ChatEndpoint.send(friendListMessage,getFriendsName());
}
else {
log.info("未找到用戶username{}的session",toName);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void send(Message friendListMessage, Set friendsName) {
try {
String toName = friendListMessage.getToName();
String message = friendListMessage.getMessage();
Session session = onlineUsers.get(toName);
if (session != null) {
String json = JSON.toJSONString(message);
session.getBasicRemote().sendText(json);
} else {
log.info("未找到用戶{}的session", toName);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@OnClose
public void onclose(Session session) {
//提出session中的記錄
String user = (String) this.httpSession.getAttribute("user");
onlineUsers.remove(user);
//通知所有用戶,此賬號下線,
String message = MessageUtils.getMessage(true, null, getFriendsName());
broadcastAllUsers(message);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554747.html
標籤:其他
下一篇:返回列表
