目录

一.概念

二.建立流程

三.四大事件

五.js中建立ws链接

 六.springboot中进行ws连接

1.首先,添加WebSocket的依赖到你的Spring Boot项目中。

 2.接下来,创建一个WebSocket处理器

3.最后,创建一个配置类,注册该WebSocket处理器:

七.使用@ServerEndpoint注解实现

 八.群聊私聊案例

九.文字和图片消息的处理


一.概念

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它通过在客户端和服务器之间建立持久的连接,实现了服务器端可以主动推送消息给客户端的功能,而不需要客户端发起请求。

WebSocket协议的主要特点包括:

全双工通信:客户端和服务器可以同时发送和接收消息,无需等待对方的回应。

基于事件驱动:当有新的消息到达时,服务器可以主动推送消息给客户端,而不需要客户端发起请求。

较低的带宽消耗:WebSocket协议使用较少的 HTTP 头信息,因此带宽消耗较小。

较低的延迟:WebSocket协议采用长连接的方式,减少了连接建立的时间和数据传输的延迟。

跨域通信:WebSocket协议支持跨域通信,可以在不同的域名下进行通信。

适用于实时应用:由于WebSocket协议的特性,它非常适用于需要实时更新的应用程序,如在线聊天、实时数据更新等。

WebSocket协议的使用需要具备以下条件:

1.客户端和服务器都需要支持WebSocket协议。

2.客户端和服务器之间需要建立一个WebSocket连接。

3.客户端和服务器需要通过WebSocket协议进行通信,发送和接收消息。

目前,WebSocket协议已被广泛应用于Web应用程序、移动应用程序和即时通讯等领域

二.建立流程

建立WebSocket连接的流程如下:

  1. 创建WebSocket对象:在客户端代码中创建一个WebSocket对象,用于与服务器建立连接。

  2. 发起握手请求:客户端WebSocket对象发送一个HTTP升级请求,请求将协议从HTTP更改为WebSocket。

  3. 服务器回应握手请求:服务器收到握手请求后,返回一个HTTP升级响应,确认请求已成功。

  4. 建立WebSocket连接:一旦握手成功,建立WebSocket连接。此时,双方可以通过WebSocket对象发送和接收消息。

  5. 数据传输:客户端和服务器之间可以通过WebSocket对象进行双向数据传输。

  6. 关闭连接:当WebSocket连接不再需要时,可以通过调用WebSocket对象的close()方法来关闭连接。

WebSocket协议建立报文格式如下:

  1. 客户端发送握手请求报文给服务器:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
  1. 服务器返回握手响应报文给客户端:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

其中,Sec-WebSocket-Key字段是客户端随机生成的16字节的字符串,服务器收到后会加上一个固定的字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",然后通过SHA-1算法计算出Sec-WebSocket-Accept字段的值。这个值用于验证客户端发送的请求是否合法。

WebSocket传输的数据格式是经过封装的二进制或文本数据。

在建立WebSocket连接之后,客户端和服务器都可以互相发送消息。发送的消息可以是文本类型或二进制类型。

对于文本类型的消息,客户端和服务器发送的数据格式如下:

Opcode: TEXT (0x1)
Payload length: 7 (7字节长度)
Payload data: "Hello!"

这个消息报文包含了一个Opcode字段,指示消息类型为文本类型;一个Payload length字段,指示消息的长度为7字节;一个Payload data字段,保存了具体的消息内容。

对于二进制类型的消息,格式类似,只是Opcode字段值改为BINARY (0x2)。

除了普通的消息类型外,WebSocket还支持PING和PONG类型的消息,用于心跳检测。PING消息的格式如下:

Opcode: PING (0x9)
Payload length: 4 (4字节长度)
Payload data: "ping"

服务器接收到PING消息后会回复一个PONG消息,PONG消息的格式如下:

Opcode: PONG (0xA)
Payload length: 4 (4字节长度)
Payload data: "pong"

三.四大事件

