网络通信协议

WebSocket通信

  1. WebSocket是一种基于TCP的网络通信协议,提供了浏览器和服务器之间的全双工通信(full-duplex)能力。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。这使得数据可以更快地从服务器传到浏览器,而且减少了数据传输的数据量,因为头信息比较小。在WebSocket API中,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
  2. HTTP协议和WebSocket协议的主要区别如下:
    1. 连接方式:HTTP协议是无短连接的。每次请求都需要建立新的连接,请求结束后连接就断开。而WebSocket协议是长连接的,客户端和服务器建立连接后,直到其中一方主动断开,连接才会断开。
    2. 数据传输:HTTP协议只能由客户端向服务器发起请求,服务器返回响应数据。而WebSocket协议是全双工通信,服务器和客户端都可以主动发送数据。
    3. 性能开销:由于HTTP协议每次请求都需要建立新的连接,所以开销较大。而WebSocket协议建立连接后,可以进行多次数据传输,开销较小。
    4. 数据格式:HTTP协议传输的数据格式比较复杂,包含了请求行、请求头、消息体等。而WebSocket协议传输的数据通过帧来传输,数据格式比较简单。
    5. 实时性:HTTP协议的实时性不强,需要客户端定时轮询服务器获取新的数据。而WebSocket协议可以实现服务器主动推送数据,实时性较强。
  3. WebSocket主要适用于以下几种场景:
    1. 实时应用:聊天应用、多人协作应用、在线游戏、实时购物等。
    2. 实时数据推送:股票、新闻、天气、设备状态等实时信息的推送。
    3. IOT物联网:实时获取设备状态,实时控制设备等。
    4. 实时分析:实时数据分析、实时监控系统等。

