通讯协议

后端实时推送

HTML5给Web浏览器带来了全双工TCP连接websocket标准服务器的能力。

1.websocket

1.1.WebSocket服务器端

@ServerEndpoint("/websocket")
public class WebSocketTest {
  @OnMessage
  public void onMessage(String message, Session session)
    throws IOException, InterruptedException {
    // Print the client message for testing purposes
    System.out.println("Received: " + message);
    // Send the first message to the client
    session.getBasicRemote().sendText("This is the first server message");
    // Send 3 messages to the client every 5 seconds
    int sentMessages = 0;
    while(sentMessages < 3){
      Thread.sleep(5000);
      session.getBasicRemote().
        sendText("This is an intermediate server message. Count: "
          + sentMessages);
      sentMessages++;
    }
    // Send a final message to the client
    session.getBasicRemote().sendText("This is the last server message");
  }
   
  @OnOpen
  public void onOpen() {
    System.out.println("Client connected");
  }
 
  @OnClose
  public void onClose() {
    System.out.println("Connection closed");
  }
}	
  • @ServerEndpoint注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址。
  • onOpen 和 onClose 方法分别被@OnOpen和@OnClose所注解。这两个注解的作用不言自明:他们定义了当一个新用户连接和断开的时候所调用的方法。
  • onMessage方法被@OnMessage所注解。这个注解定义了当服务器接收到客户端发送的消息时所调用的方法。

注意:这个方法可能包含一个javax.websocket.Session可选参数(在我们的例子里就是session参数)。 如果有这个参数,容器将会把当前发送消息客户端的连接Session注入进去。 本例中我们仅仅是将客户端消息内容打印出来,然后首先我们将发送一条开始消息,之后间隔5秒向客户端发送1条测试消息,共发送3次, 最后向客户端发送最后一条结束消息。

1.2.客户端

客户端API

  • 客户端与服务器通信
    • send() 向远程服务器发送数据
    • close() 关闭该websocket链接
  • 监听函数
    • onopen 当网络连接建立时触发该事件
    • onerror 当网络发生错误时触发该事件
    • onclose 当websocket被关闭时触发该事件
    • onmessage 当websocket接收到服务器发来的消息的时触发的事件,也是通信中最重要的一个监听事件。msg.data
  • readyState属性.这个属性可以返回websocket所处的状态。
    • CONNECTING(0) websocket正尝试与服务器建立连接
    • OPEN(1) websocket与服务器已经建立连接
    • CLOSING(2) websocket正在关闭与服务器的连接
    • CLOSED(3) websocket已经关闭了与服务器的连接
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
    websocket = new WebSocket("ws://localhost:8080/20160910_websocket/ws.do/${userSession.id }/${userSession.name }/${loginIp }");
} else {
    alert('当前浏览器 Not support websocket')
}

//1.连接发生错误的回调方法
websocket.onerror = function () {
    setMessageInnerHTML("WebSocket连接发生错误");
};

//2.连接成功建立的回调方法
websocket.onopen = function () {
    setMessageInnerHTML("WebSocket连接成功");
}

//3.接收到消息的回调方法
websocket.onmessage = function (event) {
    setMessageInnerHTML(event.data);
}

//4.连接关闭的回调方法
websocket.onclose = function () {
    setMessageInnerHTML("WebSocket连接关闭");
}

//5.监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
    closeWebSocket();
}

//6.将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
    document.getElementById('message').innerHTML += innerHTML + '<br/>';
}

//7.关闭WebSocket连接
function closeWebSocket() {
    websocket.close();
}

//8.发送消息
function send() {
    var friendId = document.getElementById('friend').value;
    
    var message = document.getElementById('text').value;
    websocket.send(friendId + ":" + message);
}

1.3.WebSockets 握手(升级过程)

客户端和服务器端TCP连接建立在HTTP协议握手发生之后。通过HTTP流量调试,很容易观察到握手。客户端一创建一个 WebSocket实例,就会出现如下请求和服务器端响应:

注意: 我们只录入了WebSocket握手所用到的HTTP头。

请求:
GET /byteslounge/websocket HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: wVlUJ/tu9g6EBZEh51iDvQ==

响应:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: websocket
Sec-WebSocket-Accept: 2TNh+0h5gTX019lci6mnvS66PSY=

注意:进行连接需要将通过Upgrade and Upgrade将协议升级到支持websocket HTTP头的Websocket协议。 服务器响应表明请求被接受,协议将转换到WebSocket协议(HTTP状态码101):HTTP/1.1 101 Web Socket Protocol Handshake

