Netty HTTP on Android

Netty是一个NIO的客户端服务器框架,它使我们可以快速而简单地开发网络应用程序,比如协议服务器和客户端。它大大简化了网络编程,比如TCP和UDP socket服务器。

“快速而简单”并不意味着开发出来的应用可维护性或性能不好。Netty已经实现了大量的协议,比如FTP,SMTP,HTTP,以及各种基于二进制和文本的传统协议。可以说Netty已经找到了一种方法来实现简单的开发,高性能,稳定性,灵活性而不需要做妥协。

Netty的结构大体如下图这样:

Netty Structure

就设计而言,Netty给不同的传输类型,不管是阻塞的还是非阻塞的,提供了统一的接口。它基于一个灵活的和可扩展的事件模型,这使得处理不同逻辑的部分可以有效的隔离开来。它具有高度可定制的线程模型 - 单线程,一个或多个线程池,比如SEDA。它还提供无连接的datagram socket支持。

如Netty这般,功能如此强大,性能如此优良的网络库,不用在Android上真是可惜了。这里我们就尝试将Netty用在Android上。

下载Netty

首先是下载Netty。Netty的官网地址,我们可以在这里找到下载Netty的地址,当然还有许许多多的文档。这里使用了当前4.1.x最新版的Netty 4.1.4,下载地址:

http://dl.bintray.com/netty/downloads/netty-4.1.4.Final.tar.bz2

解压之后,为了省事,直接将netty-4.1.4.Final/jar/all-in-one/下的netty-all-4.1.4.Final.jar拷贝进了工程下app module的libs目录下。

Netty的简单使用

不出意外,直接通过编译。接着我们就参考netty/example/src/main/java/io/netty/example/http/snoop中client部分的代码,将Netty用起来,这主要有如下几个类:

