SpringBoot集成SocketIO

一.快速入门

1.导依赖

        <!-- socket.io依赖-->
        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>1.7.7</version>
        </dependency>

        <dependency>
            <groupId>io.socket</groupId>
            <artifactId>socket.io-client</artifactId>
            <version>1.0.0</version>
        </dependency>

2.在配置文件中做一些配置

这个配置写在服务端,客户端不用写,主要是一些socket.io的配置信息。

#socket.io配置
socketio.host=127.0.0.1(别写localhost,写服务器的ip)
socketio.port=9999
# 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
socketio.maxFramePayloadLength=1048576
# 设置http交互最大内容长度
socketio.maxHttpContentLength=1048576
# socket连接数大小(如只监听一个端口boss线程组为1即可)
socketio.bossCount=1
socketio.workCount=100
socketio.allowCustomRequests=true
# 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间
socketio.upgradeTimeout=1000000
# Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
socketio.pingTimeout=6000000
# Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔
socketio.pingInterval=25000

3.用上面配置文件中的信息创建Bean对象(服务端)

package com.guguo.config;

import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SocketIOConfig {

    @Value("${socketio.host}")
    private String host;

    @Value("${socketio.port}")
    private Integer port;

    @Value("${socketio.bossCount}")
    private int bossCount;

    @Value("${socketio.workCount}")
    private int workCount;

    @Value("${socketio.allowCustomRequests}")
    private boolean allowCustomRequests;

    @Value("${socketio.upgradeTimeout}")
    private int upgradeTimeout;

    @Value("${socketio.pingTimeout}")
    private int pingTimeout;

    @Value("${socketio.pingInterval}")
    private int pingInterval;

    @Bean
    public SocketIOServer socketIOServer() {
        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        config.setSocketConfig(socketConfig);
        config.setHostname(host);
        config.setPort(port);
        config.setBossThreads(bossCount);
        config.setWorkerThreads(workCount);
        config.setAllowCustomRequests(allowCustomRequests);
        config.setUpgradeTimeout(upgradeTimeout);
        config.setPingTimeout(pingTimeout);
        config.setPingInterval(pingInterval);
        return new SocketIOServer(config);
    }

    //这个对象是用来扫描socketio的注解,比如 @OnConnect、@OnEvent
    @Bean
    public SpringAnnotationScanner springAnnotationScanner() {
        return new SpringAnnotationScanner(socketIOServer());
    }
}

4.写Handler,里面包含了处理基本事件的方法(服务端)


import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


@Slf4j
@Component
public class SocketIOHandler {
    @Autowired
    private SocketIOServer socketIoServer;//服务端对象

    //Spring IoC容器创建之后,在加载SocketIOService Bean之后启动
    //服务端启动
    @PostConstruct
    private void autoStartup() throws Exception {
        try {
            socketIoServer.start();
        }catch (Exception ex){
            ex.printStackTrace();
            log.error("SocketIOServer启动失败");
        }
    }

    //Spring IoC容器在销毁SocketIOService Bean之前关闭,避免重启项目服务端口占用问题
    //服务端关闭
    @PreDestroy
    private void autoStop() throws Exception {
        socketIoServer.stop();
    }

    //客户端连接的时候触发
    @OnConnect
    public void onConnect(SocketIOClient client) {
        String username = client.getHandshakeData().getSingleUrlParam("userName");
        SocketIOService.clientMap.put(username,client);
        //客户端:/127.0.0.1:64540  sessionId:9acac9ab-a000-43a7-bf97-31fac791e4f4 username: 张三已连接
        log.info("客户端:" + client.getRemoteAddress() + "  sessionId:" + client.getSessionId() +" username: "+ username+ "已连接");
    }

    //客户端关闭连接时触发
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        //客户端:9acac9ab-a000-43a7-bf97-31fac791e4f4断开连接
        log.info("客户端:" + client.getSessionId() + "断开连接");

    }

}

5.客户端测试,Controller层代码:

import com.itheima.boot.service.SocketIOClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
public class SocketIOClientController {
    @Autowired
    private SocketIOClientService socketIOClientService;
    //测试和服务端的连接
    @GetMapping("/testConnect")
    public void testConnect() throws IOException {
        socketIOClientService.sendMessage("张三");
    }
}

6.客户端测试,Service层代码:

package com.itheima.boot.service;

import io.socket.client.IO;
import io.socket.client.Socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class SocketIOClientService{
    public void sendMessage(String name) {
        // 服务端socket.io连接通信地址
        String url = "http://127.0.0.1:9999?userName="+name;
        System.out.println("lalalala");
        try {
            IO.Options options = new IO.Options();
            options.transports = new String[]{"websocket"};
            //失败后重试次数
            options.reconnectionAttempts = 2;
            // 失败重连的时间间隔
            options.reconnectionDelay = 1000;
            // 连接超时时间(ms)
            options.timeout = 500;

            final Socket socket = IO.socket(url, options);

            socket.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

7.结果

二.服务端发数据,客户端接收

1.服务端Controller代码

package com.guguo.controller;

import com.guguo.service.SocketIOService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SocketIOController {
    @Autowired
    private SocketIOService socketIOService;

    @GetMapping("/sendMessage")
    public void sendMessage(String message){
        socketIOService.sendMessageToAllUsers(message);
    }
    
}

2.服务端Service代码

import com.corundumstudio.socketio.SocketIOClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class SocketIOService {
    //使用ConcurrentMap来存储客户端
    public static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();

    //给每个事件进行定义,用于客户端和服务端之间的通信
    private static final String TEST_EVENT = "test_event";

    //给容器内所有的客户端发送信息
    public void sendMessageToAllUsers(String message){
        if(clientMap.isEmpty()){
            return;
        }
        for(String key:clientMap.keySet()) {
            SocketIOClient socketIOClient = clientMap.get(key);
            socketIOClient.sendEvent(TEST_EVENT,message);
        }
    }

}

3.客户端接收代码

package com.itheima.boot.service;


import io.socket.client.IO;
import io.socket.client.Socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class SocketIOClientService{
    public void sendMessage(String name) {
        // 服务端socket.io连接通信地址
        String url = "http://127.0.0.1:9999?userName="+name;
        try {
            IO.Options options = new IO.Options();
            options.transports = new String[]{"websocket"};
            //失败后重试次数
            options.reconnectionAttempts = 2;
            // 失败重连的时间间隔
            options.reconnectionDelay = 1000;
            // 连接超时时间(ms)
            options.timeout = 500;
            final Socket socket = IO.socket(url, options);

            //客户端监听某个事件,这个是系统定义的事件(链接事件)
            socket.on(Socket.EVENT_CONNECT, message -> System.out.println("连接成功"));

            // 自定义事件`test_event` -> 接收服务端成功连接消息
            socket.on("test_event", objects -> System.out.println(objects[0].toString()));
            socket.connect();

            while (true) {
                Thread.sleep(4000);
                // 向服务端发送消息
                socket.emit("push_data_event", "发送数据 " + "oejfjefwfwefw");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

4.注意问题和结果展示

服务端的逻辑是,向所有已经连接的客户端发送数据。所以,客户端要先向服务端发送请求连接,连接之后服务端将该客户端存到本地,然后再执行发送数据的逻辑。

客户端控制台:

 服务端控制台:

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值