注意:學習本文章一定要打開自己的開發工具,代碼中有詳細的解釋,最后祝大家技術突飛猛進,早日拿到心儀的offer,
WebSocket -- 從入門到精通
- 基礎講解
- Spring Boot 服務端
- Spring Boot 客戶端
- HTML 客戶端
- 基礎示例代碼
- Spring Boot服務端
- maven引入依賴
- 直接copy原始碼
- Spring Boot 客戶端
- maven引入依賴
- 創建一個類,繼承WebSocketClient
- 添加一個帶參構造
- 重寫下面四個方法
- 直接copy原始碼
- 再自定義一個類,創建一個static靜態方法回傳一個WebSocketClient物件
- 直接copy原始碼
- 撰寫一個Collections類,向服務端發送訊息
- HTML 客戶端
- 直接copy原始碼
- 進階示例代碼
- Spring Boot服務端
- 撰寫服務端代碼
- 向客戶端推送請求
- 所用物體類
- 引入依賴
- 第一次請求物體類
- 第二次請求物體類
- HTML客戶端
- 超級進階示例代碼
- Spring Boot服務端代碼
- 改動之處
- maven引入依賴
- 開啟服務端代碼
- 向客戶端推送請求
- H5 客戶端代碼
- 彩蛋
基礎講解
Spring Boot 服務端
這里我們需要先介紹幾個注解
| 注解名稱 | 說明 |
|---|---|
| @ServerEndpoint | 指定的客戶端請求的URI |
| @OnOpen | 建立連接成功呼叫 |
| @OnClose | 關閉連接時呼叫 |
| @OnMessage | 收到客戶端訊息后呼叫的方法 |
| @OnError | 發生錯誤觸發 |
Spring Boot 客戶端
監聽事件與服務器端一樣,不在陳述,
創建步驟:
- 自定義一個類繼承WebSocketClient
- 宣告一個帶參的構造方法(引數為ServerEndpoint指定的URI)
例如:ws://localhost:8080/websocketdemo- 將引數通過super(serverUri)傳給父類的構造方法;
- 請看下方詳細代碼
HTML 客戶端
//創建WebSocket物件
var websocket = new WebSocket("ws://localhost:8080/websocketdemo");
四大監聽引數
| 事件名稱 | 呼叫方式 | 觸發監聽事件 |
|---|---|---|
| open | websocket.onopen | 連接建立時觸發 |
| message | websocket.onmessage | 客戶端接收服務端資料時觸發 |
| error | websocket.onerror | 通信發生錯誤時觸發 |
| close | websocket.onclose | 連接關閉時觸發 |
請求方法
| 方法 | 說明 |
|---|---|
| websocket.send() | 像服務端發送資料 |
| websocket.close() | 關閉連接 |
基礎示例代碼
Spring Boot服務端
maven引入依賴
<!--WebSocket核心依賴包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.4.1</version>
<scope>compile</scope>
</dependency>
這里主要就是四大監聽引數,以及設定URI地址,
直接copy原始碼
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
//這里的服務地址為:ws://localhost:8080/websocketdemo
@ServerEndpoint("/websocketdemo")
@Component
public class MyDemoServer {
/**
* 百度搜索:程式員小哲
* 建立連接成功呼叫
* @param session
*/
@OnOpen
public void onOpen(Session session) {
System.out.println("連接成功!");
}
/**
* 百度搜索:程式員小哲
* 關閉連接時呼叫
* @param session
*/
@OnClose
public void onClose(Session session) {
System.out.println("關閉連接!");
}
/**
* 百度搜索:程式員小哲
* 收到客戶端訊息后呼叫的方法
* @param message 客戶端發送過來的訊息
*/
@OnMessage
public void onMessage(Session session,String message) {
System.out.println("接收訊息:"+message);
}
/**
* 百度搜索:程式員小哲
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
System.out.println("連接例外:{}"+throwable);
}
}
Spring Boot 客戶端
稍微復雜,可以使用HTML客戶端
maven引入依賴
<!--WebSocket核心依賴包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.4.1</version>
<scope>compile</scope>
</dependency>
創建一個類,繼承WebSocketClient
添加一個帶參構造

重寫下面四個方法

直接copy原始碼
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
public class MyWebSocketClient extends WebSocketClient {
public MyWebSocketClient(URI serverUri) {
super(serverUri);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println("連接成功!");
}
@Override
public void onMessage(String s) {
System.out.println("接收到服務端資料:"+s);
}
@Override
public void onClose(int i, String s, boolean b) {
System.out.println("關閉連接"+s);
}
@Override
public void onError(Exception e) {
System.out.println("發生例外"+e);
}
}
再自定義一個類,創建一個static靜態方法回傳一個WebSocketClient物件
直接copy原始碼
這里就是創建了連接服務器的方法,
import com.xiaozhe.websocket.MyWebSocketClient;
import org.java_websocket.client.WebSocketClient;
import java.net.URI;
import java.net.URISyntaxException;
public class WebSocketDemo {
public static WebSocketClient webSocketClient() {
try {
MyWebSocketClient webSocketClient = new MyWebSocketClient(new URI("ws://localhost:8080/websocketdemo"));
webSocketClient.connect();
return webSocketClient;
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
}
撰寫一個Collections類,向服務端發送訊息
我們可以使用瀏覽器url方式訪問,也可以通過postman等測驗工具來進行訪問,
url : localhost:8080/sendfirst
import com.xiaozhe.bo.FirstClient;
import org.java_websocket.client.WebSocketClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WebSocketController {
WebSocketClient webSocketClient = WebSocketDemo.webSocketClient();
@RequestMapping("sendfirst")
public void sendfirst() {
webSocketClient.send("百度搜索:程式員小哲");
}
}
HTML 客戶端
直接copy原始碼
簡單的h5客戶端代碼,可以創建連接,斷開連接,向服務端發送資料,
<!DOCTYPE HTML>
<html>
<head>
<title>WebSocketDemo</title>
</head>
<body>
<button onclick="oppen()">創建鏈接</button>
<button onclick="closeWebSocket()">斷開連接</button>
<hr>
<input id="text" type="text" />
<button onclick="send()">向服務器發送資料</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//創建連接
function oppen(){
//判斷當前瀏覽器是否支持WebSocket
if('WebSocket' in window){
//連接WebSocket節點
websocket = new WebSocket("ws://localhost:8080/websocketdemo");
}
else{
alert('當前瀏覽器不支持websocket');
}
//連接發生錯誤的回呼方法
websocket.onerror = function(){
setMessageInnerHTML("錯誤!");
};
//連接成功建立的回呼方法
websocket.onopen = function(event){
setMessageInnerHTML("連接成功!");
}
//接收到訊息的回呼方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//連接關閉的回呼方法
websocket.onclose = function(){
setMessageInnerHTML("連接關閉!");
}
//監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉視窗,server端會拋例外,
window.onbeforeunload = function(){
websocket.close();
}
//將訊息顯示在網頁上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
}
//關閉連接
function closeWebSocket(){
websocket.close();
}
//發送訊息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
進階示例代碼
之前的例子是實作單個客戶端向服務端發送資訊,但是如果服務端向指定的客戶端發訊息應該怎么做呢,那么請看下面的代碼:
建議大家將代碼復制到IDE中,便于查看,代碼中有詳細注釋便于理解,
Spring Boot服務端
撰寫服務端代碼
這里我們在主要的四大監聽方法之外加了一些屬性和方法,大家可以新建一個Spring boot專案來進行下面的專案,
import com.alibaba.fastjson.JSONObject;
import com.example.demo.zo.Xiao;
import com.example.demo.zo.Zhe;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/websocketdemoxiaozhe")
@Component
public class MyServer {
//將所有客戶端物件儲存到Map集合中
private static Map<String, MyServer> clients = new ConcurrentHashMap<String, MyServer>();
//會話
private Session session;
//會話唯一標識,使用cid來確定唯一的
private String cid;
/**
* 百度搜索:程式員小哲
* 建立連接成功呼叫
* @param session
*/
@OnOpen
public void onOpen(Session session) {
System.out.println("連接成功!");
}
/**
* 百度搜索:程式員小哲
* 關閉連接時呼叫
* @param session
*/
@OnClose
public void onClose(Session session) {
System.out.println("關閉連接!");
}
/**
* 百度搜索:程式員小哲
* 收到客戶端訊息后呼叫的方法
* @param message 客戶端發送過來的訊息
*/
@OnMessage
public void onMessage(Session session,String message) {
// message是一個json
// 將message進行反序列化
// 詳見我的另一篇博客:https://blog.csdn.net/xiaozhezhe0470/article/details/110652012
JSONObject jsonObject = JSONObject.parseObject(message);
Zhe zhe = JSONObject.toJavaObject(jsonObject, Zhe.class);
//由于我們是在創建之河駁交cid,所以我們需要在獲取提交的資訊的時候進行判斷,查看是哪一次的請求
if (zhe.getHi() != null){//第二次之后的請求
System.out.println(zhe.getHi());
}else {//第一次請求
Xiao xiao = JSONObject.toJavaObject(jsonObject, Xiao.class);
System.out.println(xiao);
this.cid = xiao.getCid();
this.session = session;
clients.put(cid,this);
try {
//將資訊回傳到指定用戶的頁面中
sendMessageTo("恭喜"+xiao.getWork()+xiao.getCname()+"注冊成功!您的賬號為:"+cid,cid);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 百度搜索:程式員小哲
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
System.out.println("連接例外:{}"+throwable);
}
/**
* 百度搜索:程式員小哲
* 向指定的客戶端發送資料
* @param message 推送的資訊
* @param cid 客戶id
*/
public void sendMessageTo(String message, String cid) throws IOException {
MyServer myWebSocket = clients.get(cid);
myWebSocket.session.getAsyncRemote().sendText(message);
}
/**
* 百度搜索:程式員小哲
* 給所有用戶進行推送資訊
* @param message 推送的資訊
*/
public void sendMessageAll(String message) throws IOException {
for (MyServer item : clients.values()) {
item.session.getAsyncRemote().sendText(message);
}
}
}
向客戶端推送請求
在這里我們新加了一個基礎代碼里面沒有的類,就是給指定的用戶推送訊息,
import com.example.demo.config.MyServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
public class ServiceToClientDemo {
@Autowired
public MyServer myServer;
/**
* 給指定的用戶推送資訊
* @param cid 用戶注冊后彈出的cid
* @param message 想要推送給用戶的資訊
*/
@GetMapping("/sendToClient")
public void sendTo(String cid,String message) throws IOException {
myServer.sendMessageTo(message,cid);
}
/**
* 給所有用戶推送資訊
* @param msg 想要推送給用戶的資訊
*/
@GetMapping("/sendAllClient")
public void sendAll(String msg) throws IOException {
myServer.sendMessageAll(msg);
}
}
所用物體類
引入依賴
使用物體類之前需要引入@Data的依賴
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
第一次請求物體類
import lombok.Data;
@Data
public class Xiao {
private String cid;
private String work;
private String cname;
}
第二次請求物體類
import lombok.Data;
@Data
public class Zhe {
private String hi;
}
HTML客戶端
與基礎代碼相比,多了一個登陸認證,這里需要注意的是,我們需要先創建連接,然后登陸認證,最后在向服務端發送資料,這里沒有加驗證,不按照順序來會報錯的,
再有一個就是我在兩個文本框里面加了默認的JSON大家直接點擊即可,方便大家操作,
<!DOCTYPE HTML>
<html>
<head>
<title>Test My WebSocket</title>
</head>
<body>
<button onclick="oppen()">創建連接</button>
<button onclick="closeWebSocket()">斷開連接</button>
<hr>
<input id="text01" type="text" value="{ 'cid': '713', 'cname': '小哲', 'work': '程式員' }"/>
<button onclick="first()">登陸認證</button>
<hr>
<input id="text" type="text" value="{ 'hi': '你好呀' }" />
<button onclick="send()">向服務器發送資料</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//創建連接
function oppen(){
//判斷當前瀏覽器是否支持WebSocket
if('WebSocket' in window){
//var message01 = document.getElementById('text01').value;
//連接WebSocket節點
websocket = new WebSocket("ws://localhost:8080/websocketdemoxiaozhe");
}
else{
alert('Not support websocket')
}
//連接發生錯誤的回呼方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//連接成功建立的回呼方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
//接收到訊息的回呼方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//連接關閉的回呼方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉視窗,server端會拋例外,
window.onbeforeunload = function(){
websocket.close();
}
//將訊息顯示在網頁上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
}
//關閉連接
function closeWebSocket(){
websocket.close();
}
//登陸賬號
function first(){
var message01 = document.getElementById('text01').value;
websocket.send(message01);
}
//發送訊息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
超級進階示例代碼
大家有沒有覺得每次創建連接在進行注冊非常的麻煩,有沒有一種辦法可以直接在連接的時候就進行注冊呢?那么請看下面的代碼,
Spring Boot服務端代碼
改動之處
一、 @ServerEndpoint("/websocketdemoxiaozhe01/{cid}")
在這里我們在后面加上了一個引數,就是我們的唯一標識cid,
二、 public void onOpen(@PathParam(“cid”) String cid, Session session) {…}
我們在onOpen方法里面多加了一個引數,注意第一個引數一定要用@PathParam修飾,不然專案跑不起來
然后就沒有什么不一樣了,直接上代碼copy到開發工具上,
maven引入依賴
<!--WebSocket核心依賴包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.4.1</version>
<scope>compile</scope>
</dependency>
開啟服務端代碼
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.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/websocketdemoxiaozhe01/{cid}")
@Component
public class MyServerDemo {
//將當前物件儲存到Map集合中
private static Map<String, MyServerDemo> clients = new ConcurrentHashMap<String, MyServerDemo>();
//會話
private Session session;
//會話唯一標識
private String cid;
/**
* 百度搜索:程式員小哲
* 建立連接成功呼叫
* @param session
*/
@OnOpen
public void onOpen(@PathParam("cid") String cid, Session session) {
System.out.println("連接成功!");
this.cid = cid;
this.session = session;
clients.put(cid,this);
try {
//將資訊回傳到指定用戶的頁面中
sendMessageTo("恭喜注冊成功!您的賬號為:"+cid,cid);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 百度搜索:程式員小哲
* 關閉連接時呼叫
* @param session
*/
@OnClose
public void onClose(Session session) {
System.out.println("關閉連接!");
}
/**
* 百度搜索:程式員小哲
* 收到客戶端訊息后呼叫的方法
* @param message 客戶端發送過來的訊息
*/
@OnMessage
public void onMessage(Session session,String message) {
System.out.println(message);
}
/**
* 百度搜索:程式員小哲
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
System.out.println("連接例外:{}"+throwable);
}
/**
* 百度搜索:程式員小哲
* 向指定的客戶端發送資料
* @param message 推送的資訊
* @param cid 客戶id
*/
public void sendMessageTo(String message, String cid) throws IOException {
MyServerDemo myWebSocket = clients.get(cid);
myWebSocket.session.getAsyncRemote().sendText(message);
}
/**
* 百度搜索:程式員小哲
* 給所有用戶進行推送資訊
* @param message 推送的資訊
*/
public void sendMessageAll(String message) throws IOException {
for (MyServerDemo item : clients.values()) {
item.session.getAsyncRemote().sendText(message);
}
}
}
向客戶端推送請求
示例url:http://localhost:8080/sendToClient01?cid=111&message=你好
示例url:http://localhost:8080/sendAllClient01?message=你們好
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
public class ServiceToClientDemoDemo {
@Autowired
public MyServerDemo myServerDemo;
/**
* 給指定的用戶推送資訊
* @param cid 用戶注冊后彈出的cid
* @param message 想要推送給用戶的資訊
*/
@GetMapping("/sendToClient01")
public void sendTo(String cid,String message) throws IOException {
myServerDemo.sendMessageTo(message,cid);
}
/**
* 給所有用戶推送資訊
* @param msg 想要推送給用戶的資訊
*/
@GetMapping("/sendAllClient01")
public void sendAll(String msg) throws IOException {
myServerDemo.sendMessageAll(msg);
}
}
H5 客戶端代碼
H5客戶端唯一的區別就是在創建連接的時候將cid一起傳到了后臺,
websocket = new WebSocket(“ws://localhost:8080/websocketdemoxiaozhe01/” + message01);
<!DOCTYPE HTML>
<html>
<head>
<title>Test My WebSocket</title>
</head>
<body>
websocketid
<input id="text01" type="text" value="111"/>
<button onclick="oppen()">創建鏈接</button>
<hr>
<input id="text" type="text" />
<button onclick="send()">向服務器發送資料</button>
<button onclick="closeWebSocket()">斷開連接</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//創建連接
function oppen(){
//判斷當前瀏覽器是否支持WebSocket
if('WebSocket' in window){
var message01 = document.getElementById('text01').value;
//連接WebSocket節點
websocket = new WebSocket("ws://localhost:8080/websocketdemoxiaozhe01/" + message01);
}
else{
alert('Not support websocket')
}
//連接發生錯誤的回呼方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//連接成功建立的回呼方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
//接收到訊息的回呼方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//連接關閉的回呼方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉視窗,server端會拋例外,
window.onbeforeunload = function(){
websocket.close();
}
//將訊息顯示在網頁上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
}
//關閉連接
function closeWebSocket(){
websocket.close();
}
//發送訊息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
彩蛋
- 有問題可以掃碼評論,我會一一進行回復,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230984.html
標籤:java
