使用Netty框架创建网页聊天
项目搭建
这是我的第一篇博客,本来想写写入门的东西,看了下其他博客大神都写的很多很详细了,这里就不在重复赘述了。因为对通信比较感兴趣,所以这里就netty网络通信框架开始学习了,这个小demo算是我的入门demo吧。
结构介绍
- common 工具类目录;
- config 服务器与客户端配置目录;
- handler 消息处理器;
- Message 消息缓存,用于保存未发送成功的消息,客户端上线则将缓存消息发送出去并将缓存消息删除;
- webapp 前端资源与视图;
源码编写
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>nettyWebSocket</groupId>
<artifactId>com.xyc.netty</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>nettyWebSocket Maven Webapp</name>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.29</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
</dependencies>
</project>
这里引入的netty版本为5.0.0.Alpha1
客户端与netty的配置类
ClientConfig.java
package com.xyc.netty.config;
import java.io.Serializable;
/**
* Created by xycpilot on 2019/6/27.
*/
public class ClientConfig implements Serializable {
private Integer id;
private String name;
private String ip;
private Boolean isOnline;
public ClientConfig(Integer id,String name,String ip,Boolean isOnline) {
super();
this.id = id;
this.name = name;
this.ip = ip;
this.isOnline = isOnline;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Boolean getIsOnline() {
return isOnline;
}
public void setIsOnline(Boolean isOnline) {
this.isOnline = isOnline;
}
}
ClientList.java
package com.xyc.netty.config;
import java.util.HashMap;
import java.util.Map;
/**
* Created by xycpilot on 2019/6/27.
*/
public class ClientList {
private Map<String,ClientConfig> clientMap = new HashMap<String,ClientConfig>();
private static ClientList instance = new ClientList();
public static ClientList getInstance(){
return instance;
}
public void setClientList() {
this.clientMap.put("192.168.51.88",new ClientConfig(1,"张三","192.168.51.88",false));
this.clientMap.put("192.168.51.132",new ClientConfig(2,"李四","192.168.51.132",false));
this.clientMap.put("192.168.51.60",new ClientConfig(3,"王麻子","192.168.51.60",false));
this.clientMap.put("192.168.51.58",new ClientConfig(4,"赵大","192.168.51.58",false));
this.clientMap.put("192.168.11.139",new ClientConfig(5,"刘二","192.168.11.139",false));
}
public Map<String, ClientConfig> getClientMap() {
return clientMap;
}
public void setClientMap(Map<String, ClientConfig> clientMap) {
this.clientMap = clientMap;
}
}
因为是练习所以使用的几个固定内网IP,将IP保存到HashMap中,以IP为key方便后面进行目标查找,初始化的时候所有IP都设置为离线状态,只有等相应IP客户端打开了链接才会设置成上线状态。这里将IP作为唯一依据过于简单,实际应用中可以加入多种参数配置以便确定唯一性,或者以注册账号的形式配置。
NettyConfig.java
package com.xyc.netty.config;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.util.HashMap;
import java.util.Map;
/**
* Created by xycpilot on 2018/12/4.
* 存储整个工程的全局配置
*/
public class NettyConfig {
/*
* 存储每一个客户接入进来时候的channel对象
* */
public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
public static Map<String,ChannelGroup> newChannelMap = new HashMap<String,ChannelGroup>(); //为新连接进来的客户端创建一个分组
}
消息处理器
CheckMessageCache.java
package com.xyc.netty.handler;
import com.xyc.netty.Message.MessageCache;
/**
* Created by xycpilot on 2019/6/27.
*/
public class CheckMessageCache {
/**
* 当客户端连接上线的时候检查是否有未发送出去的消息
* @param clientIp
* @return
*/
public static Boolean checkUnSendMsg(String clientIp){
if(MessageCache.getInstance().getMessageListMap()!= null && MessageCache.getInstance().getMessageListMap().containsKey(clientIp)){
return true;
}else{
return false;
}
}
}
MyWebSocketChannelHandler.java
package com.xyc.netty.handler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* Created by xycpilot on 2018/12/4.
* 初始化连接时候的各个组件
*/
public class MyWebSocketChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel e) throws Exception {
e.pipeline().addLast("http-codec",new HttpServerCodec());
e.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));
e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
e.pipeline().addLast("handler",new MyWebSocketHandler());
}
}
MyWebSocketHandler.java
package com.xyc.netty.handler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyc.netty.Message.MessageCache;
import com.xyc.netty.common.DateUtil;
import com.xyc.netty.common.ToolUitls;
import com.xyc.netty.config.ClientList;
import com.xyc.netty.config.NettyConfig;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent<