施工企業(yè)負責(zé)人帶班檢查計劃汕頭百度seo公司
前言
????????握手連接是WebSocket建立通信的第一步,通過客戶端和服務(wù)器之間的一系列握手操作,確保了雙方都支持WebSocket協(xié)議,并達成一致的通信參數(shù)。握手連接的過程包括客戶端發(fā)起握手請求、服務(wù)器響應(yīng)握手請求以及雙方完成握手連接。完成握手連接后,客戶端和服務(wù)器之間建立了一個持久性的雙向通信鏈路,可以進行實時的數(shù)據(jù)傳輸。
? ? ? ?本次案例,使用 servlet 實現(xiàn)一個用戶登錄進入聊天室聊天。
一、前期準(zhǔn)備
1、新建項目,結(jié)構(gòu)如下
2、導(dǎo)入依賴
<!-- websocket 依賴 --><dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version><scope>provided</scope></dependency><!-- 打印日志 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.3.8</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><!-- ch02 --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.14.2</version></dependency>
?讓我逐個解釋這些依賴項的作用:
javax.websocket-api: 這是Java WebSocket API的依賴項,用于在Java應(yīng)用程序中實現(xiàn)WebSocket通信。
logback-classic: 這是Logback日志框架的經(jīng)典實現(xiàn),用于打印日志。
lombok: 這是一個Java庫,用于通過注解減少Java代碼的樣板代碼量,提高代碼的可讀性和簡潔性。
javax.servlet-api: 這是Java Servlet API的依賴項,用于開發(fā)基于Java的Web應(yīng)用程序。
jackson-databind: 這是Jackson JSON處理庫的依賴項,用于在Java應(yīng)用程序中進行JSON數(shù)據(jù)的序列化和反序列化操作。
二、使用 servlet 實現(xiàn)登錄功能
1、新建一個 loginServlet
/*** @Date 2023-11-01* @Author qiu* 用戶登錄*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String userName = req.getParameter("userName");// 將用戶名保存到 HttpSessionreq.getSession().setAttribute("user",userName);// 重定向到聊天的首頁resp.sendRedirect("chat.html");}
}
讓我來逐行解釋這段代碼的功能:
@WebServlet("/login")
:這是一個Servlet注解,指定了該Servlet對應(yīng)的URL路徑為"/login"。當(dāng)客戶端發(fā)送帶有"/login"路徑的請求時,Servlet容器將調(diào)用該Servlet來處理請求。
public class LoginServlet extends HttpServlet
:這是一個命名為LoginServlet的Java類,它繼承自HttpServlet類,表示它是一個Servlet。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
:這是Servlet的service方法,它負責(zé)處理客戶端的請求并返回響應(yīng)。HttpServletRequest對象提供了關(guān)于HTTP請求的信息,而HttpServletResponse對象用于設(shè)置HTTP響應(yīng)。
String userName = req.getParameter("userName");
:從HTTP請求中獲取名為"userName"的參數(shù)值,并將其存儲在一個名為userName的字符串變量中。這里假設(shè)前端通過HTTP請求將用戶名作為參數(shù)傳遞給后端。
req.getSession().setAttribute("user",userName);
:使用HttpServletRequest的getSession()方法獲取當(dāng)前會話的HttpSession對象,并使用setAttribute()方法將用戶名存儲在名為"user"的屬性中。這樣,在整個會話期間,可以通過getAttribute()方法來訪問和使用該屬性。
resp.sendRedirect("chat.html");
:使用HttpServletResponse的sendRedirect()方法將響應(yīng)重定向到"chat.html"頁面。這意味著登錄成功后,用戶將被重定向到聊天頁面。
以上就是這段代碼的功能。它通過獲取用戶輸入的用戶名,并將其保存到會話中,實現(xiàn)了用戶登錄的功能。然后,通過重定向?qū)⒂脩魧?dǎo)航到聊天頁面。
2、新建一個 html 頁面實現(xiàn)登錄
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>用戶登錄</h1>
<form name="f1" method="post" action="login">
Name:<input type="text" name="userName"/>
<input type="submit" value="登錄">
</form>
</body>
</html>
讓我來逐行解釋這段代碼的功能:?
<form name="f1" method="post" action="login">
:這是一個表單(form標(biāo)簽),用于接收用戶輸入的登錄信息,并將其提交到名為"login"的URL路徑。
Name:<input type="text" name="userName"/>
:這是一個文本輸入框(input標(biāo)簽),用于用戶輸入用戶名。name屬性指定了該輸入框的名稱為"userName",以便后端能夠通過該名稱獲取用戶輸入的值。
<input type="submit" value="登錄">
:這是一個提交按鈕(input標(biāo)簽),用于提交表單數(shù)據(jù)。當(dāng)用戶點擊該按鈕時,表單中的數(shù)據(jù)將被發(fā)送到服務(wù)器進行處理。
三、握手連接
1、新建一個 消息對象 實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {/*** 發(fā)送人*/private String fromUser;/*** 發(fā)送時間*/private String sendTime;/*** 發(fā)送內(nèi)容*/private String content;}
讓我來逐行解釋這段代碼的功能:
@Data
:這是一個Lombok注解,自動生成getter、setter、equals、hashCode和toString方法等常見的代碼。
@AllArgsConstructor
:這是一個Lombok注解,自動生成一個包含所有屬性的構(gòu)造函數(shù)。
@NoArgsConstructor
:這是一個Lombok注解,自動生成一個無參構(gòu)造函數(shù)。
public class Message
:這是一個命名為Message的Java類。
private String fromUser;
:這是一個私有的字符串類型變量,表示消息的發(fā)送人。
private String sendTime;
:這是一個私有的字符串類型變量,表示消息的發(fā)送時間。
private String content;
:這是一個私有的字符串類型變量,表示消息的內(nèi)容。
以上就是這段代碼的功能。它定義了一個Message類,用于表示一條消息的數(shù)據(jù)結(jié)構(gòu)。該類包含了發(fā)送人、發(fā)送時間和發(fā)送內(nèi)容三個屬性,并使用Lombok注解簡化了相應(yīng)的代碼編寫工作。通過創(chuàng)建Message對象,可以方便地操作和傳遞消息的相關(guān)信息。
2、握手連接處理類
public class WebSocketHandshake extends Configurator {/*** 重寫握手處理方法* @param sec* @param request 請求對象* @param response 響應(yīng)對象*/@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {// 獲取 HttpSession 對象HttpSession httpSession = (HttpSession)request.getHttpSession();// 獲取用戶名String userName = (String) httpSession.getAttribute("user");// 將用戶名保存到當(dāng)前用戶連接 websocket 的 session 中sec.getUserProperties().put("user",userName);}}
讓我來逐行解釋這段代碼的功能:
public class WebSocketHandshake extends Configurator
:這是一個命名為WebSocketHandshake的Java類,繼承了Configurator類。
@Override
:這是一個注解,表示該方法重寫了父類或接口的方法。
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
:這是一個公共的無返回值方法,用于修改WebSocket握手過程中的處理邏輯。它接受三個參數(shù):
sec
:ServerEndpointConfig對象,用于配置WebSocket端點。request
:HandshakeRequest對象,表示握手請求。response
:HandshakeResponse對象,表示握手響應(yīng)。
HttpSession httpSession = (HttpSession)request.getHttpSession();
:獲取握手請求中的HttpSession對象,用于獲取用戶相關(guān)的會話信息。
String userName = (String) httpSession.getAttribute("user");
:從HttpSession對象中獲取名為"user"的屬性,即用戶名。
sec.getUserProperties().put("user",userName);
:將獲取到的用戶名保存到當(dāng)前用戶連接WebSocket的session中。通過sec.getUserProperties()
可以獲取到與當(dāng)前用戶連接相關(guān)的會話屬性,這里將用戶名保存在"user"屬性中。
以上就是這段代碼的功能。它通過重寫WebSocket握手處理方法,在握手過程中獲取并保存了用戶名,以便后續(xù)在WebSocket連接中使用。
1)為什么需要這個類握手處理類
這個類是用于在WebSocket握手過程中獲取并保存用戶名的。WebSocket是一種基于TCP協(xié)議的全雙工通信協(xié)議,它通過在客戶端和服務(wù)器之間建立持久連接,實現(xiàn)實時的雙向通信。
在實際應(yīng)用中,往往需要對連接WebSocket的用戶進行身份驗證和權(quán)限控制。而在WebSocket握手過程中,可以通過HTTP協(xié)議傳遞一些額外的信息,比如用戶的身份信息。為了在后續(xù)的WebSocket連接中能夠?qū)τ脩暨M行身份驗證和權(quán)限控制,我們需要將用戶相關(guān)的信息保存起來。
這個WebSocketHandshake類的作用就是在WebSocket握手過程中,獲取用戶的身份信息(這里是用戶名),并將其保存到當(dāng)前用戶連接的WebSocket會話中。通過在握手過程中獲取用戶名,并將其保存在WebSocket的session中,我們可以在后續(xù)的WebSocket連接中使用這個信息進行身份驗證和權(quán)限控制。
因此,這個類的存在是為了方便在WebSocket應(yīng)用中獲取和保存用戶信息,以便后續(xù)進行進一步的處理和控制。
3、服務(wù)端
@Slf4j
@ServerEndpoint(value = "/connect",configurator = WebSocketHandshake.class)
public class ChatServer {// 用戶列表,key 為用戶 id 或者是 name,// value 則是每一個客戶端的 Sessionprivate static Map<String,Session> users = new HashMap<>();@OnOpenpublic void onOpen(Session session){// 添加用戶到用戶列表String userName = (String) session.getUserProperties().get("user");// 添加到用戶列表中users.put(userName,session);}@OnMessagepublic void onMessage(String message,Session session) throws Exception {// 獲取發(fā)送人String formUser = (String) session.getUserProperties().get("user");// 創(chuàng)建發(fā)送時間String sendTime = new SimpleDateFormat("hh:mm").format(new Date());// 封裝消息對象并序列化為 JSONMessage msg = new Message(formUser,sendTime,message);String jsonMessage = new ObjectMapper().writeValueAsString(msg);log.info(jsonMessage);// 群發(fā)給所有人for (String userName : users.keySet()){Session s = users.get(userName);s.getBasicRemote().sendText(jsonMessage);}}@OnClosepublic void onClose(Session session){// 將用戶移除在線列表String userName = (String) session.getUserProperties().get("user");users.remove(userName);}}
這是一個基于Java實現(xiàn)的WebSocket聊天室后端代碼。
首先,在websocket包下,使用了@ServerEndpoint(value = "/connect")
注解聲明了一個WebSocket服務(wù)端,對應(yīng)的WebSocket地址為/connect
。
接下來,代碼中定義了一個靜態(tài)變量users
,用來存儲所有連接到該WebSocket服務(wù)端的用戶Session。在用戶連接WebSocket服務(wù)器時,通過@OnOpen
注解聲明的方法將用戶Session添加到用戶列表中。
在@OnMessage
注解聲明的方法中,當(dāng)WebSocket服務(wù)端接收到客戶端發(fā)送的消息時,先獲取發(fā)送人和發(fā)送時間,然后封裝成一個Message
對象并序列化成JSON格式。然后遍歷用戶列表,將消息發(fā)送給每一個客戶端。
最后,在@OnClose
注解聲明的方法中,當(dāng)一個用戶關(guān)閉連接時,將其從用戶列表中移除。
需要注意的是,在上述代碼中還使用了@ServerEndpoint
注解的configurator參數(shù),通過自定義WebSocketHandshake
類實現(xiàn)了WebSocket握手過程的一些特殊處理。具體可查看WebSocketHandshake
類的實現(xiàn)。
總的來說,這是一個簡單的WebSocket聊天室后端實現(xiàn),通過Java提供的WebSocket API完成了對WebSocket連接的管理和消息的廣播。
4、新建一個客戶端聊天頁面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="js/JQuery文件.txt.js"></script>
</head>
<body>
<h1>聊天室</h1>
<div id="msg"><input type="text" id="message"/><input type="button" value="發(fā)送"/><br></div><script>// 創(chuàng)建 WebSocket 對象var ws = new WebSocket("ws://localhost:8080/connect");// 接受服務(wù)端的信息ws.onmessage = function (event) {// 將消息填充到 div 中l(wèi)et data = event.data;// 將 json 字符串 轉(zhuǎn)換為 json 對象data = $.parseJSON(data);$('#msg').append(data.fromUser + " : " + data.sendTime + "<br>");$('#msg').append(data.content + "<br>");}$(function () {$(':button').on('click',function () {let msg = $('#message').val();// 發(fā)送消息ws.send(msg);// 發(fā)送完之后清空消息框$('#message').val('');})})</script></body>
</html>
這是一個簡單的前端實現(xiàn),用于創(chuàng)建一個基本的聊天室界面,并通過WebSocket協(xié)議與服務(wù)器進行通信。下面逐行詳細講解代碼的功能和實現(xiàn)。
<script src="js/JQuery文件.txt.js"></script>
:引入一個JavaScript文件,其中包含了JQuery庫的代碼。
<h1>聊天室</h1>
:在頁面中插入一個標(biāo)題,顯示為"聊天室"。
<div id="msg">
:定義一個div元素,用于顯示聊天消息。
<input type="text" id="message"/>
:創(chuàng)建一個文本輸入框,用戶可以在其中輸入消息。
<input type="button" value="發(fā)送"/><br>
:創(chuàng)建一個按鈕,用于發(fā)送消息。
<script>
:JavaScript代碼的開始標(biāo)簽。
var ws = new WebSocket("ws://localhost:8080/connect");
:創(chuàng)建一個WebSocket對象,連接到指定的服務(wù)器地址(這里是"ws://localhost:8080/connect")。
ws.onmessage
:WebSocket對象的onmessage事件,用于接收從服務(wù)器發(fā)送的消息。
let data = event.data;
:將接收到的消息內(nèi)容存儲在變量data中。
data = $.parseJSON(data);
:使用JQuery庫中的parseJSON函數(shù),將接收到的json格式消息轉(zhuǎn)換為json對象。
$('#msg').append(data.fromUser + " : " + data.sendTime + "<br>");
:將消息發(fā)送者和發(fā)送時間顯示在頁面上。
$('#msg').append(data.content + "<br>");
:將消息內(nèi)容顯示在頁面上。
$('button').on('click', function () {
:當(dāng)用戶點擊按鈕時觸發(fā)一個匿名函數(shù)。
let msg = $('#message').val();
:獲取文本輸入框中的消息內(nèi)容,并存儲在變量msg中。
ws.send(msg);
:通過WebSocket發(fā)送消息給服務(wù)器。
$('#message').val('');
:清空文本輸入框。
</script>
:JavaScript代碼的結(jié)束標(biāo)簽。
總體來說,這段代碼實現(xiàn)了一個簡單的聊天室界面,用戶可以在文本輸入框中輸入消息,點擊發(fā)送按鈕后,消息會通過WebSocket協(xié)議發(fā)送給服務(wù)器。同時,頁面會接收服務(wù)器返回的消息并將其顯示在頁面上。但需要注意的是,這只是前端部分的實現(xiàn),后端代碼和服務(wù)器的支持是實現(xiàn)完整聊天室功能的必要條件。
5、運行效果
本次案例就是通過實現(xiàn)用戶的登錄,然后把用戶名保存到作用域中,然后再從作用域中獲取用戶名實現(xiàn)的一個多人聊天案例。通過登錄去獲取到進入聊天室的人是誰。?
四、gitee 案例
地址:ch02 · qiuqiu/WebSocket-study - 碼云 - 開源中國 (gitee.com)