Spring MVC+netty socketIO 实现一对一聊天功能

本文介绍如何使用Netty-socket.io在Java应用中实现双向实时通信。通过在Spring环境中配置SocketIOServer,文章详细展示了服务器端的连接、断开连接及消息监听逻辑,并提供了客户端JavaScript代码示例,实现消息的发送与接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先导入netty socketIO包

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

服务器:
新建ServerRunner类实现ApplicationListener确保服务器在spring启动完成后启动,添加@Service标签确保spring可以扫描到。

package com.tomshow.kunya.common.NettySocket;



import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.listener.ExceptionListenerAdapter;
import com.tomshow.kunya.common.chat.Chat;
import com.tomshow.kunya.common.chat.ClientInfo;
import com.tomshow.kunya.common.chat.MessageInfo;
import com.tomshow.kunya.common.util.PropertiesUtil;

import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;


@Service("ServerRunner")
@Slf4j
public class ServerRunner implements ApplicationListener<ContextRefreshedEvent>{
	//会话集合
    private static final ConcurrentSkipListMap<String, ClientInfo> webSocketMap = new ConcurrentSkipListMap<>();
    //静态变量,用来记录当前在线连接数。(原子类、线程安全)
    private static AtomicInteger onlineCount = new AtomicInteger(0);
    private   SocketIOServer server;
    
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		 //端口
		 int WSS_PORT=Integer.parseInt(PropertiesUtil.getProperty("wss.server.port"));
		 //服务器ip
		 String WSS_HOST=PropertiesUtil.getProperty("wss.server.host");	
		 System.out.println("WSS_PORT"+WSS_PORT+"**************************"+WSS_HOST);
		try{
			//避免重复启动
			if( server== null){
				 Configuration config = new Configuration();
				 //服务器ip
				 config.setHostname(WSS_HOST);
			     config.setPort(WSS_PORT);	
			     //该处进行身份验证h
			     config.setAuthorizationListener(handshakeData -> {
			            //http://localhost:8081?username=test&password=test
			            //例如果使用上面的链接进行connect,可以使用如下代码获取用户密码信息
			            //String username = data.getSingleUrlParam("username");
			            //String password = data.getSingleUrlParam("password");
			            return true;
			        });
			     config.setExceptionListener(new ExceptionListenerAdapter(){
			    	 @Override
			    	    public boolean exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
			    		 System.out.println("错误:\n"+e.getMessage());
			    	        ctx.close();			    	        
			    	        return true;
			    	    }
			     });
			     server = new SocketIOServer(config);
			     //添加链接事件监听
			     server.addConnectListener(new ConnectListener() {					
					@Override
					public void onConnect(SocketIOClient client) {
						String clientId = client.getHandshakeData().getSingleUrlParam("clientid");
				        log.info("web socket连接:"+clientId);
				        UUID session = client.getSessionId();
				        ClientInfo si = webSocketMap.get(clientId);
				        // 如果没有连接信息、则新建会话信息
				        if (si == null) {
				            si = new ClientInfo();
				            si.setOnline(true);
				            //在线数加1
				            log.info("socket 建立新连接、sessionId:"+session+"、clientId:"+clientId+"、当前连接数:"+onlineCount.incrementAndGet());
				        }
				        // 更新设置客户端连接信息
				        si.setLeastSignificantBits(session.getLeastSignificantBits());
				        si.setMostSignificantBits(session.getMostSignificantBits());
				        si.setLastConnectedTime(new Date());
				        //将会话信息更新保存至集合中
				        webSocketMap.put(clientId, si);
					}
				});
			     //添加销毁链接事件监听
				server.addDisconnectListener(new DisconnectListener() {					
					@Override
					public void onDisconnect(SocketIOClient client) {
						String clientId = client.getHandshakeData().getSingleUrlParam("clientid");
				        webSocketMap.remove(clientId);
				        //在线数减1
				        log.info("socket 断开连接、sessionId:"+client.getSessionId()+"、clientId:"+clientId+"、当前连接数:"+ onlineCount.decrementAndGet());
						
					}
				});
				//添加发送消息事件监听
				server.addEventListener("message_event", MessageInfo.class,  new DataListener<MessageInfo>(){
					@Override
					public void onData(SocketIOClient client, MessageInfo data, AckRequest ackSender) throws Exception {
				        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
				        String time = simpleDateFormat.format(new Date());
				        Chat chat  = new Chat();
				        chat.setUserSendId(data.getTargetClientId());
				        chat.setUserReceiveId(data.getSourceClientId());
				        chat.setContent(data.getMsg());
				        chat.setCreatetime(time);
				        //这里保存聊天记录
				        String targetClientId = data.getTargetClientId();
				        ClientInfo clientInfo = webSocketMap.get(targetClientId);
				        if (clientInfo != null && clientInfo.isOnline()){
				            UUID target = new UUID(clientInfo.getMostSignificantBits(), clientInfo.getLeastSignificantBits());
				            log.info("目标会话UUID:"+target);
				            MessageInfo sendData = new MessageInfo();
				            sendData.setSourceClientId(data.getSourceClientId());
				            sendData.setTargetClientId(data.getTargetClientId());
				            sendData.setMsg(data.getMsg());
				            // 向当前会话发送信息
				            client.sendEvent("message_event", sendData);
				            // 向目标会话发送信息
				            server.getClient(target).sendEvent("message_event", sendData);
				        }
					}
					
				});
				//需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。				
				server.start();
				System.out.println("start****************************server**************************end");
				}
		}catch(Exception e){
			e.printStackTrace();
		}		
	}

}




