netty-socketio即时聊天包括文本、emoji表情、文件处理

netty-socketio是一个开源的Socket.io服务器端的一个java的实现,它基于Netty框架。项目地址为:https://github.com/mrniko/netty-socketio

最主要:服务端的主要工作,就是添加各种事件的监听,然后在监听中,做出相应的处理。

代码说明:
1.本代码不能完全copy然后执行,只是放出了关键代码,需要对socketio有一定的理解;
2.有聊天emoji表情的处理功能;
3.有文件的上传功能:我这里把文件分为图片和非图片文件,因为图片文件要实时展示出来的。

引入maven依赖:

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

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

@Service("ServerRunner")
public class ServerRunner implements ApplicationListener<ContextRefreshedEvent>{
	private static final Logger logger = LoggerFactory.getLogger(ServerRunner.class);	
	@Value("${server.host}")
	private String host ;
	@Value("${server.port}")
	private Integer port;
	@Value("${fileUrl}")  
	private String fileUrl;
	private EmojiConverter emojiConverter = EmojiConverter.getInstance();

	//会话集合
	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=port;
		//服务器ip
		String WSS_HOST=host;	
		System.out.println("WSS_PORT"+WSS_PORT+"**************************"+WSS_HOST);
		//避免重复启动
		if( server== null){
			Configuration config = new Configuration();
			//服务器ip
			config.setHostname(WSS_HOST);
			config.setPort(WSS_PORT);	
			config.setMaxFramePayloadLength(1024 * 1024);//防止上传文件超过默认值报错
			config.setMaxHttpContentLength(1024 * 1024);
			//该处进行身份验证
			config.setAuthorizationListener(handshakeData -> {return true;});
			//异常
			config.setExceptionListener(new ExceptionListenerAdapter(){
				@Override
				public boolean exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
					logger.debug("错误:"+e.getMessage());
					ctx.close();			    	        
					return true;
				}
			});
			server = new SocketIOServer(config);
			//添加链接事件监听
			server.addConnectListener(new ConnectListener() {					
				@Override
				public void onConnect(SocketIOClient client) {
					logger.debug("socket 建立新连接");
					...
				}
			});
			//添加销毁链接事件监听
			server.addDisconnectListener(new DisconnectListener() {					
				@Override
				public void onDisconnect(SocketIOClient client) {
					logger.debug("socket 断开连接");
					...
				}
			});
			//添加发送消息事件监听
			server.addEventListener("messageEvent", Message.class,  new DataListener<Message>(){
				@Override
				public void onData(SocketIOClient client, Message data, AckRequest ackSender)  {
					SendData sendData = new SendData();//发送的消息
					try{
						Message chat  = new Message();//存入数据库
						Integer userId = data.getUserId();
						String filePath = "/"+userId+"/";
						String fileRandomName = null;
						String format =null;
						ClientInfo si = webSocketMap.get(userId);
						if (si != null) {
							String result = null;
							byte[] fileByte = data.getFileByte();//接收客户端传来的文件字节数组
							String fileName = data.getFileName();//接收客户的传来的文件名称
							Integer msgType = data.getMsgType();//接收客户的传来的文件类型
							Integer materialId = null;
							if(msgType==1){//1文字
								result = emojiConverter.toAlias(data.getMsgContent());//对emoji表情进行转入,否则存入mysql数据库将报错
							}else if(msgType==2||msgType==3){//2图片和3文件
								if(fileByte!=null){
									//这里保存文件	
									format = fileName.substring(fileName.lastIndexOf("."));
										fileRandomName = System.currentTimeMillis()+format;
										FileUtil.getFile(fileByte, fileUrl+filePath, fileRandomName);//工具方法:将文件字节数组转换成文件保存到本地
										//保存到Materail,自己创建的实体类
										Material material = new Material();
										material.setFileName(fileName);
										material.setFilePath(filePath+fileRandomName);
										material.setFormat(format);
										ReturnInfo info = materialService.createMaterial(material);//保存文件信息到数据库
										if(!info.getSuccess()){
											logger.debug("保存文件失败:"+info.getMessage());
										}else {
											materialId = (Integer)info.getData();
										}							
								}
							}
							chat.setCreater(userId);
							chat.setMsgContent(result);
							chat.setMsgType(msgType);
							chat.setMaterialId(materialId);
							//这里保存聊天记录
							Integer sendStatus = 2;//发送留言的状态,2未发送
							SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
							String msgDate = simpleDateFormat.format(new Date());
							sendData.setCreateDate(new Date());
							sendData.setMsgContent(result);
							sendData.setCreater(userId);
							sendData.setMsgType(msgType);
							if(fileByte!=null){
								sendData.setFormat(format);
								sendData.setFileName(fileName);
								sendData.setFileByte(fileByte);
								sendData.setFilePath(filePath+fileRandomName);
							}
							List<Integer> userIds = data.getUserIds();//客户端传来的要转发给其他的用户的Id
							if(userIds!=null){
								for(Integer targetClientId : userIds){
									if(targetClientId!=userId){
										ClientInfo clientInfo = webSocketMap.get(targetClientId);
										if (clientInfo != null && clientInfo.isOnline()){//当前在线
											UUID target = new UUID(clientInfo.getMostSignificantBits(), clientInfo.getLeastSignificantBits());
											// 向目标会话发送信息
											if(server.getClient(target)!=null){
												server.getClient(target).sendEvent("messageEvent", sendData);
											}
											sendStatus=1;//发送留言成功											
										}else{//不在线
											sendStatus=2;//未发送
										}
										//对留言发送成功或失败的处理
									}
								}
							}
						}
					}catch(Exception e){
						e.printStackTrace();
						logger.debug("发送留言异常:"+e.getMessage());
						sendData.setSuccess(false);
						sendData.setMsgContent("发送留言异常");
					}
					// 向当前会话发送信息
					client.sendEvent("messageEvent", sendData);
				}
			});
		}
	}
}
public class Message {