1.4.Spring集成

后端引入websocket支持

<!-- webSocket 秒杀通知-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

前端引入webSocket.js

2.SSE

SSE(Server-Sent Events)是一种基于HTTP协议的服务器到客户端的推送技术,主要用于在服务器和客户端之间建立持久连接, 以便服务器可以主动发送事件到客户端,而不需要客户端不断地轮询服务器。这对于实现实时更新、通知等功能非常有用。

与websocket相比,SSE 更简单易用,且为服务端向客户端的单向传输,

SSE 的基本概念

  1. 事件流‌:SSE 使用文本流(text/event-stream MIME类型)来传输数据。
  2. ‌事件‌:数据流被分割成一系列的事件,每个事件都有一个或多个行。
  3. ‌事件字段‌:每个事件由多个行组成,每行以特定的格式表示一个字段。例如:
event: message
data: Hello world!
data: Another message
id: 12345
retry: 10000

代码案例(模拟前后端交互,后端使用webflux)

async function sendMessage() {
    // 前端发送信息
    const messageInput = document.getElementById('messageInput');
    const message = messageInput.value.trim();
    if (!message) {
      return;
    }

    appendMessage(message, true);
    messageInput.value = '';

    try {
      // 创建 EventSource 实例,连接到后端的 SSE 接口
      const eventSource = new EventSource(
          '/stream/chatStream?msg=' + encodeURIComponent(message)
          + '&modelType=' + getSelectedModel());

      // 监听 message 事件,当接收到服务器发送的消息时触发
      eventSource.onmessage = (event) => {
        const data = JSON.parse(event.data);
        appendMessage(data.content, false);
      };

      // 监听 error 事件,当连接出现错误时触发
      eventSource.onerror = (error) => {
        if (error.eventPhase === EventSource.CLOSED) {
          console.log('Connection was closed');
        } else {
          console.error('EventSource failed:', error);
        }
        // 关闭连接
        eventSource.close();
      };

    } catch (error) {
      console.error('Error:', error);
      appendMessage('发生错误,请稍后重试。', false);
    }
}
@GetMapping(value = "/chatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Map<String, Object>> chatStream(
        @RequestParam(value = "msg", defaultValue = "") String msg,
        @RequestParam(value = "modelType", defaultValue = ModelType.DEFAULT) String modelType
) {
    return chatService.chatStream(msg, modelType).map(this::createResponse);
}

3.WebRTC

WebRTC(Web Real-Time Communication,网页实时通信)是一项浏览器原生支持的实时通信技术标准,允许浏览器之间直接建立点对点(P2P)连接, 实现音视频通话、数据传输等实时交互,无需依赖第三方插件或额外软件。

WebRTC 的核心目标是打破传统实时通信对中间服务器的强依赖,让浏览器之间能够直接传输数据(音视频、文本、文件等),其关键特性包括:

  1. 点对点通信:数据主要在客户端之间直接传输(仅信号协商需服务器参与),减少延迟和服务器带宽压力;
  2. 原生浏览器支持:无需安装插件,通过 JavaScript API 即可调用(如getUserMedia捕获音视频、RTCPeerConnection建立连接);
  3. 实时性强:基于 UDP 协议传输(部分场景降级为 TCP),延迟可低至 100-200 毫秒,满足实时交互需求;
  4. 安全性内置:所有数据强制加密传输(通过 SRTP 协议),防止窃听和篡改。

3.1.原理

WebRTC 的工作流程可分为信号协商、P2P 连接建立和媒体流传输三个核心阶段, 其中 "信号协商" 需借助服务器,而实际数据传输则在客户端之间直接进行。

第一:信号协商(Signaling):发现与连接准备。浏览器(客户端)无法直接知道对方的网络位置(IP 地址、端口), 需通过 "信号服务器" 交换连接所需的元数据(如网络信息、媒体能力):

  • 交换的核心信息:
    1. SDP(Session Description Protocol):描述本地媒体能力(如支持的音视频编码格式、分辨率、采样率等),分为 "offer"(发起方提议)和 "answer"(接收方应答);
    2. ICE 候选地址(ICE Candidate):包含本地网络可被访问的 IP 地址和端口(需穿透 NAT 网络地址转换)。

