当前位置: 首页 > news >正文

8. 好客租房-WebSocket与即时通讯系统[项目必需]

本章节主要是学习WebSocket, 目标快速入门, 能够回答以下问题:

  1. WebSocket和HTTP的区别.

  1. WebSocket使用的是全双工通讯协议, 与其他类似协议有啥区别?

  1. WebSocket中的常用注解有哪些?

通过本章节的学习, 应该可以回答上来这几个问题.

8.1 WebSocket概念快速理解

WebSocket 是HTML5一种新的协议。 主要应用范围为实时通讯相关领域, 比如聊天软件, 实况更新和社交订阅等.

WebSocket 实现了浏览器与服务器全双工通信.

全双工通信就是我们现在的电话, 双方都可以讲话.
半双工通信就是指一个时间段内只有一个动作, 就是以前的对讲机, 同时只能有一个人说话, 说完需要加一个"over", 以便让别人说话.
单工通信更常见, 就是遥控 你的电视遥控只能发,不能收.

8.1.1 为什么HTML5提出WebSocket

思考这样一类问题, 现在有这样一类简单的需求需要你实现:

  • 在网页不刷新的情况下, 搭建一个网页版聊天室;

  • 网页不刷新, 并刷新购物车内的商品个数

  • 网页不刷新, 实时更新朋友的位置.

为什么一定要强调网页不刷新呢? 因为如果网页刷新,你就可以使用HTTP请求那套, 你发起一个请求, 服务器给你一个响应了.但是网页刷新在实时性要求较高的业务中根本没办法满足需求.

所以在HTML5之前. 通常的解决方案如下:

采用轮询的方式。即:通过js不断的请求服务器,查看是否有新数据,如果有,就获取到新数据

这种方案有很明显的缺点: js发出的大部分请求都没有获取到数据更新, 从而对双方机器造成了严重的资源浪费.

所以, 为了提出一个更好的方案, HTML5提出了WebSocket技术.

8.1.2 http与websocket的区别

http协议是短连接,每次请求之后,都会关闭连接,下次重新请求数据,需要再次打开链接.

WebSocket协议是一种长链接,只需要通过一次请求来初始化链接,然后所有的请求和响应都是通过这个TCP链接

进行通讯。

不理解的话,我举个例子.

HTTP就是发微信语音, 每次都需要先找到联系人, 然后按一下那个录音和发送.

WebSocket则与语音通话, 之后所有的对话都通过语音直接进行通讯. (双方只需要控制好发送的内容, 对流量也不会造成太大浪费)

8.2 WebSocket 的Java版demo快速实现

多说无益, 我们开始实战吧.

8.2.1 新建itcast-websocket工程(Maven工程)

首先先引入依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>itcast-websocket2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 我们用的maven的tomcat插件运行, 所以在maven中一定要配置打包方式为为war包 -->
    <packaging>war</packaging>
    <dependencies>
        <!-- websocket所需依赖 -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- 配置Tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8082</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

然后新建一个demo类, 话都在注释里:

package cn.itcast.websocket;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

/**
 * Websocket快速入门demo, 阅读代码前最好理解'WebSocket生命周期概念'
 * 连接->正常收发信息/异常消息->正常收发信息/异常消息->.....->正常收发信息/异常消息->断开连接
 *
 * @author 过道
 */
// @ServerEndpoint 申明这是一个websocket服务, 可以简单类比为Controller注解
@ServerEndpoint("/websocket/{uid}")
public class MyWebsocket {
    // @OnOpen 该注解标识的方法将在建立连接后执行
    // @OnOpen 标识的方法可以接收到session对象,就是客户端与服务端建立的长连接通道
    // @PathParam, 从路径中的{}中读取内容, 与SpringMVC一致.
    @OnOpen
    public void onOpen(Session session, @PathParam("uid") String uid) throws
            IOException {
        // 连接成功, 使用 session 的 api 发送文字
        session.getBasicRemote().sendText(uid + ",你好,欢迎连接WebSocket!");
    }

    // 关闭连接时, 会触发对这个注解@OnClose标识的方法的调用
    @OnClose
    public void onClose() {
        System.out.println(this + "关闭连接");
    }

