在前文大致上描述了服务端是如何接受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>() {
@Override
public 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
函数
@Override
public final void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
if (doConnect(remoteAddress, localAddress)) {
fulfillConnectPromise(promise, wasActive);
}
}
@Override
protected 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,对它整体的走向和责任链模式不熟悉的话,就会很迷茫。