流程示例:

  • 客户端 A 生成 SDP offer 并通过信号服务器发送给客户端 B;
  • 客户端 B 收到 offer 后,生成 SDP answer 并通过信号服务器返回给 A;
  • 双方分别收集自身的 ICE 候选地址(如本地局域网地址、NAT 转换后的公网地址),并通过信号服务器互相发送;
  • 双方根据收到的 SDP 和 ICE 信息,确定最优通信路径。

第二:P2P 连接建立:穿透 NAT 与防火墙。由于多数设备处于 NAT(网络地址转换)或防火墙后,直接建立 P2P 连接需解决 "地址可达性"问题, WebRTC 通过ICE(Interactive Connectivity Establishment)协议实现:

ICE 工作原理:

  • 首先尝试 "主机候选地址"(本地局域网 IP),若双方在同一局域网,直接建立连接;
  • 若失败,尝试 "服务器反射候选地址"(通过 STUN 服务器获取 NAT 转换后的公网地址);
  • 若仍失败(如严格对称 NAT 环境),则通过 TURN 服务器中转数据(作为最后的 fallback 方案)。

关键服务器:

  • STUN 服务器:帮助设备获取自身的公网 IP 和端口(用于 NAT 穿透);
  • TURN 服务器:在 P2P 连接失败时,作为中继服务器转发数据(确保通信不中断,但增加延迟)。

第三:媒体流传输:实时音视频与数据交互。连接建立后,WebRTC 通过两套核心 API 实现数据传输:

媒体流传输(RTCPeerConnection):

  • 客户端通过getUserMediaAPI 捕获摄像头、麦克风数据,生成MediaStream(媒体流);
  • 通过RTCPeerConnection将媒体流编码(如 H.264、VP8 视频编码,OPUS 音频编码)后,通过 RTP(实时传输协议)发送给对方;
  • 接收方解码后,通过

数据通道(RTCDataChannel):

  • 除音视频外,WebRTC 支持通过RTCDataChannel传输任意二进制或文本数据(如聊天消息、文件、游戏控制指令);
  • 数据通道支持可靠传输(类似 TCP,保证数据有序、不丢失)和非可靠传输(类似 UDP,低延迟但可能丢包),可按需配置。

3.2.应用场景

WebRTC 适用于需要低延迟、点对点实时交互的场景,尤其在音视频通信领域应用广泛:

  • 实时音视频通话:如网页版视频会议(腾讯会议网页版)、在线问诊(医生与患者视频沟通)、远程面试系统等,WebRTC 可提供 100-200ms 低延迟的音视频传输。
  • 屏幕共享与远程协助:如在线教育中的老师屏幕共享、远程办公中的技术支持(协助操作对方电脑),通过getDisplayMediaAPI 捕获屏幕流并实时传输。
  • 实时互动直播:如直播平台的连麦功能(主播与观众实时互动)、在线课堂的师生互动,WebRTC 可支持多人间的低延迟音视频交互。
  • 浏览器端 P2P 文件传输:如基于网页的文件共享工具,用户可直接向其他在线用户发送文件,无需通过服务器中转(速度取决于双方带宽)。
  • 实时游戏:如多人文本冒险游戏、简单实时对战游戏,通过RTCDataChannel传输玩家操作指令,实现低延迟同步。
  • 物联网设备实时监控:如通过网页实时查看家用摄像头画面、工业设备监控视频,WebRTC 可直接接收设备推送的音视频流。

4.其他

主要为框架,并非协议。

4.1.goeasy

国产实现方案,需要收费。

GoEasy实时Web推送,支持后台推送和前台推送两种:后台推送可以选择Java SDK、 Restful API支持所有开发语言;前台推送:JS推送。

无论选择哪种方式推送代码都十分简单(10分钟可搞定)。

由于它支持websocket和polling两种连接方式所以兼顾大多数主流浏览器,低版本的IE浏览器也是支持的。

地址:http://goeasy.io/

4.2.Pushlets

Pushlets是通过长连接方式实现"推"消息的。推送模式分为:Poll(轮询)、Pull(拉)。 地址:http://www.pushlets.com/

4.3.Pushlet

Pushlet 是一个开源的 Comet 框架,Pushlet 使用了观察者模型:客户端发送请求,订阅感兴趣的事件;服务器端为每个客户端分配一个会话ID 作为标记,事件源会把新产生的事件以多播的方式发送到订阅者的事件队列里。

地址:https://github.com/wjw465150/Pushlet

