中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

websocket與下位機如何通過netty方式通信傳輸行為信息

發布時間:2021-11-11 10:00:51 來源:億速云 閱讀:248 作者:柒染 欄目:編程語言

這篇文章給大家介紹websocket與下位機如何通過netty方式通信傳輸行為信息,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

前言介紹

在物聯網開發中,常常需要通過網頁端來控制設備,包括;獲取信息、執行操作、啟動停止等,就像我們在手機上會控制家里的小米盒子、路由器、電飯煲或者在線養狗等一些設備一樣。在這里所有的下層設備都可以通過socket通信鏈接到服務端,而用戶一端在通過http鏈接或者websocket鏈接到服務端,通過發送和接收數據來做出相應的行為操作。如下圖;

websocket與下位機如何通過netty方式通信傳輸行為信息

微信公眾號:bugstack蟲洞棧 & 執行流程  
 

案例目標

  1. 本章節整合Springboot+Netty,通過部署nettySocket與webSocket兩套服務端,來接收轉發行為消息。

  2. 客戶端采用js鏈接websocket,用于接收服務端反饋與發送指令,用于獲取下位機信息。

  3. 在test中啟動一個模擬下位機,用于反饋信息數據。在真實開發中下位機與服務端通信通常是定義好的字節碼,需要自己編寫解碼器。

環境準備

  1. jdk 1.8.0

  2. IntelliJ IDEA Community Edition 2018.3.1 x64

  3. Netty 4.1.36.Final

代碼示例

 1itstack-demo-netty-3-01
2└── src
3    ├── main
4    │   ├── java
5    │   │   └── org.itstack.demo.ark
6    │   │       ├── domain
7    │   │       │    ├── msgobj
8    │   │       │    │   ├── Feedback.java
9    │   │       │    │   ├── QueryInfoReq.java
10    │   │       │    │   └── Text.java
11    │   │       │    ├── Device.java
12    │   │       │    ├── EasyResult.java 
13    │   │       │    ├── InfoProtocol.java
14    │   │       │    └── ServerInfo.java 
15    │   │       ├── server
16    │   │       │    ├── socket
17    │   │       │    │   ├── MyChannelInitializer.java
18    │   │       │    │   ├── MyServerHandler.java
19    │   │       │    │   └── NettyServer.java    
20    │   │       │    └── websocket
21    │   │       │        ├── MyWsChannelInitializer.java
22    │   │       │        ├── MyWsServerHandler.java
23    │   │       │        └── WsNettyServer.java
24    │   │       ├── util
25    │   │       │    ├── CacheUtil.java
26    │   │       │    ├── MsgUtil.java
27    │   │       │    └── MsgUtil.java
28    │   │       ├── web
29    │   │       │    └── NettyController.java    
30    │   │       └── Application.java
31    │   └── resources    
32    │   │   └── application.yml
33    │   └── webapp
34    │       ├── arkWs
35    │       │    ├── js
36    │       │    │   └── ws.js   
37    │       │   └── arkWsControlCenter.html    
38    │       ├── res        
39    │       └── WEB-INF
40    │            └── index.jsp   
41    │
42    └── test
43        └── java
44            └── org.itstack.demo.test
45                └── ApiTest.java

演示部分重點代碼塊,完整代碼下載關注公眾號;bugstack蟲洞棧,回復Netty案例

