Use Netty to realize a simple communication model. Look at the program:
1. Server side:
Class Server:
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class Server { public void bind(int port) { /** * NioEventLoopGroup Is a multithreaded event handler for I/O operations * Netty Many different implementations of EventLoopGroup are provided to handle different transports * In this example, the server application is implemented, so two nioeventloopgroups will be used * One is main, also called boss, which is used to receive incoming connections * One is work, which is used to handle accepted connections * Once the boss receives the connection, it will register the information to the worker * How to know how many threads have been used and how to map to the created channel depends on the implementation of EventLoopGroup * And you can configure their relationships through constructors */ NioEventLoopGroup bossGroup = new NioEventLoopGroup(); NioEventLoopGroup workGroup = new NioEventLoopGroup(); try { /** * ServerBootstrap It is a secondary startup class to start NIO * You can use channel directly in this service */ ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workGroup) //The NioServerSocketChannel class is specified here to illustrate how a new channel can accept incoming connections .channel(NioServerSocketChannel.class) // .localAddress(port) /** * The event handling class here is often used to handle a recently ended channel * Anonymous inner class inherited from ChannelInitializer is a special processing class, whose purpose is to help users configure a new channel */ .childHandler( new ChannelInitializer<SocketChannel>() { //Add channelPipeline from serverHandler to channel //Initialize each new Channel through ServerHandler @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("handler", new ServerHandler()); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); } }) //Set request waiting queue for TCP protocol .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); //Bind port, call sync() synchronization blocking method and wait for completion ChannelFuture sync = bootstrap.bind(port).sync(); System.out.println("Server listening port:"+port +" Startup success"); //Use the sync method to block and wait for the main function to exit after the server link is closed sync.channel().closeFuture().sync(); } catch (Exception e) { } finally { //Release thread pool resources workGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); System.out.println("server Shut down"); } } }
ServerHandler class: this class is mainly responsible for the specific business implementation
import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import java.net.SocketAddress; public class ServerHandler extends SimpleChannelInboundHandler<String> { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Channel channel = ctx.channel(); // Obtain the corresponding client ip address through the channel SocketAddress remoteAddress = channel.remoteAddress(); // Get the information in the channel ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String s1 = new String(bytes, "UTF-8"); System.out.println(remoteAddress+":"+s1); channel.writeAndFlush("[server recv]"+s1); } @Override public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress()+"Online."); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress()+"Offline."); } }
2. client
import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.net.InetSocketAddress; import java.util.Scanner; public class Client { private String host; private int port; public Client(String host, int port) { this.host = host; this.port = port; } public void start(){ // Similar to server side NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("handler", new ClientHandler()); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); } }); Channel channel = bootstrap.connect().sync().channel(); System.out.println("Client successfully connected to server"); Scanner scanner = new Scanner(System.in); scanner.useDelimiter("\n"); while (scanner.hasNext()) { String msg = scanner.nextLine(); if ("exit".equals(msg)) { System.out.println("I'm offline."); channel.close(); return; } if (" ".equals(msg)) { continue; } channel.writeAndFlush(msg); } } catch (Exception e) { e.getStackTrace(); } finally { group.shutdownGracefully(); System.out.println("Client is down"); } } }
Clientandler class: mainly responsible for the function implementation of the client
public class ClientHandler extends SimpleChannelInboundHandler<String> { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String s1 = new String(bytes, "UTF-8"); System.out.println(s1); } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { // System.out.println("recv:"+msg); } }
Note: there is a problem here. Sometimes the channelRead() method can read the information in the channel. Sometimes the channelRead0() method can read the information in the same channel. Personal guess is related to the JDK version. When using, you should try which method is available first. The difference between the two methods is that the information obtained by the channelRead() method needs to be converted into String type through ByteBuf, while the channel type The read0() method gets the String type directly.
3. Start of server and client
The server:
public class ServerTest { public static void main(String[] args) { Server server = new Server(); server.bind(9999); } }
Client:
public class ClientTest { public static void main(String[] args) { Client client = new Client("127.0.0.1", 9999); client.start(); } }