WebSocket介绍
WebSocket是H5的一种新协议,与http协议基本没有关系,在webSocket没有出现之前,服务端与客户端的通信主要通过轮询来实现,而轮询可以分为两种:传统轮询(Traditional pulling)和长轮询(Long pulling)。
传统轮询,每次都会新建一个http链接,而且不是每次都能够返回需要的数据,当同时发起的请求数量达到一定的数目,会给服务端造成较大的负担。所以,就出现了长轮询。
长轮询:在每次客户端发出请求后,服务器检查上次返回的数据与此次请求时的数据之间是否有更新,如果有更新则返回新数据并结束此次连接,否则服务器 hold 住此次连接,直到有新数据时再返回相应。而这种长时间的保持连接可以通过设置一个较大的 HTTP timeout` 实现。
虽然长轮询可以有效地解决传统轮询带来的带宽浪费,但是每次连接的保持是以消耗服务器资源为代价的。尤其对于Apache+PHP 服务器,由于有默认的 worker threads 数目的限制,当长连接较多时,服务器便无法对新请求进行相应。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务端推送技术的一种。其他特点包括:
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443 ,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
IDEA创建SpringBoot项目
除以上截图的注意点其余的都是点击next,idea就自动创建成功了。
SpringBoot集成WebSocket
1.在pom.xml中添加依赖
<!--webSocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!--webSocket-->
2.新建WebSocket代码配置文件
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3.编写WebSocket设置代码,类似于controller
@Slf4j
@ServerEndpoint(value = "/websocket/{platformType}/{userId}")
@Component
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
public static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
public static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
public Session session;
//接收参数中的用户ID
public Long userId;
//接收用户中的平台类型
public Integer platformType;
/**
* 连接建立成功调用的方法
* 接收url中的参数
*/
@OnOpen
public void onOpen(Session session, @PathParam("platformType") Integer platformType, @PathParam("userId") Long userId) {
this.session = session;
this.userId = userId;
this.platformType = platformType;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
log.info("有新连接加入!当前在线人数为" + getOnlineCount() + " userId==== " + userId + " platformType==== " + platformType);
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("websocket IO异常");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("来自客户端的消息:" + message);
}
/**
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误" + error);
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 私发
*
* @param message
* @throws IOException
*/
public static void sendInfo(Long userId, String message) throws IOException {
for (WebSocketServer item : webSocketSet) {
try {
if (item.userId.equals(userId)) {
item.sendMessage(message);
}
} catch (IOException e) {
continue;
}
}
}
/**
* 群发自定义消息
*/
public static void sendInfos(String message) throws IOException {
log.info(message);
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
此时我们可以从网上查找现成的WebSocket测试工具,输入:ws://localhost:8080/websocket/1/1,点击连接之后,我们可以从界面上看到接收到我们在连接成功发的“连接成功”的字样。
配置ssl,实现wss访问
1.ssl简介
SSl是一种安全协议。
SSL(Secure Sockets Layer)及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
SSL为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密技术,可确保数据在网络上之传输过程中不会被截取及窃听。一般通用之规格为40 bit之安全标准,美国则已推出128 bit之更高安全标准,但限制出境。
SSL协议位于tcp/ip协议与各种应用层协议之间,为数据通讯提供安全支持。广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输。
2.生成自签名证书
keytool -genkeypair -alias tomcat -keyalg RSA -keystore F:\keystore.jks
生成别名为tomcat的自签名证书,保存在F盘。然后按照提示输入个人(单位)信息。
3.项目配置ssl
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=123456
server.ssl.key-password=123456
server.ssl.key-alias=tomcat
将生成的keystore.jks拷贝到resource下,在application.properties或者application.yml中配置以上信息
4.配置tomcat实现https访问
@Configuration
public class TomcatConfiguration {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addAdditionalTomcatConnectors(createSslConnector());
return tomcat;
}
private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
//Connector监听的http的端口号
connector.setPort(8080);
connector.setSecure(false);
//监听到http的端口号后转向到的https的端口号
connector.setRedirectPort(8443);
return connector;
}
/**
* 创建wss协议接口
*
* @return
*/
@Bean
public TomcatContextCustomizer tomcatContextCustomizer() {
System.out.println("init");
return new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
System.out.println("init customize");
context.addServletContainerInitializer(new WsSci(), null);
}
};
}
}
5.创建简单的控制层访问,测试是否成功
@RestController
@RequestMapping("websocket")
public class TestController {
@RequestMapping("test")
public String test() {
return "Hello World";
}
}
从界面上可以看出ssl配置成功,实现了https访问,而https访问天然支持webSocket的wss。
6.测试wss
在现成的测试网站中,输入wss://localhost:8443/websocket/1/1,在界面中也可以看到“连接成功”的字样。此时需主意的是:不能用IP去访问,需要用域名,否则连接不会成功。从WebSocketde源码阅读可以看出,如果是wss的话,WebSocket会去验证域名,验证不通过直接就抛出io异常,导致链接不成功。