Netty 本身并不是遵循 servlet 规范的。Http 是基于请求和响应的无状态协议。Http 1.1 是有 keep-alived 参数的,如果3秒没有返回,则服务端主动关闭了解,Http 1.0 则是请求完成直接返回。
Netty 的连接会被一直保持,我们需要自己去处理这个功能。
在服务端发送完毕数据后,可以在服务端关闭 Channel。
ctx.channel.close(); Netty 能做什么可以当作一个 http 服务器,但是他并没有实现 servelt 规范。虽然 Tomcat 底层本身也使用 NIO,但是 Netty 本身的特点决定了它比 Tomcat 的吞吐量更高。相比于 SpringMVC 等框架,Netty 没提供路由等功能,这也契合和 Netty 的设计思路,它更贴近底层。
Socket 开发,也是应用最为广泛的领域,底层传输的最基础框架,RPC 框架底层多数采用 Netty。直接采用 Http 当然也可以,但是效率就低了很多了。
支持长连接的开发,消息推送,聊天,服务端向客户端推送等等都会采用 WebSocket 协议,就是长连接。
Netty 对 Socket 的实现对于 Http 编程来说,我们实现了服务端就可以了,客户端完全可以使用浏览器或者 CURL 工具来充当。但是对于 Socket 编程来说,客户端也得我们自己实现。
服务器端:
Server 类于上面 Http 服务器那个一样,在 ServerInitoalizer 有一些变化
public class ServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //管道,管道里面可以有很多 handler,一层层过滤的柑橘 ChannelPipeline pipeline = socketChannel.pipeline(); // TCP 粘包 拆包 pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new LengthFieldPrepender(4)); // 字符串编码,解码 pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new ServerHandler()); } } public class ServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(ctx.channel().remoteAddress()+","+msg); ctx.channel().writeAndFlush("from server:" + UUID.randomUUID()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }客户端:
public class Client { public static void main(String[] args) throws InterruptedException { //客户端不需要两个 group,只需要一个就够了,直接连接服务端发送数据就可以了 EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try{ Bootstrap bootstrap = new Bootstrap(); //服务器端既可以使用 handler 也可以使用 childhandler, 客户端一般使用 handler //对于 服务端,handler 是针对 bossgroup的,childhandler 是针对 workergorup 的 bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new ClientInitializer()); ChannelFuture channelFuture = bootstrap.connect("localhost",8899).sync(); channelFuture.channel().closeFuture().sync(); }finally { eventLoopGroup.shutdownGracefully(); } } } public class ClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //管道,管道里面可以有很多 handler,一层层过滤的柑橘 ChannelPipeline pipeline = socketChannel.pipeline(); // TCP 粘包 拆包 pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new LengthFieldPrepender(4)); // 字符串编码,解码 pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new ClientHandler()); } } public class ClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(ctx.channel().remoteAddress()+","+msg); System.out.println("client output:"+ msg); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.channel().writeAndFlush("23123"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } Netty 长连接实现一个聊天室Server 端:
public class ServerHandler extends SimpleChannelInboundHandler<String> { //定义 channel group 来管理所有 channel private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); channelGroup.writeAndFlush("[服务器]-" + channel.remoteAddress() + "加入\n"); channelGroup.add(channel); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); channelGroup.writeAndFlush("[服务器]-" + channel.remoteAddress() + "离开\n"); //这个 channel 会被自动从 channelGroup 里移除 } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); System.out.println(channel.remoteAddress() + "上线"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); System.out.println(channel.remoteAddress() + "离开"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }Client 端:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); for(;;){ channel.writeAndFlush(br.readLine() + "\r\n"); } Netty 心跳