基于websocket的跨平臺通信——iPhone/iPad/Mac控制樹莓派(一):后端搭建
- 思路/介面說明
- 后端代碼參考了這位大佬博主的文章:
- 發送資料到后端的介面
- 后端發送資料格式
- 實作
- 工程創建等配置
- Config檔案
- WebSocketConfig.java
- FJsonConfig.java
- 引數類代碼
- 后端接收資訊引數類
- 后端發送資訊引數類
- WebSocket業務處理代碼
- 成品
- 部署
- 測驗
思路/介面說明
后端采用Springboot框架開發;
由于涉及到跨平臺、多種語言的開發,為了避免今后對后端頻繁的更改導致頻繁的部署,以及保證我的1元包年拉跨1核2G服務器不會動不動就跑滿CPU,后端我們就設計的簡單一些,資料處理統統交給終端和設備來做,
后端代碼參考了這位大佬博主的文章:
springboot+websocket構建在線聊天室(群聊+單聊)
發送資料到后端的介面
URL:/websocket/{device}
路徑引數device為連接服務器的設備名稱
引數:
{
"type": 1,
"toPlatform":["MacBook", "WM7"],
"msgType": "MasterControl",
"msg": "阿巴阿巴"
}
type:資料發送的模式,我預留的一個引數,無需理會,我固定設定為1;
toPlatform:一個String類的陣列,為該資訊需要發送到的設備名稱陣列;
msg:某種資料類轉換成的json字串;
msgType:msg原本類的名稱,接收到資料的設備用這個名稱通過工廠模式來決議msg,
后端發送資料格式
引數:
{
"fromPlatform": "Raspberry Pi",
"msgType": "MasterControl",
"msg": "阿巴阿巴"
}
fromPlatfrom:表示該資料由這個名稱的設備發送;
msgType與msg同上,
實作
工程創建等配置
我使用IDEA進行開發,
選擇Spring Initializr工程;

依賴根據自己的需求選取,不過一定要選上WebSocket;

我采用application.properties進行配置;