    /**
     * 该方法用于接收客户端发来的消息
     *
     * @param message 发来的消息数据
     * @param session 会话对象(也是通道)
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println("接收到消息:" + message);
        session.getBasicRemote().sendText("消息已收到.");
    }
}

写完之后, 我们使用maven的tomcat插件运行下项目:

8.2.2 进行测试

打开网页: http://www.easyswoole.com/wstool.html

然后url改为

ws://localhost:8082/websocket/12

如图所示, 点击开始连接, 连接成功后发送就可以了.

8.3 SpringBoot整合WebSocket

为了避免麻烦, 我们直接在8.2的demo项目上改进

pom.xml修改为下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!--spring boot的支持-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>

    <groupId>cn.itcast.websocket</groupId>
    <artifactId>itcast-websocket</artifactId>
    <version>1.0-SNAPSHOT</version>
     <!-- 这里不用管, 我们之后会用boot的main方式启动 -->
    <packaging>war</packaging>

    <dependencies>
        <!--<dependency>-->
            <!--<groupId>javax</groupId>-->
            <!--<artifactId>javaee-api</artifactId>-->
            <!--<version>7.0</version>-->
            <!--<scope>provided</scope>-->
        <!--</dependency>-->
         <!-- 修改为SpringBoot的webstocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- 配置Tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8082</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

8.3.1 快速接入SpringBoot

在Spring中,处理消息的具体业务逻辑需要实现WebSocketHandler接口。

package cn.itcast.websocket.spring;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

/**
 * 在Spring中, 我们需要实现WebSocketHandler接口, 并且需要注册为组件,交给IOC池子维护.
 */
@Component
public class MyHandler extends TextWebSocketHandler {

    /**
     * 这里就是我们demo版本的 @onMessage 哦,每次收到信息都会走这个方法.
     */
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message)
            throws IOException {
        System.out.println("获取到消息 >> " + message.getPayload());

        // 向客户端发送消息
        session.sendMessage(new TextMessage("消息已收到"));
        
		// 如果收到了 '10', 那么给另一方发送0,1,2,3,... 9
        if(message.getPayload().equals("10")){
            for (int i = 0; i < 10; i++) {
                session.sendMessage(new TextMessage("消息 -> " + i));
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 这个就是我们demo版本中的 @OnOpen 了, 建立连接后会调用这个.
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws
            Exception {
        session.sendMessage(new TextMessage(" 你好!欢迎连接到ws服务"));
    }

    /**
     * 这就是demo版本的 @OnClose 注解, 关闭连接后会触发这个.
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
            throws Exception {
        System.out.println("断开连接!");
    }
}

编写配置类

package cn.itcast.websocket.spring;

import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * WebSocket相关配置类
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    // 从IOC池子中取出我们自定义的socket处理器
    @Autowired
    private MyHandler myHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 将webSocket收到的所有路径中带有 '/ws'的交给myHandler处理
        registry.addHandler(myHandler, "/ws").setAllowedOrigins("*");
    }

}

编写启动类

package cn.itcast.websocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

然后运行后打开测试网站http://www.easyswoole.com/wstool.html

8.3.2 websocket拦截器

在Spring中提供了websocket拦截器,可以在建立连接之前写些业务逻辑,比如校验登录等。

新建一个 MyHandshakeInterceptor 拦截器

package cn.itcast.websocket.spring;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

@Component
public class MyHandshakeInterceptor implements HandshakeInterceptor {

    /**
     * 握手之前,若返回false,则不建立链接
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws
            Exception {
        //将用户id放入socket处理器的会话(WebSocketSession)中
        attributes.put("uid", 1001);
        System.out.println("开始握手。。。。。。。");
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Exception exception) {
        System.out.println("握手成功啦。。。。。。");
    }
}

修改WebSocketConfig类注册拦截器

package cn.itcast.websocket.spring;

import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * WebSocket相关配置类
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    // 从IOC池子中取出我们自定义的socket处理器
    @Autowired
    private MyHandler myHandler;

    @Autowired
    private MyHandshakeInterceptor myHandshakeInterceptor;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(this.myHandler, "/ws")
                .setAllowedOrigins("*")
                // 追加注册一个拦截器的代码
                .addInterceptors(this.myHandshakeInterceptor);
    }
}

然后为了证明我们再在MyHandler的afterConnectionEstablished 修改下, 加上对方的uid

    /**
     * 这个就是我们demo版本中的 @OnOpen 了, 建立连接后会调用这个.
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws
            Exception {
        // 获取uid
        Integer uid = (Integer) session.getAttributes().get("uid");
        session.sendMessage(new TextMessage(uid + ", 你好!欢迎连接到ws服务"));
    }

再次启动并测试, 发现ok

8.4 使用WebSocket搭建即时通讯系统

在这里我们不连接数据库, 只做简单的缓存, 之后在其他一章节中连接数据库.

8.4.1 引入依赖

pom.xml文件如下.

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>itcast-haoke-im-webstocket</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

项目结构图如下所示 :

我们先只实现WebSocket相关内容

8.4.2 搭建pojo和对应的DAO层

package cn.itcast.haoke.im.pojo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Message {
    
    private long id;
    // 消息体暂不支持复杂消息.
    private String msg;
    /**
     * 消息状态,1-未读,2-已读
     */
    private Integer status;
    
    // 发送的时间和已读的时间
    private Date sendDate;
    private Date readDate;
    // 发送方和接收方
    private User from;
    private User to;
}

用到的User类代码如下:

package cn.itcast.haoke.im.pojo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
    private Long id;
    private String username;
}

MessageDao如下, 我这里先用HashMap模拟数据库

package cn.itcast.haoke.im.dao;

import cn.itcast.haoke.im.pojo.Message;

import java.util.List;

public interface MessageDAO {

    /**
     * 查询点对点聊天记录
     */
    List<Message> findListByFromAndTo(Long fromId, Long toId, Integer page, Integer
            rows);

    /**
     * 根据id查询数据
     */
    Message findMessageById(Long id);

    /**
     * 新增消息数据*
     * * @param message
     * * @return
     */
    Message saveMessage(Message message);

    void updateMessageState(long id, int i);
}

具体实现如下:

package cn.itcast.haoke.im.dao.impl;

import cn.itcast.haoke.im.dao.MessageDAO;
import cn.itcast.haoke.im.pojo.Message;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 我们在内存中简单实现一个查询类, 之后使用数据库替换.
 */
@Component
public class MessageDAOImpl implements MessageDAO {

    private static AtomicLong ID_BUILDER = new AtomicLong(1L);
    Map<Long, Message> db = new HashMap<>();

    @Override
    public List<Message> findListByFromAndTo(Long fromId, Long toId, Integer page, Integer rows) {
        // 遍历所有信息, 找到满足的信息
        Collection<Message> values = db.values();
        // 先查出所有内容
        List<Message> messageList = new ArrayList<>();
        for (Message value : values) {
            if (value.getFrom() != null && Objects.equals(value.getFrom().getId(), fromId)) {
                if (value.getTo() != null && Objects.equals(value.getTo().getId(), toId)) {
                    messageList.add(value);
                }
            }

        }
        // 处理分页
        return messageList.subList(page * rows, rows);
    }

    @Override
    public Message findMessageById(Long id) {
        return db.get(id);
    }

    @Override
    public Message saveMessage(Message message) {
        // 按照调用顺序生成一个id
        message.setId(ID_BUILDER.getAndIncrement());
        db.put(message.getId(), message);
        return message;
    }
    
    @Override
    public void updateMessageState(long id, int i) {
        Message messageById = findMessageById(id);
        if (messageById != null) {
            // 因为是在map中操作, 所以直接修改状态就可以了.
            messageById.setStatus(i);
        }
    }
}

因为我们用的是假数据, 所以再写一些默认的假数据

package cn.itcast.haoke.im.pojo;

import java.util.HashMap;
import java.util.Map;

public class UserData {
    public static final Map<Long, User> USER_MAP = new HashMap<>();

    static {
        USER_MAP.put(1001L, User.builder().id(1001L).username("zhangsan").build());
        USER_MAP.put(1002L, User.builder().id(1002L).username("lisi").build());
        USER_MAP.put(1003L, User.builder().id(1003L).username("wangwu").build());
        USER_MAP.put(1004L, User.builder().id(1004L).username("zhaoliu").build());
        USER_MAP.put(1005L, User.builder().id(1005L).username("sunqi").build());
    }
}

8.4.3 接入WebSocket

首先, 我们按照之前的demo版流程, 编写一个Handler来处理WebSocket的几个生命周期.

package cn.itcast.haoke.im.websocket;

import cn.itcast.haoke.im.dao.MessageDAO;
import cn.itcast.haoke.im.pojo.Message;
import cn.itcast.haoke.im.pojo.UserData;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.HashMap;
import java.util.Map;

/**
 * webSocket 消息处理器
 *
 * @author 过道
 */
@Component
public class MessageHandler extends TextWebSocketHandler {

    @Autowired
    private MessageDAO messageDAO;

    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 记录所有在线的终端, 并配置唯一标识.
    private static final Map<Long, WebSocketSession> SESSIONS = new HashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        // 将当前用户的session放置到map中,后面会使用相应的session通信
        Long uid = (Long) session.getAttributes().get("uid");
        SESSIONS.put(uid, session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage
            textMessage) throws Exception {
        // 解析消息中的发送方, 接收方, 消息内容.
        Long uid = (Long) session.getAttributes().get("uid");
        JsonNode jsonNode = MAPPER.readTree(textMessage.getPayload());
        Long toId = jsonNode.get("toId").asLong();
        String msg = jsonNode.get("msg").asText();
        Message message = Message.builder()
                // 假装发送用户和接受用户都是从数据库中查出来的
                .from(UserData.USER_MAP.get(uid))
                .to(UserData.USER_MAP.get(toId))
                .msg(msg)
                .build();
        // 存入数据库
        message = this.messageDAO.saveMessage(message);
        // 判断to用户是否在线
        WebSocketSession toSession = SESSIONS.get(toId);
        if (toSession != null && toSession.isOpen()) {
            // 在线且可收消息的话, 实时发送给接收方.

            //TODO 具体格式需要和前端对接
            toSession.sendMessage(new TextMessage(MAPPER.writeValueAsString(message)));
            // 更新消息状态为已读
            this.messageDAO.updateMessageState(message.getId(), 2);
        }
    }
}

为了简单起见,我们直接使用url来传当前用户的id, 格式如下

ws://{服务器url}:{服务器端口}/ws/{当前用户id}
示例如下:
ws://localhost:8080/ws/1002
ws://localhost:8080/ws/1001

约定好id传输格式,我们需要在每个连接建立时, 将uid放入到attributes中, 所以需要用到拦截器

package cn.itcast.haoke.im.websocket;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

@Component
public class MessageHandshakeInterceptor implements HandshakeInterceptor {
    /**
     * 解析路径中的uid, 并放入 attributes 中, 以便之后使用
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws
            Exception {

        String path = request.getURI().getPath();
        String[] ss = StringUtils.split(path, '/');
        if (ss.length != 2) {
            return false;
        }
        if (!StringUtils.isNumeric(ss[1])) {
            return false;
        }
        attributes.put("uid", Long.valueOf(ss[1]));
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Exception exception) {
    }
}

现在, 只需要我们将我们自定义的拦截器和Handler配置到Spring中, 让Spring将接收到的请求转发给我们就ok了

package cn.itcast.haoke.im.websocket;

import org.springframework.beans.factory.annotation.Autowired;
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 {
    @Autowired
    private MessageHandler messageHandler;
    @Autowired
    private MessageHandshakeInterceptor messageHandshakeInterceptor;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(this.messageHandler, "/ws/{uid}")
                .setAllowedOrigins("*")
                .addInterceptors(this.messageHandshakeInterceptor);
    }
}

8.4.4 编写启动类并测试

package cn.itcast.haoke.im;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ImApplication {
    public static void main(String[] args) {
        SpringApplication.run(ImApplication.class, args);
    }
}

启动项目, 启动成功;

这次还是打开在线测试工具 http://www.easyswoole.com/wstool.html

注意要打开两份

一份的url : ws://localhost:8080/ws/1001
另一份的url: ws://localhost:8080/ws/1002

点击连接, 都连接成功.

我们打开url是 1001的页面, 发送内容

{
"toId":1002,
"msg" : "你好, 1002"
}

然后看一眼url为1002的页面, 确实收到了内容. 如此测试就可以了.

相关文章:

  • 机器学习04 决策树
  • java基础学习 day37 (集合)
  • Python闭包与闭包陷阱
  • 测试篇(三):测试用例的万能公式、对水杯和登录页面设计测试用例、测试用例的设计方法
  • 第十三届蓝桥杯省赛 Java A 组 I 题、Python A 组 I 题、Python B 组 J 题——最优清零方案(AC)
  • 阿里“云开发“小程序(uniCould)
  • 提权漏洞和域渗透历史漏洞整理
  • 传参的理解
  • 基于蜣螂算法的极限学习机(ELM)分类算法-附代码
  • 主流的操作系统(带你快速了解)
  • 六、numpy拷贝
  • STM32+python产生三角波
  • 【计算机网络(考研版)】第一站:计算机网络概述(一)
  • C++空间命名
  • 树,堆,二叉树的认识
  • 《Java编程思想》读书笔记-对象导论
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 11111111
  • bearychat的java client
  • Codepen 每日精选(2018-3-25)
  • golang中接口赋值与方法集
  • Hibernate最全面试题
  • JavaScript新鲜事·第5期
  • java多线程
  • JS题目及答案整理
  • Netty源码解析1-Buffer
  • REST架构的思考
  • Swoft 源码剖析 - 代码自动更新机制
  • Xmanager 远程桌面 CentOS 7
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 搭建gitbook 和 访问权限认证
  • 计算机常识 - 收藏集 - 掘金
  • 爬虫模拟登陆 SegmentFault
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 数据科学 第 3 章 11 字符串处理
  • elasticsearch-head插件安装
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • ​linux启动进程的方式
  • #pragma once与条件编译
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (3)(3.5) 遥测无线电区域条例
  • (4.10~4.16)
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (libusb) usb口自动刷新
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (原)Matlab的svmtrain和svmclassify
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • **PHP分步表单提交思路(分页表单提交)
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .net framework4与其client profile版本的区别
  • .Net 中Partitioner static与dynamic的性能对比
  • .net下简单快捷的数值高低位切换
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作