netty-socketio即时通讯

1,033 阅读1分钟

简介

netty-socketio是一个开源的Socket.io服务器端的一个java的实现,它基于Netty框架。socket.io是一个跨浏览器使用websocket为实时实时应用提供服务。

**github:**github.com/mrniko/nett…

maven

<dependency>
    <groupId>com.corundumstudio.socketio</groupId>
    <artifactId>netty-socketio</artifactId>
    <version>2.0.6</version>
</dependency>

@Component
public class MessagePushConfig implements InitializingBean {
	
    private static final Logger logger = LoggerFactory.getLogger(MessagePushConfig.class);
	
    @Resource
    private EventListenner eventListenner;
    
    @Value("${socketio.port}")
    private int port;

    @Autowired
    private SocketIOServer socketIOServer;

    @Override
    public void afterPropertiesSet() throws Exception {
        socketIOServer.start();
        logger.info("启动正常");
    }

    @Bean
    public SocketIOServer socketIOServer() {
        Configuration config = new Configuration();
        config.setPort(port);

        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setReuseAddress(true);
        socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        config.setSocketConfig(socketConfig);
		config.setExceptionListener(new ExceptionListenerAdapter(){
			@Override
			public boolean exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
				logger.debug("错误:"+e.getMessage());
				ctx.close();			    	        
				return true;
			}
		});
        SocketIOServer server = new SocketIOServer(config);
        server.addListeners(eventListenner);
        return server;
    }
}

@Component
public class EventListenner {
	
    private static final Logger logger = LoggerFactory.getLogger(EventListenner.class);
	
    @Resource
    private ClientCache clientCache;
    
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 客户端连接
     *
     * @param client
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        if(!StringUtils.isEmpty(userId)) {
        	UUID sessionId = client.getSessionId();
            clientCache.saveClient(userId, sessionId, client);
            logger.info("建立连接:" + userId);
        }
    }

    /**
     * 客户端断开
     *
     * @param client
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        clientCache.deleteSessionClient(userId, client.getSessionId());
        client.disconnect();
        logger.info("关闭连接:" + userId);
    }

    /**
     * 监听客户端事件chat
     *
     * @param client   客户端信息
     * @param request   请求信息
     * @param data     客户端发送数据
     */
    @OnEvent("chat")
    public void onEevent(SocketIOClient client, AckRequest request, ChatMessage message) {
    	String content = JSON.toJSONString(message);
    	redisTemplate.convertAndSend("chat", content);
    }
    
    /**
     * 监听客户端事件room
     *
     * @param client   客户端信息
     * @param request   请求信息
     * @param data     客户端发送数据
     */
    @OnEvent("room")
    public void room(SocketIOClient client, AckRequest request, ChatMessage message) {
    	String content = JSON.toJSONString(message);
    	redisTemplate.convertAndSend("room", content);
    }
    
}

@Component
public class ClientCache {

    /**
     * 本地缓存
     */
    private static Map<String, HashMap<UUID, SocketIOClient>> concurrentHashMap = new ConcurrentHashMap<>();

    /**
     * 存入本地缓存
     *
     * @param userId         用户ID
     * @param sessionId      页面sessionID
     * @param socketIOClient 页面对应的通道连接信息
     */
    public void saveClient(String userId, UUID sessionId, SocketIOClient socketIOClient) {
        HashMap<UUID, SocketIOClient> sessionIdClientCache = concurrentHashMap.get(userId);
        if (sessionIdClientCache == null) {
            sessionIdClientCache = new HashMap<>();
        }
        sessionIdClientCache.put(sessionId, socketIOClient);
        concurrentHashMap.put(userId, sessionIdClientCache);
    }

    /**
     * 根据用户ID获取所有通道信息
     *
     * @param userId
     * @return
     */
    public HashMap<UUID, SocketIOClient> getUserClient(String userId) {
        return concurrentHashMap.get(userId);
    }

    /**
     * 根据用户ID及页面sessionID删除页面链接信息
     *
     * @param userId
     * @param sessionId
     */
    public void deleteSessionClient(String userId, UUID sessionId) {
        concurrentHashMap.get(userId).remove(sessionId);
        if(concurrentHashMap.get(userId).isEmpty()) {
        	concurrentHashMap.remove(userId);
        }
    }
    
    /**
     * 获取所有用户数量
     *
     * @return
     */
    public long getUserNumber() {
        return concurrentHashMap.size();
    }
}


@Component
public class RedisSub implements MessageListener {
	
    @Resource
    private ClientCache clientCache;
    
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
    	String msgString = (String) redisTemplate.getValueSerializer().deserialize(message.getBody());
      socketIOClient.sendEvent("chat", msgString);
    }
}


npm install socket.io-client@2.2.0

import io from 'socket.io-client' 
socket = io.connect(window.platform.socketioUrl + '?userId=' + localStorage.getItem('userId'))
//监听服务器连接事件
socket.on('connect', () => {
    console.log('connect')
})
//监听服务器关闭服务事件
socket.on('disconnect', () => {
    console.log('disconnect')
})
//连接失败
socket.on('connect_error', err => {
    console.log('connect_error:' + err.message)
})
//监听服务器端发送消息事件
socket.on('chat', data => {

})
//发送消息事件
socket.emit('room', event)