总结: 其实前面有提过,尽管WebSocket有诸多优点,但是,如果服务端维护很多长连接也是挺耗费资源的,服务器集群以及览器或者客户端兼容性问题, 也会带来了一些不确定性因素。大体了解了一下各大厂的做法,大多数都还是基于轮询的方式实现的,比如:腾讯PC端微信扫码登录、京东商城支付成功通知等等。 有些小伙伴可能会问了,轮询岂不是会更耗费资源?其实在我看来,有些轮询是不可能穿透到后端数据库查询服务的,比如秒杀,一个缓存标记位就可以判定是否秒杀成功。 相对于WS的长连接以及其不确定因素,在秒杀场景下,轮询还是相对比较合适的。

Jersey

1.什么是Jersey

RESTful Web服务是基于REST架构的Web服务。在REST架构一切都是一种资源。 RESTful Web服务是轻量级的,高度可扩展性和可维护性,并且非常常用于创建基于API的Web应用程序。 Jersey是JAX-RS(JSR311)开源参考实现用于构建RESTful Web service,它包含三个部分:

  1. 核心服务器(Core Server)通过提供JSR311中标准化的注释和API标准化,可以用直观的方式开发RESTful Web服务;
  2. 核心客户端(Core Client)Jersey客户端API能够帮助开发者与RESTful服务轻松通信;
  3. 集成(Integration)Jersey还提供可以轻松继承Spring、Guice、Apache Abdera的库。

2.常用注解

2.1.@Produces

就是发送到客户端的数据标识类型(MIME)。

  • 返回xml:@Produces(MediaType.APPLICATION_XML)
  • 返回json:@Produces(MediaType.APPLICATION_JSON)
  • 返回自定义返回类型及编码:@Produces("application/json;charset=utf-8")
  • 返回多个类型:@Produces({"application/json","application/xml"})

2.2.@Consumes

就是接受只客户端的的数据标识类型(MIME)。多用于post和put请求。用法和@Produces响应

2.3.@PathParam

可以获取URI中指定规则的参数。

2.4.@QueryParam

用于获取GET请求中的查询参数。

2.5.@DefaultValue

为参数设置默认值

2.6.@FormParam

从POST请求的表单参数中获取数据

@Path("/user/{id}")
@GET
@Produces(MediaType.APPLICATION_XML)
public User getUserById(@PathParam("id") Integer id, @DefaultValue("张三")  @QueryParam("name") String name){
    return this.userDao.getById(id);
}

@POST
@Consumes("application/x-www-form-urlencoded")
public void post(@FormParam("name") String name) { 
}

@POST
@Consumes("application/x-www-form-urlencoded")
public void post2(@BeanParam User user) { 
}

@GET
public String get(@Context UriInfo ui) {
    MultivaluedMap<String, String> queryParams =
    ui.getQueryParameters(); MultivaluedMap<String, String>
    pathParams = ui.getPathParameters();
}

2.6.@BeanParam

将请求中的参数,保存为对象中。

2.7.@Context

因为参数的多变,参数结构的调整都会因为以上几种方式而遇到问题,这时可以考虑使用@Context 注释,并获取UriInfo实例 同样还可以通过@Context注释获取ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse和HttpHeaders等

2.8.@XmlRootElement

数据为XML是,用于和Javabean绑定,

@XmlRootElement
public class User{
    private String name;
    
    @XmlElement(name = "user_name")//这个必须写在set方法上。
    public void setName(String name) {
        this.name = name;
    }
}

DWR

DWR 是一个可以允许你去创建 AJAX WEB 站点的 JAVA 开源库。它可以让你在浏览器中的 Javascript 代 码调用 Web 服务器上的 Java 代码,就像在 Java 代码就在浏览器中一样。

DWR 包含 2 个主要部分:

  • 一个运行在服务器端的 Java Servlet,它处理请求并且向浏览器发回响应。
  • 运行在浏览器端的 JavaScript,它发送请求而且还能动态更新网页。

DWR 工作原理是通过动态把 Java 类生成为 Javascript。它的代码就像 Ajax 魔法一样,你感觉调用就像发 生在浏览器端, 但是实际上代码调用发生在服务器端,DWR 负责数据的传递和转换。这种从 Java 到 JavaScript的远程调用功能的方式使DWR用起来有种非常像RMI或者SOAP的常规RPC机制, 而且DWR 的优点在于不需要任何的网页浏览器插件就能运行在网页上。

email

1.邮件开发相关的概念

1.1.邮件服务器

要在Internet上提供电子邮件功能,必须有专门的电子邮件服务器(软)。例如现在Internet很多提供邮件服务的厂商: sina、sohu、163等等他们都有自己的邮件服务器。这些服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件, 并把邮件投递到邮件接收者的电子邮箱中。

