目录
1.目的
- 学习和了解Netty的应用场景和使用方式
2.Netty是什么
- Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。
2.1.Netty和Tomcat的区别
- Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的。Netty能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能。
2.2.Netty为什么流行
2.2.1并发高
- 对比于BIO(Blocking I/O,阻塞IO),Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架。当一个连接建立之后,NIO有两个步骤要做(接收完客户端发过来的全部数据、处理完请求业务之后返回response给客户端),NIO和BIO的区别主要是在第一步。在BIO中,等待客户端发数据这个过程是阻塞的,一个线程只能处理一个请求而最大线程数是有限的,BIO不能支持高并发。
- 而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接收这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是不阻塞的。
2.2.2.传输快
- 依赖NIO的一个特性——零拷贝。Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点。针对这种情况,当Netty需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。
2.2.3.封装好
3.基于Netty实现WebSocket聊天室
3.1创建simple_webchat项目
3.2编写代码
- pom.xml
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.23</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies>
- Server
public interface Server {
void start();
void shutdown();
}
- BaseServer
public abstract class BaseServer implements Server{
protected Logger logger = LoggerFactory.getLogger(getClass());
protected String host = "localhost";
protected int port = 8099;
/**
* 1. NioEventLoopGroup是用来处理I/O操作的多线程事件循环器,
* 在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。
* 第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,
* 用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上
*/
protected DefaultEventLoopGroup defLoopGroup;
protected NioEventLoopGroup bossGroup;
protected NioEventLoopGroup workGroup;
protected NioServerSocketChannel ssch;
protected ChannelFuture cf;
/**
* 2.ServerBootstrap是一个启动 NIO 服务的辅助启动类。
* 你可以在这个服务中直接使用 Channel
*/
protected ServerBootstrap b;
public void init(){
defLoopGroup = new DefaultEventLoopGroup(8, new ThreadFactory() {
private AtomicInteger index = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "DEFAULTEVENTLOOPGROUP_" + index.incrementAndGet());
}
});
bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
private AtomicInteger index = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "BOSS_" + index.incrementAndGet());
}
});
workGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 10, new ThreadFactory() {
private AtomicInteger index = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "WORK_" + index.incrementAndGet());
}
});
b = new ServerBootstrap();
}
@Override
public void shutdown() {
if (defLoopGroup != null) {
defLoopGroup.shutdownGracefully();
}
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
- UserInfo
public class UserInfo {
private static AtomicInteger uidGener = new AtomicInteger(1000);
private boolean isAuth = false; // 是否认证
private long time = 0; // 登录时间
private int userId; // UID
private String nick; // 昵称
private String addr; // 地址
private Channel channel;// 通道
//其他get和set方法直接生成就行
public void setUserId() {
this.userId = uidGener.incrementAndGet();
}
- MessageHandler
/**
* 1.SimpleChannelInboundHandler实现了ChannelInboundHandler接口,
* 提供了许多事件处理的接口方法,然后你可以覆盖这些方法
*/
public class MessageHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class);
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame)
throws Exception {
UserInfo userInfo = UserInfoManager.getUserInfo(ctx.channel());
if (userInfo != null && userInfo.isAuth()) {
JSONObject json = JSONObject.parseObject(frame.text());
// 广播返回用户发送的消息文本
UserInfoManager.broadcastMess(userInfo.getUserId(), userInfo.getNick(), json.getString("mess"));
}
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
UserInfoManager.removeChannel(ctx.channel());
UserInfoManager.broadCastInfo(ChatCode.SYS_USER_COUNT,UserInfoManager.getAuthUserCount());
super.channelUnregistered(ctx);
}
}
- UserAuthHandler
/**
* 1.SimpleChannelInboundHandler实现了ChannelInboundHandler接口,
* 提供了许多事件处理的接口方法,然后你可以覆盖这些方法
*/
public class UserAuthHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger logger = LoggerFactory.getLogger(UserAuthHandler.class);
private WebSocketServerHandshaker handshaker;
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
handleWebSocket(ctx, (WebSocketFrame) msg);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent evnet = (IdleStateEvent) evt;
// 判断Channel是否读空闲, 读空闲时移除Channel
if (evnet.state().equals(IdleState.READER_IDLE)) {
final String remoteAddress = NettyUtil.parseChannelRemoteAddr(ctx.channel());
logger.warn("NETTY SERVER PIPELINE: IDLE exception [{}]"