WebSocket入门案例

  1. 客户端:

    1. 创建WebSocket对象:在JavaScript中,我们可以创建一个WebSocket对象,指定要连接的服务器地址。
    var ws = new WebSocket("ws://localhost:8080/websocket");
    
    1. 监听事件:WebSocket对象提供了四个事件:onopen、onmessage、onerror、onclose,我们可以通过监听这些事件来处理WebSocket的各种情况。
    ws.onopen = function(event) {console.log("Connection open ..."); 
    };ws.onmessage = function(event) {console.log("Received Message: " + event.data);
    };ws.onclose = function(event) {console.log("Connection closed ..."); 
    };ws.onerror = function(event) {console.log("Error: " + event.data);
    };
    
    1. 发送数据:WebSocket对象提供了一个send方法,我们可以通过这个方法向服务器发送数据。
    ws.send("Hello Server!");
    
    1. 关闭连接:当我们不再需要WebSocket连接时,可以调用WebSocket对象的close方法来关闭连接。
    ws.close();
    

    可以直接使用js写个小页面

    <!DOCTYPE HTML>
    <html>
    <head><meta charset="UTF-8"><title>WebSocket Demo</title>
    </head>
    <body><input id="text" type="text" /><button onclick="send()">Send Message</button><button onclick="closeWebSocket()">Close</button><div id="message"></div>
    </body>
    <script type="text/javascript">var websocket = null;var clientId = Math.random().toString(36).substr(2);//判断当前浏览器是否支持WebSocketif('WebSocket' in window){//连接WebSocket节点websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);}else{alert('Not support websocket')}//连接发生错误的回调方法websocket.onerror = function(){setMessageInnerHTML("error");};//连接成功建立的回调方法websocket.onopen = function(){setMessageInnerHTML("连接成功");}//接收到消息的回调方法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 send(){var message = document.getElementById('text').value;websocket.send(message);}//关闭连接function closeWebSocket() {websocket.close();}
    </script>
    </html>
  2. 服务端:

    1. 导入WebSocket的maven坐标

      <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>
      
    2. 导入WebSocket服务端组件WebSocketServer,用于与客户端通信

      package com.sky.websocket;import com.sky.handler.TurnoverReportVOEncoder;
      import org.springframework.stereotype.Component;
      import javax.websocket.OnClose;
      import javax.websocket.OnMessage;
      import javax.websocket.OnOpen;
      import javax.websocket.Session;
      import javax.websocket.server.PathParam;
      import javax.websocket.server.ServerEndpoint;
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.Map;/*** WebSocket服务*/
      @Component
      @ServerEndpoint(value = "/ws/{sid}",encoders = {TurnoverReportVOEncoder.class}) // 为对象指定编码器(目前是转成json发送给客户端)
      public class WebSocketServer {//存放会话对象private static Map<String, Session> sessionMap = new HashMap();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}/*** 连接关闭调用的方法** @param sid*/@OnClosepublic void onClose(@PathParam("sid") String sid) {System.out.println("连接断开:" + sid);sessionMap.remove(sid);}/*** 群发** @param message*/public void sendToAllClient(String message) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {//服务器向客户端发送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}public void sendObjToAllClient(Object object) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {//服务器向客户端发送对象--注意第4步骤,需要为该对象指定一个编码器session.getBasicRemote().sendObject(object);} catch (Exception e) {e.printStackTrace();}}}}
    3. 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件ServerEndpointExporter

      package com.sky.config;import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置类,用于注册WebSocket的Bean*/
      @Configuration
      public class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
      }
      
    4. 如果想向客户端推送封装好的对象,在WebSocket中,需要提供一个编码器来将这个对象转换为可以通过网络传输的格式,通常是字符串或者二进制数据。

      package com.sky.handler;import com.fasterxml.jackson.databind.ObjectMapper;
      import com.sky.vo.TurnoverReportVO;import javax.websocket.EncodeException;
      import javax.websocket.Encoder;
      import javax.websocket.EndpointConfig;/*** @projectName: sky-take-out* @package: com.sky.handler* @className: TurnoverReportVOEncoder* @author: fangjiayueyuan* @description: TODO* @date: 2023/12/24 16:16* @version: 1.0*/
      public class TurnoverReportVOEncoder implements Encoder.Text<TurnoverReportVO>{private static ObjectMapper objectMapper = new ObjectMapper();@Overridepublic String encode(TurnoverReportVO turnoverReportVO) throws EncodeException {try {// 使用Jackson库将对象转换为JSON字符串return objectMapper.writeValueAsString(turnoverReportVO);} catch (Exception e) {throw new EncodeException(turnoverReportVO, "对象转换为JSON字符串时发生错误", e);}}@Overridepublic void init(EndpointConfig endpointConfig) {// 这里可以进行编码器的初始化操作,但在这个例子中我们不需要进行任何操作}@Overridepublic void destroy() {// 这里可以进行编码器的清理操作,但在这个例子中我们不需要进行任何操作}
      }
    5. 导入定时任务类WebSocketTask,定时向客户端推送数据

      package com.sky.task;import com.sky.service.ReportService;
      import com.sky.vo.TurnoverReportVO;
      import com.sky.websocket.WebSocketServer;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.scheduling.annotation.Scheduled;
      import org.springframework.stereotype.Component;import java.time.LocalDate;
      import java.time.LocalDateTime;
      import java.time.format.DateTimeFormatter;@Component
      public class WebSocketTask {@Autowiredprivate WebSocketServer webSocketServer;@Autowiredprivate ReportService reportService;/*** 通过WebSocket每隔5秒向客户端发送消息*/@Scheduled(cron = "0/5 * * * * ?")public void sendMessageToClient() {webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}/*** 通过WebSocket每隔5秒向客户端发送消息*/@Scheduled(cron = "0/5 * * * * ?")public void sendObjMessageToClient() {TurnoverReportVO turnoverStatistics = reportService.getTurnoverStatistics(LocalDate.parse("2023-01-01"), LocalDate.now());webSocketServer.sendToAllClient("传个对象过去");webSocketServer.sendObjToAllClient(turnoverStatistics);}
      }

RPC通信

**RPC(Remote Procedure Call)**是一种通信协议,它允许运行在一台计算机上的程序调用另一台计算机上的程序中的函数或方法,就像调用本地函数一样,无需程序员显式处理底层的网络细节。

RPC的主要特征包括:

  1. 透明性:对于调用者来说,远程过程调用和本地过程调用是透明的,调用者无需关心过程调用的是本地过程还是远程过程。

  2. 语言无关性:RPC通常支持多种编程语言,只要两个通信的程序遵循同一RPC协议,它们就可以进行通信,无论它们是用什么编程语言编写的。

  3. 同步性:RPC通常是同步的,也就是说,当一个RPC调用发出后,调用者会停止执行,直到得到结果。然而,也有一些RPC系统支持异步调用。

为什么使用RPC

  1. 简化分布式系统的开发:RPC隐藏了底层的网络通信和数据传输的复杂性,使得开发分布式应用更加简单。

  2. 提高代码的可重用性:通过RPC,可以将一些通用的功能实现为服务,然后在多个应用中重用这些服务。

  3. 提高系统的可扩展性:通过RPC,可以将一个大的系统分解为多个可以独立开发和部署的小的服务。

RPC的替代方案

  1. RESTful API:RESTful API是一种基于HTTP协议的通信方式,它使用HTTP的方法(如GET、POST、PUT、DELETE等)来操作资源。RESTful API比RPC更简单,更易于使用,但它不如RPC灵活,因为它只能使用HTTP协议,而RPC可以使用任何传输协议。
  2. 消息队列:消息队列是一种异步的通信方式,它允许程序通过发送和接收消息来进行通信。消息队列可以解耦发送者和接收者,使得它们可以独立地扩展和失败。然而,消息队列的使用比RPC更复杂,因为它需要处理消息的发送、接收、存储和确认。

RPC入门案例

以Thrift为例:

  1. 定义数据类型和服务接口:使用Thrift的IDL(接口定义语言)定义数据类型和服务接口,然后通过Thrift的编译器生成对应语言的代码。

    namespace java com.sankuai.mdp.thriftstruct User{1:i32 id2:string name3:i32 age=0
    }service UserService{User getById(1:i32 id)bool isExist(1:string name)
    }
    
  2. 通过Thrift编译器生成Java代码:会生成两个对象:User、UserService

    thrift --gen java HelloWorld.thrift
    
  3. 服务端代码,实现UserService.Iface接口;启动服务端.

    package com.sankuai.mdp.thriftserversnapshot.service.impl;import com.sankuai.mdp.thriftapisnapshot.entity.User;
    import com.sankuai.mdp.thriftapisnapshot.entity.UserService;
    import org.apache.thrift.TException;/*** @projectName: thrift-api-snapshot* @package: com.sankuai.mdp.thriftserversnapshot.service.impl* @className: UserServiceImpl* @author: fangjiayueyuan* @description: TODO* @date: 2023/12/17 21:33* @version: 1.0*/
    public class UserServiceImpl implements UserService.Iface{@Overridepublic User getById(int id) throws TException {System.out.println("-----调用getById-----");User user = new User();user.setId(id);user.setName("dog");user.setAge(18);return user;}@Overridepublic boolean isExist(String name) throws TException {return false;}
    }
    package com.sankuai.mdp.thriftserversnapshot.service.impl;import com.sankuai.mdp.thriftapisnapshot.entity.UserService;
    import org.apache.thrift.protocol.TBinaryProtocol;
    import org.apache.thrift.server.TServer;
    import org.apache.thrift.server.TSimpleServer;
    import org.apache.thrift.transport.TServerSocket;
    import org.apache.thrift.transport.TServerTransport;
    import org.apache.thrift.transport.TTransportException;/*** @projectName: thrift-api-snapshot* @package: com.sankuai.mdp.thriftserversnapshot.service.impl* @className: SimpleService* @author: fangjiayueyuan* @description: TODO* @date: 2023/12/17 21:59* @version: 1.0*/
    public class SimpleService {public static void main(String[] args) {try{TServerTransport serverTransport = new TServerSocket(9090);UserService.Processor processor = new UserService.Processor(new UserServiceImpl());TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();TSimpleServer.Args targs = new TSimpleServer.Args(serverTransport);targs.processor(processor);targs.protocolFactory(protocolFactory);TServer server = new TSimpleServer(targs);server.serve();} catch (TTransportException e) {throw new RuntimeException(e);}}
    }
    
  4. 客户端代码,调用服务端的方法,就像调用本地方法一样

    package com.sankuai.mdp.thriftclientsnapshot.service.impl;import com.sankuai.mdp.thriftapisnapshot.entity.User;
    import com.sankuai.mdp.thriftapisnapshot.entity.UserService;
    import org.apache.thrift.TException;
    import org.apache.thrift.protocol.TBinaryProtocol;
    import org.apache.thrift.transport.TSocket;
    import org.apache.thrift.transport.TTransport;
    import org.apache.thrift.transport.TTransportException;/*** @projectName: thrift-api-snapshot* @package: com.sankuai.mdp.thriftclientsnapshot.service.impl* @className: SimpleClient* @author: fangjiayueyuan* @description: TODO* @date: 2023/12/17 21:58* @version: 1.0*/
    public class SimpleClient {public static void main(String[] args) {TTransport transport = null;try {transport = new TSocket("localhost", 9090);TBinaryProtocol protocol = new TBinaryProtocol(transport);UserService.Client client = new UserService.Client(protocol);transport.open();User result = client.getById(1);System.out.println("Result:" + result);} catch (TTransportException e) {e.printStackTrace();} catch (TException e) {throw new RuntimeException(e);} finally {if (transport != null) {transport.close();}}}
    }
  5. 先后启动运行服务端SimpleService.java、客户端代码SimpleClient.java
    在这里插入图片描述
    在这里插入图片描述
    Git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/299646.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个int型有序数组,如何拿到最低的值和这个值一共存在多少个?

一个int型有序数组&#xff0c;如何拿到最低的值和这个值一共存在多少个&#xff1f; 示例代码&#xff1a; int min 0;int os 1;int[] element {5,4,4,2,2};for (int i : element) {if (i min) {os;} else {min i;os 1;}}System.out.println("min " min);S…

《Git快速入门》Git分支

1.master、origin、origin/master 区别 首先搞懂git分支的一些名称区别&#xff1a; master &#xff1a; Git 的默认分支名字。它并不是一个特殊分支、跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master 分支&#xff0c;是因为 git init 命令默认创建它&#xff0c…

Python爬虫中的多线程、线程池

进程和线程的基本介绍 进程是一个资源单位&#xff0c;线程是一个执行单位&#xff0c;CPU调度线程来执行程序代码。 当运行一个程序时&#xff0c;会给这个程序分配一个内存空间&#xff0c;存放变量等各种信息资源&#xff0c;而这个内存空间可以说是一个进程&#xff0c; 一…

自学SLAM(9)《第五讲:特征点法视觉里程计》作业

文章目录 1.ORB特征点1.1 ORB提取1.2 ORB描述1.3 暴力匹配1.4 最后&#xff0c;请结合实验&#xff0c;回答下⾯⼏个问题 2.从 E 恢复 R&#xff0c;t3.用 G-N 实现 Bundle Adjustment4.* 用 ICP 实现轨迹对齐 1.ORB特征点 1.1 ORB提取 ORB(Oriented FAST and BRIEF) 特征是 S…

Android camera打开摄像头、预览

一、activity_main.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.a…

【MySQL】数据库之事务

目录 一、什么是事务 二、事务的ACID是什么&#xff1f; 三、有哪些典型的不一致性问题&#xff1f; 第一种&#xff1a;脏读 第二种&#xff1a;不可重复读 第三种&#xff1a;幻读 第四种&#xff1a;丢失更新 四、隔离级别有哪些&#xff1f; &#xff08;1&#xf…

doris基本操作,05-Rollup

简述 Rollup类似于mysql的视图&#xff0c;区别在于视图并没有将数据独立存储&#xff0c;视图是逻辑上的连接。而Rollup将数据独立存储了&#xff0c;玩的是真的。当查询命中Rollup时&#xff0c;会从Rollup表里获取数据&#xff0c;提高查询效率。 操作 创建Rollup表 alt…

codeforces round 894题解 A~F

文章目录 A. Gift Carpet题目大意思路AC代码 B. Sequence Game题目大意思路AC代码 C. Flower City Fence题目大意思路AC代码 D. Ice Cream Balls题目大意思路AC代码 E. Kolya and Movie Theatre题目大意思路AC代码 F. Magic Will Save the World题目大意思路AC代码 A. Gift Car…

【软件工程】可执行文件和数据分离

一、概述 可执行文件和数据分离是一种软件设计策略&#xff0c;旨在将程序代码和程序使用的数据分离存储。这种方法通常用于提高软件的模块化程度和灵活性&#xff0c;以及方便软件的管理和维护。 在可执行文件和数据分离中&#xff0c;程序代码通常以可执行文件的形式存储&a…

搭建Nginx文件下载站点

一、下载Nginx 首先&#xff0c;确保你的服务器上已经安装了Nginx&#xff0c;使用编译安装&#xff0c;下载最新版Nginx。 wget https://nginx.org/download/nginx-1.25.3.tar.gz tar -xf nginx-1.25.3.tar.gz二、安装Fancyindex和Nginx-Fancyindex-Theme模块 # 下载Fancyin…

MyBatis:Generator

MyBatis Generator附批量操作分页查询存储过程 Generator 介绍网址&#xff1a;Introduction to MyBatis Generator Generator &#xff0c;一个用于 MyBatis 的代码生成工具&#xff0c;可以根据数据库表结构自动生成对应的实体类、DAO 接口和 SQL 映射文件&#xff0c;提高…

java练习之abstract (抽象) final(最终) static(静态) 练习

1&#xff1a;分析总结&#xff1a;写出private、abstract、static、final之间能否联动使用&#xff0c;并写出分析原因 private static final 之间可以任意结合 abstract 不可以与private static final 结合使用 2&#xff1a;关于三个修饰符描述不正确的是(AD) A. static …