1.2.电子邮箱

电子邮箱(Email地址)的获得需要在邮件服务器上进行申请,确切地说,电子邮箱其实就是用户在邮件服务器上申请的一个账户。 用户在邮件服务器上申请了一个账号后,邮件服务器就会为这个账号分配一定的空间,用户从而可以使用这个账号以及空间, 发送电子邮件和保存别人发送过来的电子邮件。

1.3.邮件开发用到的协议:SMTP、POP、RFC822、MIME

(1).SMTP:Simle Message Transfer Protocal简单消息传输协议。发送邮件时使用的协议。 描述了数据该如何表示。默认使用的端口:25

(1).POP3:Post Office Protocal邮局协议。接收邮件时使用的协议。默认使用的端口:110

2.手工发送邮件和接收邮件

itheimacloud@163.com iamsorry
itheima14@163.com iamsorry
SMTP:smtp.163.com POP3:pop.163.com

Base64编码:
用户名:itheimacloud aXRoZWltYWNsb3Vk
密码:iamsorry aWFtc29ycnk=

2.1.手工发送邮件的过程:telnet(执行命令行)

telnet smtp.163.com 25

------------------------------------------------------------以下属于SMTP协议的内容
ehlo wzt //向服务器打招呼
auth login //请求认证
//提示输入用户名和密码(Base64编码后的)
aXRoZWltYWNsb3Vk
aWFtc29ycnk=
mail from:<itheimacloud@163.com>
rcpt to:<itheima14@163.com>
data //说明邮件的开始
//邮件内容是要遵循一定格式的:RFC822规范
from:itheimacloud@163.com //fengjie@163.com
to:itheima14@163.com
subject:这是一封手工发送的邮件
aaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbb
.
------------------------------------------------------------
quit

2.2.手工收取邮件(POP协议)(执行命令行)

telnet pop.163.com 110
----------------------------------以下属于POP协议的东东
user itheima14
pass iamsorry
stat //返回邮箱的统计信息
list 邮件编号 //返回某一封的邮件统计信息
retr 邮件编号 //取出某一封邮件的内容
----------------------------------
quit

3.相关框架

  • Java mail: Oracle提供的,对api进行了简单的封装
  • Apache Commons email
  • Spring email

4.发生失败原因

因为各种原因,总会有邮件发送失败的情况,比如:邮件发送过于频繁、网络异常等。 在出现这种情况的时候,我们一般会考虑重新重试发送邮件,会分为以下几个步骤来实现:

  1. 接收到发送邮件请求,首先记录请求并且入库。
  2. 调用邮件发送接口发送邮件,并且将发送结果记录入库。
  3. 启动定时系统扫描时间段内,未发送成功并且重试次数小于3次的邮件,进行再次发送

5.异步发送

很多时候邮件发送并不是我们主业务必须关注的结果,比如通知类、提醒类的业务可以允许延时或者失败。 这个时候可以采用异步的方式来发送邮件,加快主交易执行速度, 在实际项目中可以采用MQ发送邮件相关参数,监听到消息队列之后启动发送邮件。

webService

webservice,就是应用程序之间跨语言的调用,不同的语言平台对WebService都有实现,Java的WebService实现,比较流行的有Axis2、Jaxws,本文介绍的是Axis2

一、重要的组成

  1. xml
  2. wsdl:web service description language—-web服务描述语言。 也是一种xml文件,声明了调用的服务的name,URL,method以及如何调用。
  3. soap:simple object access protoacl—–简单对象访问协议。限定了xml的格式,是在http的基础上传输xml数据。因为有请求体,所以必须是post请求。
  4. operation name:服务提供的方法
  5. targetNamespace: 默认为倒置的包名,例如服务类在com.zx.service中,那就是service.zx.com
  6. JAX-WS:java api for xml - WebService

二、需要注意

  1. 发布的外部服务不能包含静态方法,静态变量等,这些成员是在初始化的时候,存在到内容中的,是无法被序列化, 进而数据传输的。
  2. 使用自动生成的webservice服务端,需要/WEB-INF/conf/axis2.xml,配置其内两个属性,以便调试
<parameter name="hotdeployment">true</parameter><!-- 开启热部署,不需要重启即可部署服务 -->  
<parameter name="hotupdate">true</parameter><!-- 开启热更新,不需要重启即可更新服务 --> 

文章信息

张雪随笔
master
Homepage
Issues
Download

This Software is under the terms of The Unlicense.