This is the 22nd day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
Common methods of Channel
- Close () can be used to close a Channel
- CloseFuture () is used to handle Channel closure events
- The sync method waits synchronously for a Channel to close
- The addListener method asynchronously waits for a Channel to close
- The pipeline() method is used to add handlers
- The write() method writes data
- Because of caching, data is not sent immediately after being written to a buffer in a Channel
- Only when the buffer is full or the flush() method is called will the data be sent through a Channel
- The writeAndFlush() method writes data and sends it immediately (flush)
Why sync() is needed
In the first two blog posts, Netty programming (1) — First introduction to Netty+ super full annotation – Juejin (cn) and Netty programming (2) — EventLoop – Juejin (cn). The sync() method was said at the time to block. In this blog post, I will first explain the necessity of this sentence.
First of all, we will give the client code, and the previous use is not fully using the chain code:
The analysis reason
If we take line 31channelFuture.sync()
The server cannot receive the “hello world” message.
This is because the process of establishing a connection is asynchronous and non-blocking,
- Asynchronous: The thread calling CONNECT does not care about the result
- Non-blocking: after calling connect, the execution can continue, with different results.
The connect method is initiated by the main thread, but is actually executed by one of the NIO threads in the NioEventLoopGroup, and the main thread continues down. If you don’t block the main thread with sync() and wait for the connection to be established, then the channel object received through channelfuture.channel () is not a channel that is actually connected to the server, because the connection hasn’t been established yet. It cannot transmit the information correctly to the server.
The solution
There are two ways to solve the problem:
-
The first method is to use the channelFuture.sync() method all the time, blocking the main thread, synchronizing the results, waiting for the connection to actually be established, and then getting the Channel to pass the data. With this method, the thread that gets the Channel and sends the data is the main thread
-
The second way is to use the addListener (callback object) method, which is used to asynchronously fetch the Channel and send the data after the connection is established, so that the thread that performs these operations is the NIO thread (the thread that performs the connect operation), Once the NIO thread connection is established, the operationComplete method of the callback object is called. Here is an example to illustrate this method:
public class MyClient {
public static void main(String[] args) throws IOException, InterruptedException {
ChannelFuture channelFuture = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(newStringEncoder()); }})// This method is asynchronous and non-blocking. The main thread does not block. It is the NIO thread that does the connecting
// NIO thread: thread in NioEventLoop
.connect(new InetSocketAddress("localhost".8080));
// After the connect method completes, the connection is actually established
channelFuture.addListener(new ChannelFutureListener() {
// After the NIO thread connection is established, it is called
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
Channel channel = channelFuture.channel();
channel.writeAndFlush("hello world"); }}); System.in.read(); }}Copy the code
When the client successfully establishes a connection with the server, the operationComplete method in the addListener method is executed. The parameter channelFuture passed in is the channelFuture that called the addListener method, and the method is then executed. It is worth noting that the operationComplete method is executed in the NIO thread, which is the responsibility of the event loop group.
Deal with close
When we want to close a channel, we call channel.close() to close it. But this method is an asynchronous method. The actual closure is not performed in the thread calling the method, but in the NIO thread
If we want to do something extra after a channel is actually closed, we can do it in one of two ways:
-
The corresponding ChannelFuture object is obtained through the channel.closeFuture() method, and the sync() method is called to block the thread performing the operation and wait for the channel to actually close before performing any further operations
// Get the closeFuture object ChannelFuture = channel.closeFuture(); // Wait for the NIO thread to execute the close operation closeFuture.sync();Copy the code
-
Call the closeFuture.addListener method to add subsequent operations of close
closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture ChannelFuture) throws Exception {system.out.println (" Perform some additional operations after closing..." ); Gracefully disabled: Denied the connection, sending the remaining data, not stopping group.shutdownGracefully disabled (); }});Copy the code