server/socket/MyServerHandler.java & socket數據處理

  • 當有下位機鏈接服務端時,構建下位機信息,實際使用可以通過注冊方式進行鏈接驗證。

  • 當收到下位機信息后轉發到websocket端,使網頁端收到下位機信息反饋

 1public class MyServerHandler extends ChannelInboundHandlerAdapter {
2
3    private Logger logger = LoggerFactory.getLogger(MyServerHandler.class);
4
5    /**
6     * 當客戶端主動鏈接服務端的鏈接后,這個通道就是活躍的了。也就是客戶端與服務端建立了通信通道并且可以傳輸數據
7     */
8    @Override
9    public void channelActive(ChannelHandlerContext ctx) throws Exception {
10        SocketChannel channel = (SocketChannel) ctx.channel();
11        String channelId = channel.id().toString();
12        System.out.println("鏈接報告開始");
13        System.out.println("鏈接報告信息:有一客戶端鏈接到本服務端。channelId:" + channelId);
14        System.out.println("鏈接報告IP:" + channel.localAddress().getHostString());
15        System.out.println("鏈接報告Port:" + channel.localAddress().getPort());
16        System.out.println("鏈接報告完畢");
17
18        //構建設備信息{下位機、中繼器、IO板卡}
19        Device device = new Device();
20        device.setChannelId(channelId);
21        device.setNumber(UUID.randomUUID().toString());
22        device.setIp(channel.remoteAddress().getHostString());
23        device.setPort(channel.remoteAddress().getPort());
24        device.setConnectTime(new Date());
25        //添加設備信息
26        deviceGroup.put(channelId, device);
27        CacheUtil.cacheClientChannel.put(channelId, channel);
28    }
29
30
31    @Override
32    public void channelRead(ChannelHandlerContext ctx, Object objMsgJsonStr) throws Exception {
33        //接收設備發來信息
34        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息內容:" + objMsgJsonStr);
35
36        //轉發消息到Ws
37        CacheUtil.wsChannelGroup.writeAndFlush(new TextWebSocketFrame(objMsgJsonStr.toString()));
38    }
39
40}

server/websocket/MyWsServerHandler.java & websocket數據處理

  • websocket數據需要轉換后使用,可以支持文本消息,本案例中使用json字符串,方便對象傳輸

  • channelRead轉發數據,當收到數據后發送給下位機,主要通過內容中channel控制

 1public class MyWsServerHandler extends ChannelInboundHandlerAdapter {
2
3
4    @Override
5    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
6
7       ...
8
9        //ws
10        if (msg instanceof WebSocketFrame) {
11            WebSocketFrame webSocketFrame = (WebSocketFrame) msg;
12            //關閉請求
13            if (webSocketFrame instanceof CloseWebSocketFrame) {
14                handshaker.close(ctx.channel(), (CloseWebSocketFrame) webSocketFrame.retain());
15                return;
16            }
17            //ping請求
18            if (webSocketFrame instanceof PingWebSocketFrame) {
19                ctx.channel().write(new PongWebSocketFrame(webSocketFrame.content().retain()));
20                return;
21            }
22            //只支持文本格式,不支持二進制消息
23            if (!(webSocketFrame instanceof TextWebSocketFrame)) {
24                throw new Exception("僅支持文本格式");
25            }
26
27            String request = ((TextWebSocketFrame) webSocketFrame).text();
28            System.out.println("服務端收到:" + request);
29            InfoProtocol infoProtocol = JSON.parseObject(request, InfoProtocol.class);
30            //socket轉發消息
31            String channelId = infoProtocol.getChannelId();
32            Channel channel = CacheUtil.cacheClientChannel.get(channelId);
33            if (null == channel) return;
34            channel.writeAndFlush(MsgUtil.buildMsg(infoProtocol));
35
36            //websocket消息反饋發送成功
37            ctx.writeAndFlush(MsgUtil.buildWsMsgText(ctx.channel().id().toString(), "向下位機傳達消息success!"));
38        }
39
40    }
41
42}

web/NettyController.java & 控制層方便獲取服務端信息

  • 控制層提供了查詢服務列表、鏈接設備信息、以及主動觸發信息發送

  • 另外如果需要將服務端的啟動關閉進行手動控制,可以在這里面提供方法供調用

 1@Controller
