在前文大致上描述了服务端是如何接受TCP链接,然后处理消息的。这里简单描述一下客户端是如何与服务端建立TCP链接 && 发送消息过去的

基础知识 🥰🥰

理解操作系统提供的 connect 函数。

实例 🧐🧐

  1. public static void main(String[] args) throws Exception {
  2. // Configure the client.
  3. EventLoopGroup group = new NioEventLoopGroup();
  4. try {
  5. Bootstrap b = new Bootstrap();
  6. b.group(group)
  7. .channel(NioSocketChannel.class)
  8. .option(ChannelOption.TCP_NODELAY, true)
  9. .handler(new ChannelInitializer<SocketChannel>() {
  10. @Override
  11. public void initChannel(SocketChannel ch) throws Exception {
  12. ChannelPipeline p = ch.pipeline();
  13. //p.addLast(new LoggingHandler(LogLevel.INFO));
  14. p.addLast(new EchoClientHandler());
  15. }
  16. });
  17. // Start the client.
  18. ChannelFuture f = b.connect(HOST, PORT).sync();
  19. // Wait until the connection is closed.
  20. f.channel().closeFuture().sync();
  21. } finally {
  22. // Shut down the event loop to terminate all threads.
  23. group.shutdownGracefully();
  24. }
  25. }
  26. }

除了 b.connect(HOST, PORT).sync(); 有些陌生以外,其他的配置都在服务启动有过说明,这里就不赘述了。

connect,本质上就是调用操作系统提供connect 函数,然后使用同步阻塞,等到返回的时候,要么报错,链接拒绝,链接超时等等,要么是已经建立好TCP链接了。在没有报错的情况下,我们可以使用 netstat -ant 来查看建立的TCP链接加深我们的理解。

register

服务启动

connect

由于Netty对handler使用了责任链模式进行串联,在以前也描述过,抓住filter/handler的头部或者是尾部进行分析即可,由于是connect,netty从尾部开始处理。由头部调用socket进行链接。

最终调用到 AbstractNioChannel.javaconnect 函数

  1. @Override
  2. public final void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
  3. if (doConnect(remoteAddress, localAddress)) {
  4. fulfillConnectPromise(promise, wasActive);
  5. }
  6. }
  1. @Override
  2. protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
  3. boolean success = false;
  4. try {
  5. boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
  6. if (!connected) {
  7. //注册事件
  8. selectionKey().interestOps(SelectionKey.OP_CONNECT);
  9. }
  10. success = true;
  11. return connected;
  12. } finally {
  13. if (!success) {
  14. doClose();
  15. }
  16. }
  17. }
  1. public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)
  2. throws IOException {
  3. try {
  4. //调用jdk提供的socket的connect函数(native调用c的connect函数,其实就是操作系统的connect函数)
  5. return socketChannel.connect(remoteAddress)
  6. } catch (PrivilegedActionException e) {
  7. throw (IOException) e.getCause();
  8. }
  9. }

这段代码其实很简单,无非就是注册,但是经过netty的封装以后,这个流程就不简单了,如果刚开始接触Netty,对它整体的走向和责任链模式不熟悉的话,就会很迷茫。