netty-socketio即時聊天包括文字、emoji表情、檔案處理
阿新 • • 發佈:2018-12-06
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){
//這裡儲存檔案
}
}
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
}
emoji表情報錯參考:https://blog.csdn.net/qq_23888451/article/details/84638365
另外,該功能是在這篇文章上,做了些改進。