GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。
Upgrade: websocket
Connection: Upgrade
这个就是Websocket的核心了,告诉Apache、Nginx等服务器:注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:你妹,不要忽悠窝,我要验证你是不是真的是Websocket助理。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~
Upgrade: websocket
Connection: Upgrade
依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。
三、Websocket的作用
javax
javaee-api
7.0
provided
但使用springboot的内置tomcat时,就不需要引入javaee-api了,spring-boot已经包含了。使用springboot的websocket功能首先引入springboot组件。
org.springframework.boot
spring-boot-starter-websocket
1.3.5.RELEASE
springboot的高级组件会自动引用基础的组件,像spring-boot-starter-websocket就引入了spring-boot-starter-web和spring-boot-starter,所以不要重复引入。
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3、接下来就是写websocket的具体实现类,很简单,直接上代码:
package com.bocom.fmp.common.websocket;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
@ServerEndpoint(value = "/client/{userId}")
@Component
public class WarningPushSocket {
private static Logger logger = LogManager.getLogger(WarningPushSocket.class.getName());
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet wsClientMap = new CopyOnWriteArraySet<>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
* @param session 当前会话session
*/
@OnOpen
public void onOpen (@PathParam("userId")String userId,Session session){
this.session = session;
wsClientMap.add(this);
addOnlineCount();
logger.info(session.getId()+"有新链接加入,当前链接数为:" + wsClientMap.size());
}
/**
* 连接关闭
*/
@OnClose
public void onClose (){
wsClientMap.remove(this);
subOnlineCount();
logger.info("有一链接关闭,当前链接数为:" + wsClientMap.size());
}
/**
* 收到客户端消息
* @param message 客户端发送过来的消息
* @param session 当前会话session
* @throws IOException
*/
@OnMessage
public void onMessage (String message, Session session) throws IOException {
logger.info("来终端的警情消息:" + message);
sendMsgToAll(message);
}
/**
* 发生错误
*/
@OnError
public void onError(Session session, Throwable error) {
logger.info("wsClientMap发生错误!");
error.printStackTrace();
}
/**
* 给所有客户端群发消息
* @param message 消息内容
* @throws IOException
*/
public void sendMsgToAll(String message) throws IOException {
for ( WarningPushSocket item : wsClientMap ){
item.session.getBasicRemote().sendText(message);
}
logger.info("成功群送一条消息:" + wsClientMap.size());
}
public void sendMessage (String message) throws IOException {
this.session.getBasicRemote().sendText(message);
logger.info("成功发送一条消息:" + message);
}
public static synchronized int getOnlineCount (){
return WarningPushSocket.onlineCount;
}
public static synchronized void addOnlineCount (){
WarningPushSocket.onlineCount++;
}
public static synchronized void subOnlineCount (){
WarningPushSocket.onlineCount--;
}
}
My WebSocket
Welcome