summary
The data flowing through the network always has the same type: bytes. How these bytes are transmitted mainly depends on what we call network transmission. Users do not care about the details of transmission, only whether bytes are reliably sent and received
If you use Java network programming, you will find that sometimes when you need to support high concurrency connections, and then you try to switch blocking transmission to non blocking transmission, you will encounter problems because of the difference between the two APIs. Netty provides a common API, which makes the conversion easier.
Traditional transmission mode
This article describes how to use JDK API only to implement blocking (OIO) and non blocking versions (NIO) of applications
The blocking network is programmed as follows:
public class PlainOioServer { public void server(int port) throws IOException { // Bind the server to the specified port final ServerSocket socket = new ServerSocket(port); try { while (true) { // Receive connection final Socket clientSocket = socket.accept(); System.out.println("Accepted connection from " + clientSocket); // Create a new thread to handle the connection new Thread(() -> { OutputStream out; try { out = clientSocket.getOutputStream(); // Write messages to connected clients out.write("Hi\r\n".getBytes(StandardCharsets.UTF_8)); out.flush(); // Close connection x clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } catch (IOException e) { e.printStackTrace(); } } }
This code can handle a medium number of concurrent clients, but with the increase of concurrent connections, you decide to use asynchronous network programming, but the asynchronous API is completely different
The non blocking version is as follows:
public class PlainNioServer { public void server(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); ServerSocket ssocket = serverChannel.socket(); InetSocketAddress address = new InetSocketAddress(port); // Bind the server to the selected port ssocket.bind(address); // Open the Selector to process the Channel Selector selector = Selector.open(); // Register the ServerSocket with the Selector to accept the connection serverChannel.register(selector, SelectionKey.OP_ACCEPT); final ByteBuffer msg = ByteBuffer.wrap("Hi\r\n".getBytes()); while (true) { try { // Waiting for a new event to be processed, the blocking will continue until the next incoming event selector.select(); } catch (IOException e) { e.printStackTrace(); break; } Set<SelectionKey> readKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = readKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); try { // Check whether the event is a new connection that is ready to be accepted if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); // Accept the client and register it with the selector client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate()); System.out.println("Accepted connection from " + client); } // Check whether the socket is ready to write data if (key.isWritable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = (ByteBuffer) key.attachment(); while (buffer.hasRemaining()) { // Write data to connected clients if (client.write(buffer) == 0) { break; } } client.close(); } } catch (IOException exception) { key.cancel(); try { key.channel().close(); } catch (IOException cex) { cex.printStackTrace(); } } } } } }
As you can see, blocking and non blocking code are very different. If you completely rewrite the program in order to achieve non blocking, it is undoubtedly very difficult
Netty based transmission
The blocking network processing using Netty is as follows:
public class NettyOioServer { public void server(int port) throws Exception { final ByteBuf buf = Unpooled.unreleasableBuffer( Unpooled.copiedBuffer("Hi\n\r", StandardCharsets.UTF_8)); EventLoopGroup group = new OioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(group) // Use blocking mode .channel(OioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new SimpleChannelInboundHandler<>() { @Override protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.writeAndFlush(buf.duplicate()) .addListener(ChannelFutureListener.CLOSE); } }); } }); ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } }
The non blocking version as like the blocking version is as like as two peas, only two places need to be changed.
EventLoopGroup group = new NioEventLoopGroup(); b.group(group).channel(NioServerSocketChannel.class);
Transmission API
The core of the transport API is the interface Channel, which is used for all IO operations. Each Channel will be assigned a ChannelPipeline and ChannelConfig. ChannelConfig contains all configuration settings of the Channel. ChannelPipeline holds all ChannelHandler instances that will be applied to inbound and outbound data and events
In addition to accessing the allocated ChannelPipeline and ChannelConfig, other methods of Channel can also be used
Method name | describe |
---|---|
eventLoop | Returns the EventLoop assigned to the Channel |
pipeline | Returns the ChannelPipeline assigned to the Channel |
isActive | Returns true if the Channel is active |
localAddress | Returns the local SocketAddress |
remoteAddress | Returns the remote SocketAddress |
write | Write data to remote node |
flush | Flush the previously written data to the bottom layer for transmission |
writeAndFlush | This is equivalent to calling write() and then flush() |
Built in transmission
Netty has built-in transports that can be used out of the box, but they support different protocols, so you must choose a transport that is compatible with the protocol used by your application
name | package | describe |
---|---|---|
NIO | io.netty.channel.socket.nio | Using Java nio. Channels package as the foundation |
Epoll | io.netty.channel.epoll | On non blocking and niol driven Linux, only niol () can support a variety of transmission features, which are faster than niol () and niol () |
OIO | io.netty.channel.socket.oio | Using Java Net package as the basis |
Local | io.netty.channel.local | A local transport that can communicate through a pipeline within the VM |
Embedded | io.netty.channel.embedded | Embedded transmission, which allows the use of ChannelHandler without the need for a real network-based transmission, is mainly used for testing |