    private List<Integer> userIds;
    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createDate;

    private Integer msgType;

    private Integer materialId;

    private String msgContent;
    
    private String createrFullname;
    
    private String filePath;
    
    private Integer userId;//在接收聊天消息时用到,等于creater字段
    
    private byte[] fileByte;
    
    private String fileName;
    
    private String format;
    //getter and setter
}
public class SendData extends Message {
	private Boolean success;
	private String msgDate;
	private String message;
	//getter and setter
}
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtil {
	/** 
     * 根据byte数组,生成文件 
     */  
    public static void getFile(byte[] bfile, String filePath,String fileName) {  
        BufferedOutputStream bos = null;  
        FileOutputStream fos = null;  
        File file = null;  
        createFolderIfNotExists(filePath);
        try {  
            file = new File(filePath+"\\"+fileName);  
            fos = new FileOutputStream(file);  
            bos = new BufferedOutputStream(fos);  
            bos.write(bfile);  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if (bos != null) {  
                try {  
                    bos.close();  
                } catch (IOException e1) {  
                    e1.printStackTrace();  
                }  
            }  
            if (fos != null) {  
                try {  
                    fos.close();  
                } catch (IOException e1) {  
                    e1.printStackTrace();  
                }  
            }  
        }  
    } 
    public static void createFolderIfNotExists(String dirName)
			throws SecurityException {
		File theDir = new File(dirName);
		mkDir(theDir);
	}

	public static void mkDir(File file) {
		if (file.getParentFile().exists()) {
			file.mkdir();
		} else {
			mkDir(file.getParentFile());
			file.mkdir();
		}
	}
}

emoji表情报错参考:https://blog.csdn.net/qq_23888451/article/details/84638365

另外,该功能是在这篇文章上,做了些改进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值