WebSocket有四个主要的事件:

  1. 连接建立事件onopen):在客户端与服务器成功建立连接时触发。可以用来发送初始数据或进行认证。

  2. 消息接收事件onmessage):当服务器向客户端发送消息时触发。可以通过此事件获取服务器发送的数据。

  3. 连接关闭事件onclose):在客户端与服务器的连接关闭时触发。可以进行一些清理工作,比如释放资源或重新连接。

  4. 错误事件onerror):当 WebSocket 连接发生错误时触发。可以通过此事件处理连接错误,例如连接失败或消息发送失败等。

五.js中建立ws链接

在JavaScript中建立WebSocket连接可以使用WebSocket对象。以下是建立WebSocket连接的步骤:

  1. 创建一个WebSocket对象:使用new WebSocket()构造函数创建一个WebSocket对象。需要传入WebSocket的URL作为参数。例如:
const socket = new WebSocket('wss://example.com/socket');
  1. 事件处理:WebSocket对象有几个事件处理函数,用于处理不同的WebSocket状态和消息。

    • onopen事件:当WebSocket连接成功建立时触发。
    • onmessage事件:当接收到服务器发送的消息时触发。
    • onclose事件:当WebSocket连接关闭时触发。
    • onerror事件:当发生WebSocket错误时触发。

    你可以使用下面的语法进行事件处理:

    socket.onopen = function() {
      // 连接成功
    };
    
    socket.onmessage = function(event) {
      // 接收到消息
      const message = event.data;
    };
    
    socket.onclose = function(event) {
      // 连接关闭
      const code = event.code;
      const reason = event.reason;
    };
    
    socket.onerror = function(error) {
      // 处理错误
    };
  2. 发送和接收消息:可以使用WebSocket对象的send()方法发送消息,使用onmessage事件处理函数接收消息。例如:

    socket.send('Hello, server!');
    
    socket.onmessage = function(event) {
      const message = event.data;
      console.log('Received message: ' + message);
    };
  3. 关闭连接:可以使用WebSocket对象的close()方法关闭连接。例如:

    socket.close();

 六.springboot中进行ws连接

1.首先,添加WebSocket的依赖到你的Spring Boot项目中。

  在pom.xml文件中,加入以下依赖:

<dependencies>
    <!-- ... 其他依赖 ... -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>

 2.接下来,创建一个WebSocket处理器

,用于处理WebSocket相关的事件:

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

@Component
public class MyWebSocketHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("WebSocket连接已建立");
        session.sendMessage(new TextMessage("你已成功连接到WebSocket服务器"));
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        System.out.println("收到消息:" + message.getPayload());
        session.sendMessage(new TextMessage("服务端已收到您的消息:" + message.getPayload()));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        System.out.println("WebSocket连接已关闭");
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("WebSocket传输错误");
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

在这个处理器中,我们实现了WebSocketHandler接口,并重写了其中的方法。在afterConnectionEstablished方法中,当Websocket连接成功建立时,会打印一条消息,并发送一条欢迎消息给客户端。在handleMessage方法中,当收到客户端发送的消息时,会打印消息内容,并返回一条回复消息给客户端。在afterConnectionClosed方法中,当Websocket连接关闭时,会打印一条消息。

3.最后,创建一个配置类,注册该WebSocket处理器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MyWebSocketHandler(), "/websocket")
                .setAllowedOrigins("*");
    }
}

在这个配置类中,我们使用@EnableWebSocket注解启用WebSocket支持,并通过registerWebSocketHandlers方法注册了我们之前创建的WebSocket处理器MyWebSocketHandler,指定了WebSocket的访问路径为/websocket并设置允许的跨域访问。

七.使用@ServerEndpoint注解实现

以下是使用@ServerEndpoint注解实现WebSocket的四大事件的示例代码:

@ServerEndpoint("/websocket")
public class MyWebSocketEndpoint {