实体类:

@Component
@Setter
@Getter
public class Chat {
	/**
     * 发送者用户ID
     */
    private String userSendId;
    /**
     * 接受者用户Id
     */
    private String userReceiveId;
    /**
     * 内容
     */
    private String content;
    /**
     * 创建时间
     */
    private String createtime;
}

/**
 * nettyIo 实体类
 */
@Component
@Getter
@Setter
public class ClientInfo {
    private String clientId;
    private boolean isOnline;
    private long mostSignificantBits;
    private long leastSignificantBits;
    private Date lastConnectedTime;
}
@Component
@Setter
@Getter
public class MessageInfo {
    //源客户端id
    private String sourceClientId;
    //目标客户端id
    private String targetClientId;
    //消息内容
    private String msg;

}

客户端:
引入socket.io.js

<script src="${ctxsta}/common/moment/socket.io.js"></script>

定义连接:

 <script>
        var clientId= '8015',targetId = '5';
        //clientId为本端通话id,避免重复。项目中可用用户id替换
        //targetId 为对端通话id
        var socket = io.connect('http://localhost:8085?clientid=' + clientId);
        socket.on('connect', function () {
            showMsg(':<span class="connect-msg">成功连接到服务器!</span>');
        });
        socket.on('message_event', function (data) {
            showMsg('<br /><span class="username-msg">' + data.sourceClientId + ':</span> ' + data.msg);
        });
        socket.on('disconnect', function () {
            showMsg(':<span class="disconnect-msg">服务已断开!</span>');
        });
        function sendDisconnect() {
            socket.disconnect();
        }
        function sendMessage() {
            var message = $('#msg').val();
            $('#msg').val('');
            var jsonObject = {
                sourceClientId: clientId,
                targetClientId: targetId,
                msg: message
            };
            socket.emit('message_event', jsonObject);
        }
        function showMsg(message) {
            var currentTime = "<span class='time'>" + moment().startOf('hour').fromNow() + "</span>";
            var element = $("<div>" + currentTime + "" + message + "</div>");
            $('#console').append(element);
        }
        $(document).keydown(function (e) {
            if (e.keyCode == 13) {
                $('#send').click();
            }
        });
    </script>   

页面:

<body>
<h1>Netty-socket.io Demo</h1><br/>
<div id="console" class="well"></div>
<form class="well form-inline" onsubmit="return false;">
    <input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>&nbsp;&nbsp;
    <button type="button" onClick="sendMessage()" class="btn" id="send">Send</button>&nbsp;&nbsp;
    <button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
</form>
</body>

完成。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值