2public class NettyController {
3
4    private Logger logger = LoggerFactory.getLogger(NettyController.class);
5
6    @RequestMapping("/index")
7    public String index() {
8        return "index";
9    }
10
11    @RequestMapping("/queryNettyServerList")
12    @ResponseBody
13    public Collection<ServerInfo> queryNettyServerList() {
14        try {
15            Collection<ServerInfo> serverInfos = CacheUtil.serverInfoMap.values();
16            logger.info("查詢服務端列表。{}", JSON.toJSONString(serverInfos));
17            return serverInfos;
18        } catch (Exception e) {
19            logger.info("查詢服務端列表失敗。", e);
20            return null;
21        }
22    }
23
24    @RequestMapping("/queryDeviceList")
25    @ResponseBody
26    public Collection<Device> queryDeviceList() {
27        try {
28            Collection<Device> deviceInfos = CacheUtil.deviceGroup.values();
29            logger.info("查詢設備鏈接列表。{}", JSON.toJSONString(deviceInfos));
30            return deviceInfos;
31        } catch (Exception e) {
32            logger.info("查詢設備鏈接列表失敗。", e);
33            return null;
34        }
35    }
36
37    @RequestMapping("/doSendMsg")
38    @ResponseBody
39    public EasyResult doSendMsg(String reqStr) {
40        try {
41            logger.info("向設備發送信息[可以通過另外一個Web服務調用本接口發送信息]:{}", reqStr);
42            InfoProtocol infoProtocol = MsgUtil.getMsg(reqStr);
43            String channelId = infoProtocol.getChannelId();
44            Channel channel = CacheUtil.cacheClientChannel.get(channelId);
45            channel.writeAndFlush(MsgUtil.buildMsg(infoProtocol));
46            return EasyResult.buildSuccessResult();
47        } catch (Exception e) {
48            logger.error("向設備發送信息失敗:{}", reqStr, e);
49            return EasyResult.buildErrResult(e);
50        }
51    }
52
53}

Application.java & 啟動層

  • 通過繼承CommandLineRunner方法,在服務就緒后啟動socket服務

  • 啟動后需要循環驗證是否啟動完成

 1@SpringBootApplication
2@ComponentScan("org.itstack.demo.ark")
3public class Application implements CommandLineRunner {
4
5    private Logger logger = LoggerFactory.getLogger(Application.class);
6
7    @Value("${netty.socket.port}")
8    private int nettyServerPort;
9    @Value("${netty.websocket.port}")
10    private int nettyWsServerPort;
11    //默認線程池
12    private static ExecutorService executorService = Executors.newFixedThreadPool(2);
13
14    public static void main(String[] args) {
15        SpringApplication.run(Application.class, args);
16    }
17
18    @Override
19    public void run(String... args) throws Exception {
20        //啟動NettyServer-socket
21        logger.info("啟動NettyServer服務,啟動端口:{}", nettyServerPort);
22        NettyServer nettyServer = new NettyServer(new InetSocketAddress(nettyServerPort));
23        Future<Channel> future = executorService.submit(nettyServer);
24        Channel channel = future.get();
25        if (null == channel) {
26            throw new RuntimeException("netty server-s open error channel is null");
27        }
28        while (!channel.isActive()) {
29            logger.info("啟動NettyServer服務,循環等待啟動...");
30            Thread.sleep(500);
31        }
32        logger.info("啟動NettyServer服務,完成:{}", channel.localAddress());
33        CacheUtil.serverInfoMap.put(nettyServerPort, new ServerInfo("NettySocket", NetUtil.getHost(), nettyServerPort, new Date()));
34
35        //啟動NettyServer-websocket
36        logger.info("啟動NettyWsServer服務,啟動端口:{}", nettyWsServerPort);
37        WsNettyServer wsNettyServer = new WsNettyServer(new InetSocketAddress(nettyWsServerPort));
38        Future<Channel> wsFuture = executorService.submit(wsNettyServer);
39        Channel wsChannel = wsFuture.get();
40        if (null == wsChannel) {
41            throw new RuntimeException("netty server-ws open error channel is null");
42        }
43        while (!wsChannel.isActive()) {
44            logger.info("啟動NettyWsServer服務,循環等待啟動...");
45            Thread.sleep(500);
46        }
47        logger.info("啟動NettyWsServer服務,完成:{}", wsChannel.localAddress());
48        CacheUtil.serverInfoMap.put(nettyServerPort, new ServerInfo("NettyWsSocket", NetUtil.getHost(), nettyServerPort, new Date()));
49    }
50
51}

webapp/arkWs/js/ws.js & 鏈接websocket服務端

 1socket = new WebSocket("ws://localhost:7398/websocket");
2
3    socket.onmessage = function(event){
4
5        var msg = JSON.parse(event.data);
6        console.info(msg);
7
8        $("#msgBox").val($("#msgBox").val() + event.data + "\r\n");
9
10    };