server.port、application.name、url、password等引數根據需要修改;
這里我選擇了Mysql Server的依賴,必須要配置一個資料庫連接,否則無法部署;也便于之后擴展資料庫相關功能,
pom.xml匯入FastJson:(可以換成別的JSON決議庫)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
Config檔案
由于我采用了websocket依賴以及FastJson庫(可以換成別的JSON決議庫),而這兩個庫都有比較蛋疼的bug,需要配置兩個config檔案:
WebSocketConfig.java
// package com.wmiii.wmsocket.config; 改成自己的package路徑
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
FJsonConfig.java
// package com.wmiii.wmsocket.config; 改成自己的package路徑
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class FJsonConfig {
@Bean
public HttpMessageConverter configureMessageConverters() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
// 保留map空的欄位
SerializerFeature.WriteMapNullValue,
// 將String型別的null轉成""
SerializerFeature.WriteNullStringAsEmpty,
// 將Number型別的null轉成0
SerializerFeature.WriteNullNumberAsZero,
// 將List型別的null轉成[]
SerializerFeature.WriteNullListAsEmpty,
// 將Boolean型別的null轉成false
SerializerFeature.WriteNullBooleanAsFalse,
// 避免回圈參考
SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
List<MediaType> mediaTypeList = new ArrayList<>();
// 解決中文亂碼問題,相當于在Controller上的@RequestMapping中加了個屬性produces = "application/json"
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);
return converter;
}
}
引數類代碼
(類名我亂取的
后端接收資訊引數類
BaseMsg.java
// package com.wmiii.wmsocket.msg; 改成自己package的路徑
import lombok.Data; // 記得匯入lombok依賴
import java.util.ArrayList;
@Data
public class BaseMsg {
Integer type; // 1為指定發送物件,其余暫定為廣播test
ArrayList<String> toPlatform;
String msgType;
String msg; // json格式的msg,后端無需關心具體內容
}
后端發送資訊引數類
ToMsgParam.java
// package com.wmiii.wmsocket.param; 改成自己package的路徑
import lombok.Data;
@Data
public class ToMsgParam {
String fromPlatform;
String msgType;
String msg; // JSON格式的資料
}
WebSocket業務處理代碼
// 省略import
@ServerEndpoint(value = "/websocket/{device}")
@Component
public class WmWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("device") String device) {
}
/**
* 連接關閉呼叫的方法
*/
@OnClose
public void onClose() {
}
/**
* 收到客戶端訊息后呼叫的方法
*
* @param message 客戶端發送過來的訊息*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("device") String device) {
}
/**
* 發生錯誤時呼叫
*te
*/
@OnError
public void onError(Session session, Throwable error) {
}
}
對于一個擁有 @ServerEndpoint 注解的類,它就會被當做處理對應url的websocket業務的組件;其中需要實作四個注解的方法:
擁有 @OnOpen 注解的方法:在創建了一個新的websocket連接時呼叫;
擁有 @OnClose 注解的方法:websocket連接斷開時呼叫;
擁有 @OnMessage 注解的方法:收到訊息時呼叫;
擁有 @OnError 注解的方法:發生錯誤時呼叫;
成品
(看注釋就好懶得另外打字了
// 省略import
@ServerEndpoint(value = "/websocket/{device}")
@Component
public class WmWebSocket {
//用來存放每個客戶端對應的WmWebSocket物件,
private static CopyOnWriteArraySet<WmWebSocket> webSocketSet = new CopyOnWriteArraySet<WmWebSocket>();
//與某個客戶端的連接會話,需要通過它來給客戶端發送資料
private Session session;
private String device;
//用來記錄平臺名稱和該session進行系結
private static Map<String,Session> deviceMap = new HashMap<String, Session>();
@OnOpen
public void onOpen(Session session, @PathParam("device") String device) {
this.session = session;
this.device = device;
deviceMap.put(device, session);
webSocketSet.add(this); // 加入set中
System.out.println("設備" + device +"加入, 當前設備數為" + webSocketSet.size());
this.session.getAsyncRemote().sendText(device+"成功連接上WebSocket(sessionId: "+session.getId()+")-->當前在線設備數: "+webSocketSet.size());
}
/**
* 連接關閉呼叫的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); // 從set中洗掉
deviceMap.remove(device);
System.out.println("設備" + this.device +"連接關閉!當前在線設備數: " + webSocketSet.size());
}
/**
* 收到客戶端訊息后呼叫的方法
*
* @param message 客戶端發送過來的訊息*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("device") String device) {
System.out.println(device + ": " + message);
BaseMsg baseMsg;
try {
baseMsg = JSON.parseObject(message, BaseMsg.class);
switch (baseMsg.getType()) {
case 1:
ToMsgParam toMsgParam = new ToMsgParam();
toMsgParam.setFromPlatform(device);
toMsgParam.setMsgType(baseMsg.getMsgType());
toMsgParam.setMsg(baseMsg.getMsg());
String toMsg = JSON.toJSONString(toMsgParam);
Session fromSession = deviceMap.get(device);
Session toSession;
// 獲取資料目標設備串列
ArrayList<String> toList = baseMsg.getToPlatform();
// 用來存盤資料發送失敗的目標設備,暫時沒用;
ArrayList<String> failed = new ArrayList<>();
// 逐個查詢session的map進行資料發送
for(String toPlatform: toList) {
toSession = deviceMap.get(toPlatform);
try {
toSession.getAsyncRemote().sendText(toMsg);
} catch (Exception e) {
// 如果該目標平臺的資料發送失敗,則加入發送失敗串列,暫時沒用;
failed.add(toPlatform);
}
}
break;
default:
System.out.println("default");
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 發生錯誤時呼叫
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("發生錯誤");
error.printStackTrace();
}
}
部署
使用寶塔面板部署,jar包部署,
選擇IDEA右側的Maven,運行Lifecycle下的package:

然后在專案根目錄下的target檔案夾中找到生成的一個.jar檔案,上傳到云服務器;

記得在服務器上開放設定的專案埠(我的是8880),
在上傳jar包的路徑下運行:
nohup java -jar xxx.jar &
這里的xxx是你的jar包名稱,就部署完畢了,
測驗
最近(指寫下這篇文章的時候)postman更新了WebSocket介面的測驗;在Workspace右邊點擊New選擇WebSocket Request就可以了,

(終于不用自寫HTML測驗了)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/345701.html
標籤:其他
上一篇:HCIA系列課程