1package io.netty.example.http.snoop; 2 3import java.net.URI; 4import java.net.URISyntaxException; 5 6import javax.net.ssl.SSLException; 7 8import io.netty.bootstrap.Bootstrap; 9import io.netty.channel.Channel; 10import io.netty.channel.EventLoopGroup; 11import io.netty.channel.nio.NioEventLoopGroup; 12import io.netty.channel.socket.nio.NioSocketChannel; 13import io.netty.handler.codec.http.DefaultFullHttpRequest; 14import io.netty.handler.codec.http.HttpHeaderNames; 15import io.netty.handler.codec.http.HttpHeaderValues; 16import io.netty.handler.codec.http.HttpMethod; 17import io.netty.handler.codec.http.HttpVersion; 18import io.netty.handler.ssl.SslContext; 19import io.netty.handler.ssl.SslContextBuilder; 20import io.netty.handler.ssl.util.InsecureTrustManagerFactory; 21 22public class HttpClient { 23 public static final String TAG = "NettyClient"; 24 25 public static void getResponse(final String url) { 26 new Thread() { 27 @Override 28 public void run() { 29 try { 30 URI uri = new URI(url); 31 String scheme = uri.getScheme() == null ? "http" : uri.getScheme(); 32 String host = uri.getHost(); 33 int port = uri.getPort(); 34 35 if (port == -1) { 36 if ("http".equalsIgnoreCase(scheme)) { 37 port = 80; 38 } else if ("https".equalsIgnoreCase(scheme)) { 39 port = 443; 40 } 41 } 42 if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) { 43 System.err.println("Only HTTP(S) is supported."); 44 return; 45 } 46 47 final boolean ssl = "https".equalsIgnoreCase(scheme); 48 final SslContext sslCtx; 49 if (ssl) { 50 sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); 51 } else { 52 sslCtx = null; 53 } 54 55 EventLoopGroup group = new NioEventLoopGroup(); 56 try { 57 Bootstrap bootstrap = new Bootstrap(); 58 bootstrap.group(group).channel(NioSocketChannel.class) 59 .handler(new HttpClientInitializer(sslCtx)); 60 61 // Make the connection attempt. 62 Channel channel = bootstrap.connect(host, port).sync().channel(); 63 64 // Prepare the HTTP request. 65 DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, 66 HttpMethod.GET, url); 67 request.headers().set(HttpHeaderNames.HOST, host); 68 request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderNames.KEEP_ALIVE); 69 request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); 70 71 // Send the HTTP request. 72 channel.writeAndFlush(request); 73 74 // Wait for the server to close the connection. 75 channel.closeFuture().sync(); 76 } finally { 77 group.shutdownGracefully(); 78 } 79 } catch (URISyntaxException e) { 80 } catch (SSLException e) { 81 } catch (InterruptedException e) { 82 } 83 } 84 }.start(); 85 } 86}

这个class提供给外部调用的接口。使用者可以传入URL,将借由这个类,通过Netty来访问网络并获取响应。然后来看HttpClientInitializer:

1package io.netty.example.http.snoop; 2 3import io.netty.channel.ChannelInitializer; 4import io.netty.channel.ChannelPipeline; 5import io.netty.channel.socket.SocketChannel; 6import io.netty.handler.codec.http.HttpClientCodec; 7import io.netty.handler.codec.http.HttpContentDecompressor; 8import io.netty.handler.ssl.SslContext; 9 10public class HttpClientInitializer extends ChannelInitializer<SocketChannel> { 11 12 private final SslContext sslCtx; 13 14 public HttpClientInitializer(SslContext sslCtx) { 15 this.sslCtx = sslCtx; 16 } 17 18 @Override 19 public void initChannel(SocketChannel ch) { 20 ChannelPipeline p = ch.pipeline(); 21 22 // Enable HTTPS if necessary. 23 if (sslCtx != null) { 24 p.addLast(sslCtx.newHandler(ch.alloc())); 25 } 26 27 p.addLast(new HttpClientCodec()); 28 29 // Remove the following line if you don't want automatic content decompression. 30 p.addLast(new HttpContentDecompressor()); 31 32 // Uncomment the following line if you don't want to handle HttpContents. 33 //p.addLast(new HttpObjectAggregator(1048576)); 34 35 p.addLast(new HttpClientHandler()); 36 } 37}

这个class负责对Channel的Pipeline进行初始化,这其中最关键的是HttpClientHandler:

1package io.netty.example.http.snoop; 2 3import android.util.Log; 4 5import io.netty.channel.ChannelHandlerContext; 6import io.netty.channel.SimpleChannelInboundHandler; 7import io.netty.handler.codec.http.HttpContent; 8import io.netty.handler.codec.http.HttpObject; 9import io.netty.handler.codec.http.HttpResponse; 10import io.netty.handler.codec.http.LastHttpContent; 11import io.netty.util.CharsetUtil; 12 13public class HttpClientHandler extends SimpleChannelInboundHandler<HttpObject> { 14 15 @Override 16 public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { 17 if (msg instanceof HttpResponse) { 18 HttpResponse response = (HttpResponse) msg; 19 20 Log.i(HttpClient.TAG, "STATUS: " + response.status()); 21 Log.i(HttpClient.TAG, "VERSION: " + response.protocolVersion()); 22 23 if (!response.headers().isEmpty()) { 24 for (CharSequence name: response.headers().names()) { 25 for (CharSequence value: response.headers().getAll(name)) { 26 Log.i(HttpClient.TAG, "HEADER: " + name + " = " + value); 27 } 28 } 29 } 30 } 31 if (msg instanceof HttpContent) { 32 HttpContent content = (HttpContent) msg; 33 String responseContent = content.content().toString(CharsetUtil.UTF_8); 34 Log.i(HttpClient.TAG, responseContent); 35 36 if (content instanceof LastHttpContent) { 37 ctx.close(); 38 } 39 } 40 } 41 42 @Override 43 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 44 cause.printStackTrace(); 45 ctx.close(); 46 } 47}

我们正是通过实现HttpClientHandler,而获取到Netty返回给我们的响应的。

在我们的Android应用代码中调用HttpClient来通过Netty从网络获取响应:

1package io.netty.example.http.snoop; 2 3import android.support.v7.app.AppCompatActivity; 4import android.os.Bundle; 5import android.util.Log; 6import android.view.View; 7import android.widget.Button; 8import android.widget.TextView; 9 10public class MainActivity extends AppCompatActivity { 11 private static final String TAG = "MainActivity"; 12 13 private TextView mTextScreen; 14 15 @Override 16 protected void onCreate(Bundle savedInstanceState) { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.activity_main); 19 20 Button btnGetIpInfo = (Button) findViewById(R.id.btn_get_ip_info_with_netty); 21 btnGetIpInfo.setOnClickListener(mBtnClickListener); 22 23 mTextScreen = (TextView) findViewById(R.id.text_screen); 24 } 25 26 View.OnClickListener mBtnClickListener = new View.OnClickListener() { 27 28 @Override 29 public void onClick(View v) { 30 String url = "http://ip.taobao.com/service/getIpInfo.php?ip=123.58.191.68"; 31 mTextScreen.setText("To access " + url); 32 Log.i(TAG, "To access " + url); 33 if (R.id.btn_get_ip_info_with_netty == v.getId()) { 34 HttpClient.getResponse(url); 35 } 36 } 37 }; 38}

当然不能忘记了在AndroidManifest.xml中添加对INTERNET权限的请求:

    <uses-permission android:name="android.permission.INTERNET"/>

做完了所有这些之后,Netty基本上就可以跑起来了。不过意外还是发生了:

108-10 10:13:33.670 17720-17720/io.netty.example.http.snoop6.myapplication I/MainActivity: To access http://ip.taobao.com/service/getIpInfo.php?ip=123.58.191.68 208-10 10:13:33.818 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: java.lang.NoClassDefFoundError: com.jcraft.jzlib.Inflater 308-10 10:13:33.818 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.compression.JZlibDecoder.<init>(JZlibDecoder.java:27) 408-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.compression.ZlibCodecFactory.newZlibDecoder(ZlibCodecFactory.java:122) 508-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.http.HttpContentDecompressor.newContentDecoder(HttpContentDecompressor.java:57) 608-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:87) 708-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:46) 808-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:88) 908-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) 1008-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) 1108-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) 1208-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:435) 1308-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293) 1408-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:280) 1508-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:396) 1608-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248) 1708-10 10:13:33.826 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:250) 1808-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) 1908-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) 2008-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) 2108-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) 2208-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) 2308-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) 2408-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) 2508-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129) 2608-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:571) 2708-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:474) 2808-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:428) 2908-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:398) 3008-10 10:13:33.834 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:877) 3108-10 10:13:33.841 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144) 3208-10 10:13:33.841 17720-18708/io.netty.example.http.snoop6.myapplication W/System.err: at java.lang.Thread.run(Thread.java:841) 3308-10 10:13:33.841 17720-18708/io.netty.example.http.snoop6.myapplication I/NettyClient: --------- beginning of /dev/log/system 3408-10 10:35:02.162 17720-17720/io.netty.example.http.snoop6.myapplication I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@41c10f28 time:1282964865

有一个class com.jcraft.jzlib.Inflater找不到。这还需要添加对jzlib的依赖:

    compile 'com.jcraft:jzlib:1.1.2'

至此顺利地将Netty跑起来:

1{ 2 "code":0, 3 "data":{ 4 "country":"中国", 5 "country_id":"CN", 6 "area":"华东", 7 "area_id":"300000", 8 "region":"浙江省", 9 "region_id":"330000", 10 "city":"杭州市", 11 "city_id":"330100", 12 "county":"", 13 "county_id":"-1", 14 "isp":"网易网络", 15 "isp_id":"1000119", 16 "ip":"123.58.191.68" 17 } 18}

Netty的裁剪

Netty很强大,all-in-one jar用起来很方便,这很不错。但all-in-one jar有点大,其中包含的一些诸如对memcache,redis,stomp,sctp和udt等的支持,我们在移动端并不会用掉。因而需要对它做一点裁剪。

既然不能用all-in-one包,那就把需要的几个jar文件单独copy进我们的工程好了。对于android而言,目测netty-4.1.4.Final/jar/下我们需要拷贝的jar文件主要有下面这些:

1netty-codec-4.1.4.Final.jar 2netty-codec-http-4.1.4.Final.jar 3netty-codec-http2-4.1.4.Final.jar 4netty-codec-socks-4.1.4.Final.jar 5netty-common-4.1.4.Final.jar 6netty-handler-4.1.4.Final.jar 7netty-transport-4.1.4.Final.jar

用这些文件来替换之前的netty-all-4.1.4.Final.jar。遇到了编译错误:

1:app:compileDebugJavaWithJavac 2Full recompilation is required because at least one of the classes of removed jar 'netty-all-4.1.4.Final.jar' requires it. Analysis took 0.364 secs. 3/media/data/MyProjects/MyApplication/app/src/main/java/io/netty/example/http/snoop/myapplication/HttpClient.java:67: 错误: 无法访问ByteBufHolder 4 request.headers().set(HttpHeaderNames.HOST, host); 5 ^ 6 找不到io.netty.buffer.ByteBufHolder的类文件 7/media/data/MyProjects/MyApplication/app/src/main/java/io/netty/example/http/snoop/myapplication/HttpClientInitializer.java:39: 错误: 无法访问ByteBufAllocator 8 p.addLast(sslCtx.newHandler(ch.alloc())); 9 ^ 10 找不到io.netty.buffer.ByteBufAllocator的类文件 11/media/data/MyProjects/MyApplication/app/src/main/java/io/netty/example/http/snoop/myapplication/HttpClientHandler.java:48: 错误: 找不到符号 12 String responseContent = content.content().toString(CharsetUtil.UTF_8); 13 ^ 14 符号: 方法 content() 15 位置: 类型为HttpContent的变量 content 16: /media/data/MyProjects/MyApplication/app/src/main/java/io/netty/example/http/snoop/myapplication/HttpClient.java使用或覆盖了已过时的 API17: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。 183 个错误 19:app:compileDebugJavaWithJavac FAILED 20 21FAILURE: Build failed with an exception. 22 23* What went wrong: 24Execution failed for task ':app:compileDebugJavaWithJavac'. 25> Compilation failed; see the compiler error output for details. 26 27* Try: 28Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. 29 30BUILD FAILED

看来是少了一些东西了,ByteBufHolder。那就把netty-buffer-4.1.4.Final.jar也加进工程里。再次编译,继续出错,这是在产生APK时遇到了麻烦:

1:app:transformResourcesWithMergeJavaResForDebug FAILED 2 3FAILURE: Build failed with an exception. 4 5* What went wrong: 6Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'. 7> com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/INDEX.LIST 8 File1: /media/data/MyProjects/MyApplication/app/libs/netty-codec-4.1.4.Final.jar 9 File2: /media/data/MyProjects/MyApplication/app/libs/netty-transport-4.1.4.Final.jar 10 File3: /media/data/MyProjects/MyApplication/app/libs/netty-buffer-4.1.4.Final.jar 11 File4: /media/data/MyProjects/MyApplication/app/libs/netty-codec-socks-4.1.4.Final.jar 12 File5: /media/data/MyProjects/MyApplication/app/libs/netty-handler-4.1.4.Final.jar 13 File6: /media/data/MyProjects/MyApplication/app/libs/netty-codec-http2-4.1.4.Final.jar 14 File7: /media/data/MyProjects/MyApplication/app/libs/netty-codec-http-4.1.4.Final.jar 15 16 17* Try: 18Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. 19 20BUILD FAILED 21 22Total time: 15.352 secs 23Duplicate files copied in APK META-INF/INDEX.LIST 24 File1: /media/data/MyProjects/MyApplication/app/libs/netty-codec-4.1.4.Final.jar 25 File2: /media/data/MyProjects/MyApplication/app/libs/netty-transport-4.1.4.Final.jar 26 File3: /media/data/MyProjects/MyApplication/app/libs/netty-buffer-4.1.4.Final.jar 27 File4: /media/data/MyProjects/MyApplication/app/libs/netty-codec-socks-4.1.4.Final.jar 28 File5: /media/data/MyProjects/MyApplication/app/libs/netty-handler-4.1.4.Final.jar 29 File6: /media/data/MyProjects/MyApplication/app/libs/netty-codec-http2-4.1.4.Final.jar 30 File7: /media/data/MyProjects/MyApplication/app/libs/netty-codec-http-4.1.4.Final.jar 31 3211:03:39: External task execution finished 'assembleDebug'.

总是报Duplicate files copied in APK META-INF/INDEX.LIST的错误。这主要是因为这些jar文件里,不同的jar文件中的META-INF/INDEX.LIST包含了相同的内容所致。这需要在build.gradle中的android元素里添加如下的配置:

1 packagingOptions { 2 exclude 'META-INF/INDEX.LIST' 3 }

再次编译,这次则是包Duplicate files copied in APK META-INF/io.netty.versions.properties。这证明了前面的方法是行之有效的,于是把META-INF/io.netty.versions.properties也加进packagingOptions的exclude列表:

1 packagingOptions { 2 exclude 'META-INF/INDEX.LIST' 3 exclude 'META-INF/io.netty.versions.properties' 4 }

再次编译,编译通过。但是在运行的时候遇到了一点小麻烦:

111:29:59.685 9005-9050/com.example.hanpfei0306.myapplication W/dalvikvm: threadid=11: thread exiting with uncaught exception (group=0x41959ce0) 208-11 11:29:59.685 9005-9050/com.example.hanpfei0306.myapplication E/AndroidRuntime: FATAL EXCEPTION: Thread-1197 3 Process: com.example.hanpfei0306.myapplication, PID: 9005 4 java.lang.NoClassDefFoundError: io.netty.resolver.DefaultAddressResolverGroup 5 at io.netty.bootstrap.Bootstrap.<clinit>(Bootstrap.java:53) 6 at com.example.hanpfei0306.myapplication.HttpClient$1.run(HttpClient.java:57)

提示找不到class io.netty.resolver.DefaultAddressResolverGroup,看来我们的jar文件是加少了,把netty-resolver-4.1.4.Final.jar也加进来。终于,我们前面编写的HttpClient能够正常地跑起来了。经过一番裁剪,Netty的大小大概从3.4MB减小到2.9MB。

总结一下,若不用体型巨大的netty-all jar文件,则我们需要导入如下的这些jar文件以编译和运行Netty:

1netty-buffer-4.1.4.Final.jar 2netty-codec-4.1.4.Final.jar 3netty-codec-http-4.1.4.Final.jar 4netty-codec-http2-4.1.4.Final.jar 5netty-codec-socks-4.1.4.Final.jar 6netty-common-4.1.4.Final.jar 7netty-handler-4.1.4.Final.jar 8netty-resolver-4.1.4.Final.jar 9netty-transport-4.1.4.Final.jar

TODO

Netty的诸多抽象,比如Bootstrap,Channel,EventLoopGroup,Handler,codec,Buffer等诸多高级特性,以及它的NIO接口,这里都没有涉及,线程模型也没有仔细厘清。这里只是最最简单的一个使用范例,要把Netty很好地应用在实际的项目中,还需要对Netty本身更深入的研究。

同时,对Netty的裁剪可能也过于粗糙,或许还有更多的东西可以裁剪掉,以减小最终的APP的大小。

要使用Netty来支持HTTP/2也还需要做更多的事情。

但窥探到Netty的灵活强大,还是让我们对这个库充满期待。

参考文档

多个jar包的合并

Netty4.x中文教程系列

netty-4-user-guide

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Netty 高性能网络协议服务器开发

本文通过一个实例来讲解如何使用框架来开发网络协议服务器,项目使用工具来构建和运行,并且支持部署。项目代码已在GitHub开源,。Netty简介Netty是一个异步、事件驱动的网络应用框架,使用它可以快速开发出可维护良好的、高性能的网络协议服务器。它大幅简化和流程化了网络编程,比如TCP和UDP套接字服务器开发。难能

Netty序章之BIO NIO AIO演变

Netty序章之BIONIOAIO演变Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠的网络服务器和客户端程序。Netty简化了网络程序的开发,是很多框架和公司都在使用的技术。更是面试的加分项。Netty并非横空出世,它是在BIO,NIO,AIO演变中的产物,是一种

BIO、NIO、AIO系列二:Netty

一、概述Netty是一个Java的开源框架。提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty是一个NIO客户端,服务端框架。允许快速简单的开发网络应用程序。例如:服务端和客户端之间的协议,它简化了网络编程规范。二、NIO开发的问题