WebSocket是HTML5提供的一种新的网络通信协议。它实现了服务端与客户端的全双工通信,建立在传输层TCP协议之上,即浏览器与服务端需要先建立TCP协议,再发送WebSocket连接建立请求。
因为 HTTP 协议有一个缺陷:通信只能由客户端发起,HTTP 协议做不到服务器主动向客户端推送信息
比如说我们想要获取一个实时的新闻信息,在每次更新新闻信息后,我们都需要刷新页面才能获取到最新的信息,只有再次发起客户端请求,服务器端才会返回结果。但是服务器端不能做到推送消息给客户端,当然我们可以使用轮询,查看服务器有没有新的消息,比如 "聊天室" 这样的,但是轮询效率是非常低的,因此WebSocket就这样产生了。
客户端发送请求信息,服务端接收到这个请求并返回响应信息。
当连接建立后,客户端发送http请求时,通过Upgrade:webSocket Connection:Upgrade
告知服务器需要建立的是WebSocket连接,并且还会传递WebSocket版本号、协议的字版本号、原始地址、主机地址, WebSocket相互通信的Header很小,大概只有2Bytes
。
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.5</version>
</dependency>
实现WebSocketServer抽象类
public class WsServer extends WebSocketServer {
public WsServer(int port){
super(new InetSocketAddress(port));
}
public WsServer(InetSocketAddress address){
super(address);
}
// 建立连接
@Override
public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
broadcast(webSocket.getRemoteSocketAddress().getAddress().getHostAddress() + " 连接服务器!");
// 获取客户端连接的路径
// 例如客户端使用ws://ip:port/test连接,获取值为/test
broadcast("连接的路径: " + clientHandshake.getResourceDescriptor());
}
// 连接关闭
@Override
public void onClose(WebSocket webSocket, int code, String reason, boolean remote) {
broadcast(webSocket + " 与服务器断开连接!");
System.out.println(webSocket + " 与服务器断开连接!");
}
// 收到消息
@Override
public void onMessage(WebSocket webSocket, String message) {
// 广播消息,所有连接都会收到
broadcast(message);
// 只给当前连接发送消息
// webSocket.send(message);
System.out.println(webSocket + ": " + message);
}
// 连接异常
@Override
public void onError(WebSocket webSocket, Exception ex) {
ex.printStackTrace();
}
// 服务启动
@Override
public void onStart() {
System.out.println("WsServer 启动成功!");
setConnectionLostTimeout(0);
setConnectionLostTimeout(100);
}
}
public class Client {
public static void main(String[] args) throws Exception{
// 创建客户端实例
WebSocketClient webSocketClient = new WebSocketClient(new URI("ws://localhost:8887/"), new Draft_6455()) {
//连接服务端时触发
@Override
public void onOpen(ServerHandshake handshakedata) {
System.out.println("与服务器连接成功!");
}
//收到服务端消息时触发
@Override
public void onMessage(String message) {
System.out.println("收到消息:" + message);
}
//和服务端断开连接时触发
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("退出连接!");
}
//连接异常时触发
@Override
public void onError(Exception ex) {
ex.printStackTrace();
}
};
// 建立连接
webSocketClient.connect();
BufferedReader sysin = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String in = sysin.readLine();
// 发送消息
webSocketClient.send(in);
if (in.equals("exit")) {
webSocketClient.close();
break;
}
}
}
}
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、配置类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
// 设置最大Text大小
container.setMaxTextMessageBufferSize(512000);
// 设置最大Binary大小
container.setMaxBinaryMessageBufferSize(512000);
// 设置空闲连接超时ms
container.setMaxSessionIdleTimeout(5000L);
return container;
}
}
3、ws服务端逻辑
@Component
@ServerEndpoint("/test/{id}")
public class WsServer {
private static final Logger log = LoggerFactory.getLogger(WsServer.class);
private static final ConcurrentHashMap<String,Session> webSockets = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("id") String id){
webSockets.put(id,session);
log.info("新的客户端id : {} ,URI: {} 连接服务器!",id,session.getRequestURI());
}
@OnClose
public void onClose(Session session, @PathParam("id") String id){
webSockets.remove(id);
log.info("客户端id : {} 与服务器断开连接!",id);
}
@OnMessage
public void onMessage(Session session, @PathParam("id") String id, String message){
log.info("收到客户端 id : {} 发送的消息 : {}",id,message);
}
@OnError
public void onError(Session session, @PathParam("id") String id,Throwable error){
log.info("客户端id : {} ,发生异常 : {}",id,error);
}
/**
* 发送消息给指定客户端
* @param message
* @param id
*/
public void send(String message,String ... id){
try {
for (String i : id){
webSockets.get(i).getBasicRemote().sendText(message);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
4、启动类
@SpringBootApplication
public class Main implements CommandLineRunner {
@Autowired
private WsServer wsServer;
public static void main(String[] args) throws Exception {
SpringApplication.run(Main.class);
}
@Override
public void run(String... args) throws Exception {
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("输入目标客户端id : ");
String ids = scanner.next();
System.out.println("输入要发送的消息 : ");
String msg = scanner.next();
wsServer.send(msg,ids.split(","));
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket</title>
</head>
<body>
<input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSockeet连接</button>
<hr/>
<div id="message"></div>
</body>
</html>
<script type="text/javascript">
var webSocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window){
webSocket = new WebSocket('ws://localhost:8090/webSocket');
} else{
alert("当前浏览器不支持WebSocket");
}
//连接发生错误的回调方法
webSocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误!");
}
webSocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功!")
}
webSocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
}
webSocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
}
window.onbeforeunload = function () {
closeWebSocket();
}
function closeWebSocket() {
webSocket.close();
}
function send() {
var message = document.getElementById('text').value;
webSocket.send(message);
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
</script>