案例測試

  1. 分別啟動如下內容;

  2. Application.java,Plugins/spring-boot/spring-boot:run

  3. ApiTest.java,右鍵啟動模擬下位機

  4. 打開服務端鏈接;http://localhost:8080/ http://localhost:8080/arkWs/arkWsControlCenter.html



    websocket與下位機如何通過netty方式通信傳輸行為信息

    微信公眾號:bugstack蟲洞棧 & 服務端與監控

  5. 發送模擬信息,觀察執行結果;

     12019-12-01 15:11:49.965  INFO 7620 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
    22019-12-01 15:11:49.965  INFO 7620 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
    32019-12-01 15:11:49.980  INFO 7620 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 15 ms
    42019-12-01 15:11:51.157  INFO 7620 --- [nio-8080-exec-3] o.itstack.demo.ark.web.NettyController   : 查詢設備鏈接列表。[{"channelId":"281d1279","connectTime":1575184302964,"ip":"127.0.0.1","number":"74de0967-c0b4-4426-a9d1-183feaff2acf","port":3972}]
    52019-12-01 15:11:51.162  INFO 7620 --- [io-8080-exec-10] o.itstack.demo.ark.web.NettyController   : 查詢服務端列表。[{"ip":"10.13.70.50","openDate":1575184290501,"port":7397,"typeInfo":"NettyWsSocket"}]
    62019-12-01 15:11:58.476  INFO 7620 --- [ntLoopGroup-7-1] o.i.d.a.s.websocket.MyWsServerHandler    : 鏈接報告開始
    72019-12-01 15:11:58.476  INFO 7620 --- [ntLoopGroup-7-1] o.i.d.a.s.websocket.MyWsServerHandler    : 鏈接報告信息:有一客戶端鏈接到本服務端
    82019-12-01 15:11:58.476  INFO 7620 --- [ntLoopGroup-7-1] o.i.d.a.s.websocket.MyWsServerHandler    : 鏈接報告IP:0:0:0:0:0:0:0:1
    92019-12-01 15:11:58.476  INFO 7620 --- [ntLoopGroup-7-1] o.i.d.a.s.websocket.MyWsServerHandler    : 鏈接報告Port:7398
    102019-12-01 15:11:58.476  INFO 7620 --- [ntLoopGroup-7-1] o.i.d.a.s.websocket.MyWsServerHandler    : 鏈接報告完畢
    11服務端收到:{"channelId":"281d1279","msgType":2,"msgObj":{"stateType":"1"}}
    122019-12-01 15:12:05 接收到消息內容:{"msgObj":{"stateMsg":"溫度信息:91.31334894176383°C","stateType":1,"channelId":"93c1120a"},"msgType":3,"channelId":"93c1120a"}
    13服務端收到:{"channelId":"281d1279","msgType":2,"msgObj":{"stateType":"1"}}
    142019-12-01 15:12:05 接收到消息內容:{"msgObj":{"stateMsg":"溫度信息:44.83794772946285°C","stateType":1,"channelId":"93c1120a"},"msgType":3,"channelId":"93c1120a"}

綜上總結

  1. 在使用springboot與netty結合,開發一個簡便的服務端還是很方便的,而且在集合一些springcloud的服務,可以使項目工程更加完善。

  2. 可以嘗試做一些設備控制服務,在我們不在家的時候也可以通過一個h6鏈接控制家里的設備,比如快到家將熱水器打開。

  3. 本案例還偏向于模擬,在實際開發過程中還是會出現很多業務問題需要解決,尤其是服務端與下位機的通信,需要編寫編碼器與解碼器。

關于websocket與下位機如何通過netty方式通信傳輸行為信息就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

那坡县| 丽江市| 铁岭市| 仁寿县| 称多县| 改则县| 开化县| 汉源县| 肃北| 溧水县| 新蔡县| 墨脱县| 雷山县| 新沂市| 开平市| 苗栗县| 奇台县| 镇宁| 宜城市| 堆龙德庆县| 济南市| 清镇市| 金川县| 昭平县| 偃师市| 宝清县| 丹阳市| 仙游县| 奎屯市| 成武县| 馆陶县| 砚山县| 马山县| 开化县| 昭苏县| 保靖县| 东光县| 双桥区| 沿河| 九江县| 宾川县|