    private static Set<Session> sessionSet = new HashSet<>();

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("WebSocket连接已建立");
        sessionSet.add(session);
        try {
            session.getBasicRemote().sendText("你已成功连接到WebSocket服务器");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到消息:" + message);
        try {
            session.getBasicRemote().sendText("服务端已收到您的消息:" + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnClose
    public void onClose(Session session) {
        System.out.println("WebSocket连接已关闭");
        sessionSet.remove(session);
    }

    @OnError
    public void onError(Throwable error) {
        System.err.println("WebSocket错误:" + error.getMessage());
    }
}

在这个示例中,通过@ServerEndpoint("/websocket")注解声明了一个WebSocket端点,并使用@OnOpen@OnMessage@OnClose注解分别实现了连接建立、接收消息和连接关闭事件。

需要注意的是,Session对象用于表示客户端与服务器之间的WebSocket连接,通过它可以向客户端发送消息。在示例中,我们使用一个HashSet来保存所有连接的Session对象。

 八.群聊私聊案例

下面是一个简单的示例,展示了如何在WebSocket中实现群聊和私聊功能:

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

@ServerEndpoint("/websocket")
public class ChatWebSocketEndpoint {

    private static Set<Session> sessionSet = new HashSet<>();

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("WebSocket连接已建立");
        sessionSet.add(session);
        try {
            session.getBasicRemote().sendText("你已成功连接到WebSocket服务器");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        String[] messageParts = message.split(":", 2);
        String sender = session.getId();
        
        if (messageParts.length > 1) {  // 私聊
            String receiver = messageParts[0].trim();
            String privateMessage = messageParts[1].trim();
            sendPrivateMessage(sender, receiver, privateMessage);
        } else {  // 群聊
            sendGroupMessage(sender, message);
        }
    }
    
    private void sendGroupMessage(String sender, String message) {
        for (Session session : sessionSet) {
            try {
                session.getBasicRemote().sendText("[" + sender + "]: " + message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    private void sendPrivateMessage(String sender, String receiver, String message) {
        for (Session session : sessionSet) {
            if (session.getId().equals(receiver)) {
                try {
                    session.getBasicRemote().sendText("[私聊]" + sender + ": " + message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return;
            }
        }
        
        try {
            Session senderSession = getSessionById(sender);
            senderSession.getBasicRemote().sendText("私聊对象不存在");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private Session getSessionById(String sessionId) {
        for (Session session : sessionSet) {
            if (session.getId().equals(sessionId)) {
                return session;
            }
        }
        return null;
    }

    @OnClose
    public void onClose(Session session) {
        System.out.println("WebSocket连接已关闭");
        sessionSet.remove(session);
    }

    @OnError
    public void onError(Throwable error, Session session) {
        System.err.println("WebSocket错误:" + error.getMessage());
        sessionSet.remove(session);
    }
}

这个示例中定义了一个WebSocket端点ChatWebSocketEndpoint,使用@ServerEndpoint("/websocket")注解来指定WebSocket访问的路径。在onOpen方法中,当有新的客户端连接时,将其Session对象添加到一个静态的sessionSet中,并向客户端发送连接成功的消息。在onMessage方法中,根据收到的消息内容判断是群聊还是私聊,并调用相应的方法来发送消息。sendGroupMessage方法用于发送群聊消息,遍历所有的连接Session对象,并发送消息到每个会话。sendPrivateMessage方法用于发送私聊消息,找到私聊的接收者的Session对象,发送消息给接收者。如果找不到接收者,向发送者发送一条错误消息。

九.文字和图片消息的处理

 当我们发送的信息包含图片时,可以通过格式进行判断,假如是base64编码的图片,就像下面这样先进行判断,然后在发送给对应的客户端

  @OnMessage
    public void onMessage(String message, Session session) {
        if (message.startsWith("data:image")) { // 判断是否为图片数据
            // 处理图片数据
            byte[] imageData = getImageDataFromMessage(message);
            sendImage(session, imageData);
        } else {
            // 处理字符串数据
            sendText(session, message);
        }
    }

    private byte[] getImageDataFromMessage(String message) {
        // 提取图片数据,具体实现略
        // 这里假设直接从Base64编码中提取图片数据
        String base64Data = message.substring(message.indexOf(",") + 1);
        return javax.xml.bind.DatatypeConverter.parseBase64Binary(base64Data);
    }

    private void sendImage(Session sender, byte[] imageData) {
        for (Session session : sessionSet) {
            if (session != sender) {
                try {
                    session.getBasicRemote().sendBinary(ByteBuffer.wrap(imageData));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void sendText(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
11-29 05:07