1.danmu:
/** * 处理 Http 请求 */ public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1 private final String wsUri; private static final File INDEX; static { URL location = HttpRequestHandler.class.getProtectionDomain().getCodeSource().getLocation(); try { String path = location.toURI() + "WebsocketDanMu.html"; path = !path.contains("file:") ? path : path.substring(5); INDEX = new File(path); } catch (URISyntaxException e) { throw new IllegalStateException("Unable to locate WebsocketChatClient.html", e); } } public HttpRequestHandler(String wsUri) { this.wsUri = wsUri; } @Override public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { if (wsUri.equalsIgnoreCase(request.getUri())) { ctx.fireChannelRead(request.retain()); } else { if (HttpHeaders.is100ContinueExpected(request)) { send100Continue(ctx); } RandomAccessFile file = new RandomAccessFile(INDEX, "r"); HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK); response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8"); boolean keepAlive = HttpHeaders.isKeepAlive(request); if (keepAlive) { response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length()); response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } ctx.write(response); if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length())); } else { ctx.write(new ChunkedNioFile(file.getChannel())); } ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!keepAlive) { future.addListener(ChannelFutureListener.CLOSE); } file.close(); } } private static void send100Continue(ChannelHandlerContext ctx) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); ctx.writeAndFlush(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { Channel incoming = ctx.channel(); System.out.println("Client:"+incoming.remoteAddress()+"异常"); // 当出现异常就关闭连接 cause.printStackTrace(); ctx.close(); } }
2.
** * 处理TextWebSocketFrame * */ public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // (1) Channel incoming = ctx.channel(); for (Channel channel : channels) { if (channel != incoming){ channel.writeAndFlush(new TextWebSocketFrame(msg.text())); } else { channel.writeAndFlush(new TextWebSocketFrame("我发送的"+msg.text() )); } } } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2) Channel incoming = ctx.channel(); // Broadcast a message to multiple Channels channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入")); channels.add(incoming); System.out.println("Client:"+incoming.remoteAddress() +"加入"); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3) Channel incoming = ctx.channel(); // Broadcast a message to multiple Channels channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开")); System.err.println("Client:"+incoming.remoteAddress() +"离开"); // A closed Channel is automatically removed from ChannelGroup, // so there is no need to do "channels.remove(ctx.channel());" } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5) Channel incoming = ctx.channel(); System.out.println("Client:"+incoming.remoteAddress()+"在线"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6) Channel incoming = ctx.channel(); System.err.println("Client:"+incoming.remoteAddress()+"掉线"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) // (7) throws Exception { Channel incoming = ctx.channel(); System.err.println("Client:"+incoming.remoteAddress()+"异常"); // 当出现异常就关闭连接 cause.printStackTrace(); ctx.close(); } }
3.
/** * Websocket 聊天服务器-服务端 * */ public class WebsocketDanmuServer { private int port; public WebsocketDanmuServer(int port) { this.port = port; } public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(2); // (1) EventLoopGroup workerGroup = new NioEventLoopGroup(3); try { ServerBootstrap b = new ServerBootstrap(); // (2) b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // (3) .childHandler(new WebsocketDanmuServerInitializer()) //(4) .option(ChannelOption.SO_BACKLOG, 128) // (5) .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) System.out.println("SnakeGameServer 启动了" + port); // 绑定端口,开始接收进来的连接 ChannelFuture f = b.bind(port).sync(); // (7) // 等待服务器 socket 关闭 。 // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); System.out.println("SnakeGameServer 关闭了"); } } public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } new WebsocketDanmuServer(port).run(); } }
4.
/** * 服务端 ChannelInitializer * */ public class WebsocketDanmuServerInitializer extends ChannelInitializer<SocketChannel> { //1 @Override public void initChannel(SocketChannel ch) throws Exception {//2 ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("http-decodec",new HttpRequestDecoder()); pipeline.addLast("http-aggregator",new HttpObjectAggregator(65536)); pipeline.addLast("http-encodec",new HttpResponseEncoder()); pipeline.addLast("http-chunked",new ChunkedWriteHandler()); /* pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(64*1024)); pipeline.addLast(new ChunkedWriteHandler()); */ pipeline.addLast("http-request",new HttpRequestHandler("/ws")); pipeline.addLast("WebSocket-protocol",new WebSocketServerProtocolHandler("/ws")); pipeline.addLast("WebSocket-request",new TextWebSocketFrameHandler()); } }
二。基于netty 的httpdemo:
package com.tuling.netty.http_demo; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.*; /** * 1、初始化Bootstrap (连接) * 2、初始化pipeline(编解码) * 3、业务处理 * Created by Tommy on 2018/1/23. */ public class HttpServer { /** * */ public void openServer() { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.channel(NioServerSocketChannel.class); EventLoopGroup boot = new NioEventLoopGroup(1); EventLoopGroup work = new NioEventLoopGroup(8); bootstrap.group(boot, work); bootstrap.childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast("http-decode", new HttpRequestDecoder());//解码request 1 ch.pipeline().addLast("http-encode", new HttpResponseEncoder());// 编码response 3 ch.pipeline().addLast("http-server-handler", new HttpServerHandler()); // 业务处理2 } }); try { ChannelFuture f = bootstrap.bind(8080).sync(); System.out.println("服务启动成功:8080"); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { boot.shutdownGracefully(); work.shutdownGracefully(); } } private static class HttpServerHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=utf-8"); String src = "<!DOCTYPE html>\n" + "<html lang=\"en\">\n" + "<head>\n" + " <meta charset=\"UTF-8\">\n" + " <title>hello word</title>\n" + "</head>\n" + "<body>\n" + "hello word\n" + "</body>\n" + "</html>"; response.content().writeBytes(src.getBytes("UTF-8")); ChannelFuture f = ctx.writeAndFlush(response); ChannelFuture f2=ctx.channel().writeAndFlush(response); f.addListener(ChannelFutureListener.CLOSE); } } public static void main(String[] args) { HttpServer server = new HttpServer(); server.openServer(); } }
对应的Server文件:
public class HttpSimpleServer { //open 启动服务 public void openServer() { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.channel(NioServerSocketChannel.class); EventLoopGroup boss = new NioEventLoopGroup(1); EventLoopGroup work = new NioEventLoopGroup(8); bootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { ch.pipeline().addLast("http-decoder", new HttpRequestDecoder()); ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536)); ch.pipeline().addLast("http-encoder", new HttpResponseEncoder()); ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler()); ch.pipeline().addLast("http-server", new HttpServerHandler()); ch.pipeline().addLast("WebSocket-protocol", new WebSocketServerProtocolHandler("/ws")); // 封装了编码和解码操作 ch.pipeline().addLast("WebSocket-handler", new WebSocketServerHandler());// 处理业务 } }); bootstrap.group(boss, work); try { ChannelFuture future = bootstrap.bind(8080).sync(); System.out.println("服务启动:8080"); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { boss.shutdownGracefully(); work.shutdownGracefully(); } } private static class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { if ("/ws".equalsIgnoreCase(msg.uri())) { ctx.fireChannelRead(msg.retain()); return; } File f=new File("E:\\git\\tuling-teach-netty\\src\\main\\resources\\HelloWord.html"); RandomAccessFile file = new RandomAccessFile(f, "r");//4 DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, file.length()); // response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderNames.KEEP_ALIVE); ctx.write(response); ctx.write(new ChunkedNioFile(file.getChannel())); ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); future.addListener(ChannelFutureListener.CLOSE); file.close(); } } private static class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("接收数据:"+msg.text()); ctx.writeAndFlush(new TextWebSocketFrame("hello word")); } } public static void main(String[] args) { HttpSimpleServer simpleServer = new HttpSimpleServer(); simpleServer.openServer(); } }
三。netty 服务端代码:
public class TimeServer { // 初始netty 服务 // 启动器:ServerBootstrap // public void openSever(int port) { ServerBootstrap bootstrap = new ServerBootstrap(); EventLoopGroup bootGroup = new NioEventLoopGroup(1); //connect \accept \read \write EventLoopGroup workGroup = new NioEventLoopGroup(3); bootstrap.group(bootGroup, workGroup); bootstrap.channel(NioServerSocketChannel.class); bootstrap.option(ChannelOption.SO_BACKLOG, 1024); bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new TimerServerHandler()); } }); try { System.out.println("服务启动成功"); ChannelFuture f = bootstrap.bind(port).sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } bootGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } private static class TimerServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // ctx.fireChannelRead(msg); // 读取流 ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String body = new String(bytes); System.out.println("netty 服务端接收消息:" + body); String result; if ("time".equals(body.trim())) { result = SimpleDateFormat.getDateTimeInstance().format(new Date()); } else { result = "Bad Order!!"; } // 写入流 ByteBuf responseBuf = Unpooled.copiedBuffer(result.getBytes()); ctx.write(responseBuf); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // super.chann elReadComplete(ctx); ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } } public static void main(String[] args) { TimeServer server = new TimeServer(); server.openSever(8888); } }
Timeclinet:
public class TimeClient { private final TimerClientHandler client; public TimeClient(String host, int port) { final Bootstrap bootstrap = new Bootstrap(); final EventLoopGroup group = new NioEventLoopGroup(1); client = new TimerClientHandler(); bootstrap.group(group); bootstrap.channel(NioSocketChannel.class); bootstrap.option(ChannelOption.TCP_NODELAY, true); bootstrap.remoteAddress(host, port); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(client); } }); Thread t = new Thread(new Runnable() { @Override public void run() { try { ChannelFuture f = bootstrap.connect().sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("连接关闭,资源释放 "); group.shutdownGracefully(); } } }, "IO Client"); t.setDaemon(true); t.start(); } public void sendMessage(String msg) { ByteBuf msgBuf = Unpooled.buffer(msg.getBytes().length); msgBuf.writeBytes(msg.getBytes()); client.ctx.writeAndFlush(msgBuf); } public void close() { client.ctx.close(); } private static class TimerClientHandler extends ChannelInboundHandlerAdapter { private ChannelHandlerContext ctx; @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); String body = "time"; ByteBuf msg = Unpooled.buffer(body.getBytes().length); msg.writeBytes(body.getBytes()); ctx.writeAndFlush(msg); this.ctx = ctx; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String body = new String(bytes); System.out.println("当前服务时间:" + body); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } public static void main(String[] args) throws IOException { TimeClient client = new TimeClient("127.0.0.1", 8888); while (true) { byte[] bytes = new byte[1024]; int size = System.in.read(bytes); String cmd = new String(bytes, 0, size).trim(); try { if (cmd.equals("close")) { client.close(); } else { client.sendMessage(cmd); } } catch (Exception e) { e.printStackTrace(); } } } }