WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它允许服务器和客户端之间进行实时的数据交换,使得网页应用能够实现类似桌面应用的即时交互功能。与传统的 HTTP 请求/响应模式不同,WebSocket 提供了一个持久的连接,使得数据可以在任何时候从服务器发送到客户端,而无需等待客户端请求。
WebSocket 的主要特点包括:
- 全双工通信:支持双向数据传输,即服务器可以主动向客户端推送消息。
- 低延迟:由于建立了持久连接,减少了建立连接的开销,从而降低了通信延迟。
- 高并发性:一个 WebSocket 连接可以处理多个消息,适合需要频繁通信的场景。
- 跨域支持:通过适当的配置,WebSocket 可以支持跨域请求,但需要注意浏览器的安全策略。
WebSocket 的使用场景非常广泛,包括但不限于:
- 实时聊天应用
- 在线游戏
- 实时金融行情更新
- 协作编辑工具
- 实时通知系统
以下是一个简单的 WebSocket 示例代码:
服务端 (Node.js)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
客户端 (HTML + JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Test</title>
</head>
<body>
<script>
var ws = new WebSocket('ws://localhost:8080');
ws.onopen = function() {
console.log('Connected to server');
};
ws.onmessage = function(event) {
console.log('Received: ', event.data);
};
</script>
</body>
</html>
WebSocket 和 HTTP 轮询是两种不同的实时通信技术,它们在实现方式、性能和适用场景上有明显的区别。
-
连接类型:
- WebSocket:WebSocket 是一种持久的双向连接,一旦建立连接,服务器和客户端可以通过同一个 TCP 连接进行全双工通信。这意味着数据可以在任意时刻从客户端发送到服务器,反之亦然,而不需要重新建立连接。
- HTTP 轮询:HTTP 轮询是一种基于 HTTP 请求的单向通信方式。客户端定时向服务器发送 HTTP 请求以获取最新数据,每次请求都是独立的,并且需要重新建立连接。
-
性能:
- WebSocket:由于 WebSocket 保持一个持续的连接,减少了频繁建立和关闭连接的开销,因此在实时性要求高的应用中表现更好。延迟较低,数据传输效率更高。
- HTTP 轮询:HTTP 轮询需要频繁地发送请求,这会导致较高的网络流量和服务器负载。此外,每次轮询都有一定的延迟,因此不适合对实时性要求非常高的场景。
-
资源消耗:
- WebSocket:WebSocket 连接一旦建立,资源消耗相对稳定,不会因为频繁的连接和断开导致额外的开销。
- HTTP 轮询:HTTP 轮询会频繁地创建和销毁连接,增加了服务器的资源消耗,尤其是在高并发的情况下,这种开销会更加明显。
-
适用场景:
- WebSocket:适用于需要高实时性的应用场景,如在线游戏、实时聊天、金融行情等。
- HTTP 轮询:适用于对实时性要求不高的场景,或者在不支持 WebSocket 的环境中作为替代方案使用。
-
兼容性:
- WebSocket:现代浏览器对 WebSocket 的支持较好,但在一些老旧的浏览器中可能需要 polyfill 或降级处理。
- HTTP 轮询:几乎所有的浏览器都支持 HTTP 请求,因此兼容性更好,但需要考虑性能问题。
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它的主要优点包括:
- 低延迟:WebSocket 允许服务器和客户端之间进行实时双向通信,减少了传统 HTTP 请求/响应模式中的延迟。
- 节省带宽:由于 WebSocket 保持一个持续的连接,避免了频繁的握手和建立连接的过程,从而节省了带宽。
- 简化开发:WebSocket 提供了一个简单的 API,使得开发者可以更容易地实现实时通信功能,而不需要处理底层的 TCP 连接细节。
- 高并发支持:WebSocket 能够高效地处理大量并发连接,适合需要实时数据推送的应用,如在线游戏、金融行情等。
- 跨平台兼容性:WebSocket 得到了广泛的浏览器支持,并且可以在不同的操作系统和设备上使用,确保了应用的广泛可用性。
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,与传统的 HTTP 轮询相比具有以下优势:
-
持久连接:WebSocket 建立的是持久连接,一旦连接建立,服务器可以随时向客户端发送消息,而不需要客户端请求。这减少了延迟和不必要的网络开销。
-
双向通信:WebSocket 支持服务器到客户端(Server-to-Client)和客户端到服务器(Client-to-Server)的双向通信,使得实时应用更加高效。
-
低延迟:由于 WebSocket 保持了持久连接,避免了频繁的连接建立和关闭操作,从而显著降低了通信延迟。
-
资源利用:相比于传统的 HTTP 轮询,WebSocket 减少了多次请求带来的带宽和服务器资源的浪费,因为只需要一个持续的连接来处理所有通信。
-
简化开发:WebSocket 提供了简单的 API,使得开发者可以更容易地实现实时通信功能,而不必处理复杂的轮询逻辑。
-
更好的控制:WebSocket 允许更精细的控制,例如可以设置心跳检测机制来确保连接的稳定性,以及通过帧分片传输大数据。
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,适用于需要实时数据传输和双向通信的应用场景。以下是一些常见的 WebSocket 适用场景: -
实时聊天应用:WebSocket 可以实现即时通讯功能,用户之间可以实时发送和接收消息,无需刷新页面。
-
在线游戏:用于实时同步玩家的状态和游戏数据,减少延迟,提高用户体验。
-
实时金融行情:股票、期货等金融市场的数据变化频繁,WebSocket 可以实时推送最新的行情数据到客户端。
-
实时通知系统:如社交媒体的通知、邮件提醒等,通过 WebSocket 可以及时将新信息推送给用户。
-
远程控制:例如远程桌面、智能家居控制等,通过 WebSocket 实现对设备的实时控制和状态监控。
-
协作工具:如在线文档编辑、白板工具等,多个用户可以同时对同一个文档进行编辑,WebSocket 保证数据的实时同步。
-
直播流媒体:在视频直播或音频直播中,WebSocket 可以用于传输实时的音视频数据。
-
物联网(IoT)应用:设备与服务器之间的实时数据交换,如传感器数据上报、远程控制指令下发等。
-
实时数据分析:用于大数据处理中的实时分析,将分析结果实时反馈给前端展示。
-
在线客服系统:客户与客服人员之间的实时交流,提升客户服务体验。
WebSocket 和 HTTP 是两种不同的网络协议,它们在功能和使用场景上存在显著差异。以下是 WebSocket 与 HTTP 的主要区别:
-
连接方式:
- HTTP:每次请求都需要建立一个新的 TCP 连接,完成请求后立即断开连接。这种方式被称为“无状态”的通信模式。
- WebSocket:一旦建立了一个 WebSocket 连接,这个连接会一直保持打开状态,允许服务器和客户端之间进行双向通信。这种持续的连接使得实时数据交换更加高效。
-
通信模式:
- HTTP:基于请求/响应模型,客户端发送请求到服务器,服务器处理请求并返回响应。每个请求都是独立的,没有持久的连接。
- WebSocket:基于事件驱动的模型,允许服务器主动向客户端推送消息,而不需要等待客户端的请求。这种机制特别适合需要实时更新的应用,如在线聊天、股票行情等。
-
数据传输效率:
- HTTP:由于每次请求都需要建立和关闭连接,传输效率相对较低。此外,HTTP 头信息较大,增加了不必要的开销。
- WebSocket:一旦连接建立,后续的数据包只需要很小的头部信息,减少了传输的开销,提高了数据传输的效率。
-
适用场景:
- HTTP:适用于传统的网页浏览、文件下载等场景,这些场景通常不需要频繁的双向通信。
- WebSocket:适用于需要实时交互的应用,如在线游戏、实时聊天、金融交易系统等。
-
资源占用:
- HTTP:由于每次请求都需要建立新的连接,频繁的请求会导致较高的资源消耗。
- WebSocket:通过保持长连接,减少了频繁建立和关闭连接的资源开销,适合高并发的场景。
-
安全性:
- HTTP:可以通过 HTTPS 来保证数据传输的安全性,但每次请求都需要重新建立加密通道。
- WebSocket:可以在初始握手时使用 HTTPS 来保证连接的安全性,之后的数据通信则通过已建立的安全通道进行。
总结来说,WebSocket 提供了一种高效的双向通信机制,适合需要实时数据交换的应用;而 HTTP 更适合传统的请求/响应模式,适用于大多数静态内容的传输。
Getting Started
Reference Documentation
For further reference, please consider the following sections:
- Official Apache Maven documentation
- Spring Boot Maven Plugin Reference Guide
- Create an OCI image
- Spring Integration
- Spring for RabbitMQ
- Spring for Apache Kafka
- Apache Kafka Streams Support
- Apache Kafka Streams Binding Capabilities of Spring Cloud Stream
- Spring for Apache ActiveMQ 5
- Spring for Apache ActiveMQ Artemis
- WebSocket
Guides
The following guides illustrate how to use some features concretely:
- Integrating Data
- Messaging with RabbitMQ
- Samples for using Apache Kafka Streams with Spring Cloud stream
- Java Message Service API via Apache ActiveMQ Classic.
- Messaging with JMS
- Using WebSocket to build an interactive web application
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-artemis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>