在前文大致上描述了服务端是如何接受TCP链接,然后处理消息的。这里简单描述一下客户端是如何与服务端建立TCP链接 && 发送消息过去的
基础知识 🥰🥰
实例 🧐🧐
public static void main(String[] args) throws Exception {// Configure the client.EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();//p.addLast(new LoggingHandler(LogLevel.INFO));p.addLast(new EchoClientHandler());}});// Start the client.ChannelFuture f = b.connect(HOST, PORT).sync();// Wait until the connection is closed.f.channel().closeFuture().sync();} finally {// Shut down the event loop to terminate all threads.group.shutdownGracefully();}}}
除了 b.connect(HOST, PORT).sync(); 有些陌生以外,其他的配置都在服务启动有过说明,这里就不赘述了。
connect,本质上就是调用操作系统提供connect 函数,然后使用同步阻塞,等到返回的时候,要么报错,链接拒绝,链接超时等等,要么是已经建立好TCP链接了。在没有报错的情况下,我们可以使用 netstat -ant 来查看建立的TCP链接加深我们的理解。
register
connect
由于Netty对handler使用了责任链模式进行串联,在以前也描述过,抓住filter/handler的头部或者是尾部进行分析即可,由于是connect,netty从尾部开始处理。由头部调用socket进行链接。
最终调用到 AbstractNioChannel.java 的connect 函数
@Overridepublic final void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {if (doConnect(remoteAddress, localAddress)) {fulfillConnectPromise(promise, wasActive);}}
@Overrideprotected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {boolean success = false;try {boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);if (!connected) {//注册事件selectionKey().interestOps(SelectionKey.OP_CONNECT);}success = true;return connected;} finally {if (!success) {doClose();}}}
public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)throws IOException {try {//调用jdk提供的socket的connect函数(native调用c的connect函数,其实就是操作系统的connect函数)return socketChannel.connect(remoteAddress)} catch (PrivilegedActionException e) {throw (IOException) e.getCause();}}
这段代码其实很简单,无非就是注册,但是经过netty的封装以后,这个流程就不简单了,如果刚开始接触Netty,对它整体的走向和责任链模式不熟悉的话,就会很迷茫。
