Netty
Buffer
| IntBuffer intBuffer = IntBuffer.allocate(5); |
| |
| for (int i = 0; i < intBuffer.capacity(); i++) { |
| intBuffer.put(i); |
| } |
| |
| |
| intBuffer.flip(); |
| while (intBuffer.hasRemaining()) { |
| |
| System.out.println(intBuffer.get()); |
| } |
类型化放入
| public static void main(String[] args) throws Exception { |
| ByteBuffer buffer = ByteBuffer.allocate(64); |
| |
| |
| buffer.putInt(100); |
| buffer.putChar('*'); |
| buffer.putLong(1L); |
| |
| buffer.flip(); |
| |
| |
| System.out.println(buffer.getInt()); |
| System.out.println(buffer.getChar()); |
| System.out.println(buffer.getLong()); |
| } |
只读
| public static void main(String[] args) throws Exception { |
| ByteBuffer buffer = ByteBuffer.allocate(64); |
| |
| for (int i = 0; i < buffer.capacity(); i++) { |
| buffer.put((byte) i); |
| } |
| |
| buffer.flip(); |
| |
| |
| ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer(); |
| while (readOnlyBuffer.hasRemaining()) { |
| System.out.println(readOnlyBuffer.get()); |
| } |
| |
| |
| readOnlyBuffer.put((byte) 1); |
| } |
MappedByteBuffer
| public static void main(String[] args) throws Exception { |
| RandomAccessFile randomAccessFile = new RandomAccessFile("file", "rw"); |
| FileChannel channel = randomAccessFile.getChannel(); |
| |
| |
| |
| |
| |
| |
| |
| MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5); |
| map.put(0, (byte) 'X'); |
| map.put(4, (byte) 'Y'); |
| randomAccessFile.close(); |
| } |
Buffer数组
| public static void main(String[] args) throws Exception { |
| ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); |
| InetSocketAddress inetSocketAddress = new InetSocketAddress(8888); |
| |
| serverSocketChannel.socket().bind(inetSocketAddress); |
| |
| ByteBuffer[] byteBuffers = new ByteBuffer[2]; |
| byteBuffers[0] = ByteBuffer.allocate(5); |
| byteBuffers[1] = ByteBuffer.allocate(3); |
| |
| SocketChannel socketChannel = serverSocketChannel.accept(); |
| int count = 8; |
| while (true) { |
| int byteRead = 0; |
| while (byteRead < count) { |
| long l = socketChannel.read(byteBuffers); |
| byteRead += l; |
| System.out.println("byteRead:" + l); |
| |
| Arrays.stream(byteBuffers) |
| .map(buffer -> "position:" + buffer.position() |
| + ", limit:" + buffer.limit()) |
| .forEach(System.out::println); |
| } |
| |
| |
| Arrays.asList(byteBuffers) |
| .forEach(ByteBuffer::flip); |
| |
| |
| long byteWrite = 0; |
| while (byteWrite < count) { |
| long l = socketChannel.write(byteBuffers); |
| byteWrite += l; |
| } |
| |
| |
| Arrays.asList(byteBuffers) |
| .forEach(ByteBuffer::clear); |
| } |
| } |
Channel
ByteBuffer
| public static void main(String[] args) throws Exception { |
| FileInputStream fileInputStream = new FileInputStream("file"); |
| FileChannel channel1 = fileInputStream.getChannel(); |
| |
| FileOutputStream fileOutputStream = new FileOutputStream("file.txt"); |
| FileChannel channel2 = fileOutputStream.getChannel(); |
| |
| ByteBuffer byteBuffer = ByteBuffer.allocate(1024); |
| while (true) { |
| |
| |
| |
| |
| |
| |
| byteBuffer.clear(); |
| |
| |
| int read = channel1.read(byteBuffer); |
| if (read == -1) { |
| break; |
| } |
| |
| |
| byteBuffer.flip(); |
| channel2.write(byteBuffer); |
| } |
| |
| fileInputStream.close(); |
| fileOutputStream.close(); |
| } |
transferTo&transferFrom
| public static void main(String[] args) throws Exception { |
| FileInputStream fileInputStream = new FileInputStream("file"); |
| FileChannel sourceChannel = fileInputStream.getChannel(); |
| |
| FileOutputStream fileOutputStream = new FileOutputStream("file.txt"); |
| FileChannel destChannel = fileOutputStream.getChannel(); |
| |
| |
| sourceChannel.transferTo(0, sourceChannel.size(), destChannel); |
| |
| fileInputStream.close(); |
| fileOutputStream.close(); |
| } |
Selector
-
一个EventLoopGroup(事件循环组) 包含一个或者多个EventLoop;
-
一个EventLoop 在它的生命周期内只和一个Thread 绑定;
-
所有由EventLoop 处理的I/O 事件都将在它专有的Thread 上被处理;
-
一个Channel 在它的生命周期内只注册于一个EventLoop;
-
一个EventLoop 可能会被分配给一个或多个Channel。
-
有客户端连接时,会通过ServerSocketChannel得到SocketChannel
-
将SocketChannel注册到Selector上,register(Selector sel, int ops, Object att)
,返回SelectionKey,会和该Selector上关联(集合)。一个Selector上可以注册多个SocketChannel
-
Selector会用select方法进行监听,返回发生事件的通道个数
-
得到有事件发生的Channel的SelectionKey,然后得到该Channel
| selector.select(); |
| selector.select(100); |
| selector.wakeup(); |
| selector.selectNow(); |
NIO服务器
| import java.net.InetSocketAddress; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.SelectionKey; |
| import java.nio.channels.Selector; |
| import java.nio.channels.ServerSocketChannel; |
| import java.nio.channels.SocketChannel; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| class NIOServer { |
| public static void main(String[] args) throws Exception { |
| ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); |
| |
| Selector selector = Selector.open(); |
| |
| serverSocketChannel.socket().bind(new InetSocketAddress(7899)); |
| |
| serverSocketChannel.configureBlocking(false); |
| |
| serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); |
| |
| |
| while (true) { |
| if (selector.select(1000) == 0) { |
| System.out.println("服务器等待1s,无连接"); |
| continue; |
| } |
| |
| |
| Set<SelectionKey> selectionKeys = selector.selectedKeys(); |
| |
| Iterator<SelectionKey> iterator = selectionKeys.iterator(); |
| while (iterator.hasNext()) { |
| |
| SelectionKey key = iterator.next(); |
| |
| |
| if (key.isAcceptable()) { |
| |
| |
| SocketChannel socketChannel = serverSocketChannel.accept(); |
| socketChannel.configureBlocking(false); |
| System.out.println("客户端连接成功" + socketChannel.hashCode()); |
| |
| socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(10)); |
| } |
| |
| if (key.isReadable()) { |
| |
| SocketChannel channel = (SocketChannel) key.channel(); |
| |
| ByteBuffer byteBuffer = (ByteBuffer) key.attachment(); |
| channel.read(byteBuffer); |
| System.out.println("客户端发来:" + new String(byteBuffer.array(), StandardCharsets.UTF_8)); |
| } |
| |
| |
| iterator.remove(); |
| } |
| |
| } |
| } |
| } |
NIO客户端
| import java.net.InetSocketAddress; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.SocketChannel; |
| import java.nio.charset.StandardCharsets; |
| |
| class NIOClient { |
| public static void main(String[] args) throws Exception { |
| SocketChannel socketChannel = SocketChannel.open(); |
| |
| socketChannel.configureBlocking(false); |
| InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 7899); |
| if (!socketChannel.connect(inetSocketAddress)) { |
| while (!socketChannel.finishConnect()) { |
| System.out.println("连接中,客户端不会阻塞,可以做其他事"); |
| } |
| } |
| |
| |
| ByteBuffer byteBuffer = ByteBuffer.wrap("haha".getBytes(StandardCharsets.UTF_8)); |
| |
| socketChannel.write(byteBuffer); |
| System.in.read(); |
| } |
| } |
零拷贝
todo
Reactor模式
单Reactor单线程
| package groupchat; |
| |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.SelectionKey; |
| import java.nio.channels.Selector; |
| import java.nio.channels.SocketChannel; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Iterator; |
| import java.util.Scanner; |
| import java.util.concurrent.TimeUnit; |
| |
| public class GroupChatClient { |
| private static final String HOST = "127.0.0.1"; |
| private static final int PORT = 9998; |
| private Selector selector; |
| private SocketChannel socketChannel; |
| private String userName; |
| |
| public GroupChatClient() { |
| try { |
| selector = Selector.open(); |
| socketChannel = SocketChannel.open(); |
| socketChannel.connect(new InetSocketAddress(HOST, PORT)); |
| socketChannel.configureBlocking(false); |
| socketChannel.register(selector, SelectionKey.OP_READ); |
| userName = socketChannel.getLocalAddress().toString().substring(1); |
| System.out.println(userName + " is ok..."); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public void sendInfo(String info) { |
| info = userName + " 说:" + info; |
| try { |
| socketChannel.write(ByteBuffer.wrap(info.getBytes(StandardCharsets.UTF_8))); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public void readInfo() { |
| try { |
| int count = selector.select(); |
| if (count > 0) { |
| Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); |
| while (iterator.hasNext()) { |
| SelectionKey key = iterator.next(); |
| if (key.isReadable()) { |
| SocketChannel channel = (SocketChannel) key.channel(); |
| ByteBuffer buffer = ByteBuffer.allocate(1024); |
| channel.read(buffer); |
| System.out.println(new String(buffer.array(), StandardCharsets.UTF_8)); |
| } |
| } |
| iterator.remove(); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public static void main(String[] args) { |
| GroupChatClient groupChatClient = new GroupChatClient(); |
| new Thread(() -> { |
| while (true) { |
| groupChatClient.readInfo(); |
| try { |
| TimeUnit.SECONDS.sleep(3); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| }).start(); |
| |
| Scanner scanner = new Scanner(System.in); |
| while (scanner.hasNextLine()) { |
| String s = scanner.nextLine(); |
| groupChatClient.sendInfo(s); |
| } |
| } |
| } |
| |
| package groupchat; |
| |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.SelectionKey; |
| import java.nio.channels.Selector; |
| import java.nio.channels.SocketChannel; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Iterator; |
| import java.util.Scanner; |
| import java.util.concurrent.TimeUnit; |
| |
| public class GroupChatClient { |
| private static final String HOST = "127.0.0.1"; |
| private static final int PORT = 9998; |
| private Selector selector; |
| private SocketChannel socketChannel; |
| private String userName; |
| |
| public GroupChatClient() { |
| try { |
| selector = Selector.open(); |
| socketChannel = SocketChannel.open(); |
| socketChannel.connect(new InetSocketAddress(HOST, PORT)); |
| socketChannel.configureBlocking(false); |
| socketChannel.register(selector, SelectionKey.OP_READ); |
| userName = socketChannel.getLocalAddress().toString().substring(1); |
| System.out.println(userName + " is ok..."); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public void sendInfo(String info) { |
| info = userName + " 说:" + info; |
| try { |
| socketChannel.write(ByteBuffer.wrap(info.getBytes(StandardCharsets.UTF_8))); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public void readInfo() { |
| try { |
| int count = selector.select(); |
| if (count > 0) { |
| Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); |
| while (iterator.hasNext()) { |
| SelectionKey key = iterator.next(); |
| if (key.isReadable()) { |
| SocketChannel channel = (SocketChannel) key.channel(); |
| ByteBuffer buffer = ByteBuffer.allocate(1024); |
| channel.read(buffer); |
| System.out.println(new String(buffer.array(), StandardCharsets.UTF_8)); |
| } |
| } |
| iterator.remove(); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public static void main(String[] args) { |
| GroupChatClient groupChatClient = new GroupChatClient(); |
| new Thread(() -> { |
| while (true) { |
| groupChatClient.readInfo(); |
| try { |
| TimeUnit.SECONDS.sleep(3); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| }).start(); |
| |
| Scanner scanner = new Scanner(System.in); |
| while (scanner.hasNextLine()) { |
| String s = scanner.nextLine(); |
| groupChatClient.sendInfo(s); |
| } |
| } |
| } |
单Reactor多线程
1、Reactor对象通过select监听客户端请求事件,收到事件后,通过dispatch进行分发。
2、如果建立连接请求,则Acceptor通过accept处理连接请求,然后创建一个Handler对象处理完成连接后的各种事件。
3、如果不是连接请求,则由reactor分发调用连接对应的handler来处理。
4、handler只负责相应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池的某个线程处理业务。
5、worker线程池会分配独立线程完成真正的业务,并将结果返回给handler。
6、handler收到响应后,通过send分发将结果返回给client。
主从Reactor多线程
1、Reactor主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件。
2、当Acceptor处理连接事件后,MainReactor将连接分配给SubAcceptor。
3、SubAcceptor将连接加入到连接队列进行监听,并创建handler进行各种事件处理。
4、当有新事件发生时,SubAcceptor就会调用对应的handler进行各种事件处理。
5、handler通过read读取数据,分发给后面的work线程处理。
6、work线程池分配独立的work线程进行业务处理,并返回结果。
7、handler收到响应的结果后,再通过send返回给client。
注意:Reactor主线程可以对应多个Reactor子线程,即SubAcceptor。
Netty案例
Netty服务器
| package netty; |
| |
| 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.util.NettyRuntime; |
| |
| public class NettyServer { |
| public static void main(String[] args) throws Exception { |
| |
| |
| |
| |
| |
| |
| |
| |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| try { |
| |
| ServerBootstrap bootstrap = new ServerBootstrap(); |
| |
| bootstrap.group(bossGroup, workerGroup) |
| .channel(NioServerSocketChannel.class) |
| .option(ChannelOption.SO_BACKLOG, 128) |
| .childOption(ChannelOption.SO_KEEPALIVE, true) |
| .childHandler(new ChannelInitializer<SocketChannel>() { |
| |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| |
| ch.pipeline().addLast(new NettyServerHandler()); |
| } |
| }); |
| |
| System.out.println(".....服务器 is ready..."); |
| |
| |
| ChannelFuture channelFuture = bootstrap.bind(8888).sync(); |
| |
| channelFuture.addListener(new ChannelFutureListener() { |
| @Override |
| public void operationComplete(ChannelFuture future) throws Exception { |
| if (channelFuture.isSuccess()) { |
| System.out.println("服务已启动,端口号为8888..."); |
| } else { |
| System.out.println("服务启动失败..."); |
| } |
| } |
| }); |
| |
| channelFuture.channel().closeFuture().sync(); |
| } finally { |
| bossGroup.shutdownGracefully(); |
| workerGroup.shutdownGracefully(); |
| } |
| } |
| } |
| package netty; |
| |
| import io.netty.buffer.ByteBuf; |
| import io.netty.buffer.Unpooled; |
| import io.netty.channel.Channel; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.ChannelInboundHandlerAdapter; |
| import io.netty.util.CharsetUtil; |
| |
| import java.util.concurrent.TimeUnit; |
| |
| public class NettyServerHandler extends ChannelInboundHandlerAdapter { |
| |
| |
| |
| |
| |
| @Override |
| public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| System.out.println("server ctx =" + ctx); |
| Channel channel = ctx.channel(); |
| |
| |
| ByteBuf buf = (ByteBuf) msg; |
| System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8)); |
| System.out.println("客户端地址:" + channel.remoteAddress()); |
| |
| |
| ctx.channel().eventLoop().execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| TimeUnit.SECONDS.sleep(3); |
| System.out.println("延迟3s后执行的结果"); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| }); |
| |
| |
| ctx.channel().eventLoop().schedule(new Runnable() { |
| @Override |
| public void run() { |
| System.out.println("定时4s后执行的结果"); |
| } |
| }, 4, TimeUnit.SECONDS); |
| } |
| |
| |
| |
| @Override |
| public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { |
| |
| |
| |
| ctx.writeAndFlush(Unpooled.copiedBuffer("公司最近账户没啥钱,再等几天吧!", CharsetUtil.UTF_8)); |
| } |
| |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| ctx.close(); |
| } |
| } |
Netty客户端
| package netty; |
| |
| import io.netty.bootstrap.Bootstrap; |
| import io.netty.channel.ChannelFuture; |
| import io.netty.channel.ChannelInitializer; |
| import io.netty.channel.EventLoopGroup; |
| import io.netty.channel.nio.NioEventLoopGroup; |
| import io.netty.channel.socket.SocketChannel; |
| import io.netty.channel.socket.nio.NioSocketChannel; |
| |
| public class NettyClient { |
| public static void main(String[] args) throws Exception { |
| |
| EventLoopGroup group = new NioEventLoopGroup(); |
| try { |
| |
| |
| Bootstrap bootstrap = new Bootstrap(); |
| |
| bootstrap.group(group) |
| .channel(NioSocketChannel.class) |
| .handler(new ChannelInitializer<SocketChannel>() { |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ch.pipeline().addLast(new NettyClientHandler()); |
| } |
| }); |
| System.out.println("客户端 ok.."); |
| |
| |
| ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).sync(); |
| |
| channelFuture.channel().closeFuture().sync(); |
| } finally { |
| group.shutdownGracefully(); |
| } |
| } |
| } |
| package netty; |
| |
| import io.netty.buffer.ByteBuf; |
| import io.netty.buffer.Unpooled; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.ChannelInboundHandlerAdapter; |
| import io.netty.util.CharsetUtil; |
| |
| public class NettyClientHandler extends ChannelInboundHandlerAdapter { |
| |
| |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| System.out.println("client ctx =" + ctx); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("老板,工资什么时候发给我啊?", CharsetUtil.UTF_8)); |
| } |
| |
| |
| @Override |
| public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| ByteBuf buf = (ByteBuf) msg; |
| System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8)); |
| System.out.println("服务器的地址: "+ ctx.channel().remoteAddress()); |
| } |
| |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| cause.printStackTrace(); |
| ctx.close(); |
| } |
| } |
Netty群聊
服务器
| package nettygroupchat; |
| |
| 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; |
| import io.netty.handler.logging.LogLevel; |
| import io.netty.handler.logging.LoggingHandler; |
| import io.netty.handler.timeout.IdleStateHandler; |
| |
| public class GroupChatServer { |
| |
| private final int port; |
| |
| public GroupChatServer(int port) { |
| this.port = port; |
| } |
| |
| public void run() { |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workGroup = new NioEventLoopGroup(); |
| try { |
| ServerBootstrap serverBootstrap = new ServerBootstrap() |
| .group(bossGroup, workGroup) |
| .channel(NioServerSocketChannel.class) |
| .option(ChannelOption.SO_BACKLOG, 128) |
| .childOption(ChannelOption.SO_KEEPALIVE, true) |
| .handler(new LoggingHandler(LogLevel.INFO)) |
| .childHandler(new ChannelInitializer<SocketChannel>() { |
| @Override |
| protected void initChannel(SocketChannel socketChannel) throws Exception { |
| ChannelPipeline pipeline = socketChannel.pipeline(); |
| pipeline.addLast(new StringDecoder()); |
| pipeline.addLast(new StringEncoder()); |
| |
| |
| pipeline.addLast(new IdleStateHandler(3, 5, 7)); |
| |
| pipeline.addLast(new HeartBeatHandler()); |
| pipeline.addLast(new GroupChatServerHandler()); |
| } |
| }); |
| |
| ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); |
| channelFuture.channel().closeFuture().sync(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| bossGroup.shutdownGracefully(); |
| workGroup.shutdownGracefully(); |
| } |
| } |
| |
| public static void main(String[] args) { |
| GroupChatServer groupChatServer = new GroupChatServer(7788); |
| groupChatServer.run(); |
| } |
| } |
- GroupChatServerHandler.java
| package nettygroupchat; |
| |
| import io.netty.channel.Channel; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| import io.netty.channel.group.ChannelGroup; |
| import io.netty.channel.group.DefaultChannelGroup; |
| import io.netty.util.concurrent.GlobalEventExecutor; |
| |
| import java.text.SimpleDateFormat; |
| |
| public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> { |
| |
| |
| private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); |
| SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| |
| |
| @Override |
| public void handlerAdded(ChannelHandlerContext ctx) throws Exception { |
| Channel channel = ctx.channel(); |
| |
| |
| channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + "加入聊天\n"); |
| |
| channelGroup.add(channel); |
| } |
| |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| System.out.println(ctx.channel().remoteAddress() + "上线了"); |
| } |
| |
| @Override |
| public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
| System.out.println(ctx.channel().remoteAddress() + "离线了"); |
| } |
| |
| @Override |
| public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { |
| channelGroup.writeAndFlush("[客户端]" + ctx.channel().remoteAddress() + "离开了\n"); |
| |
| System.out.println("channelGroup.size()=" + channelGroup.size()); |
| } |
| |
| @Override |
| protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { |
| Channel channel = channelHandlerContext.channel(); |
| channelGroup.forEach(ch -> { |
| if (ch != channel) { |
| ch.writeAndFlush("[客户端]" + channel.remoteAddress() + "发送:" + s + "\n"); |
| } else { |
| ch.writeAndFlush("[自己]" + channel.remoteAddress() + "发送:" + s + "\n"); |
| } |
| }); |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| ctx.close(); |
| } |
| } |
| package nettygroupchat; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.ChannelInboundHandlerAdapter; |
| import io.netty.handler.timeout.IdleStateEvent; |
| |
| public class HeartBeatHandler extends ChannelInboundHandlerAdapter { |
| @Override |
| public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { |
| if (evt instanceof IdleStateEvent) { |
| IdleStateEvent event = (IdleStateEvent) evt; |
| String eventType = ""; |
| switch (event.state()) { |
| case READER_IDLE: |
| eventType = "读空闲"; |
| break; |
| case WRITER_IDLE: |
| eventType = "写空闲"; |
| break; |
| case ALL_IDLE: |
| eventType = "读写空闲"; |
| break; |
| } |
| System.out.println(ctx.channel().remoteAddress() + " 超时时间 " + eventType); |
| } |
| } |
| } |
客户端
| package nettygroupchat; |
| |
| import io.netty.bootstrap.Bootstrap; |
| import io.netty.channel.*; |
| 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.util.Scanner; |
| |
| public class GroupChatClient { |
| private final String HOST; |
| private final int PORT; |
| |
| public GroupChatClient(String HOST, int PORT) { |
| this.HOST = HOST; |
| this.PORT = PORT; |
| } |
| |
| public void run() { |
| EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); |
| try { |
| Bootstrap bootstrap = new Bootstrap() |
| .group(eventLoopGroup) |
| .channel(NioSocketChannel.class) |
| .handler(new ChannelInitializer<SocketChannel>() { |
| @Override |
| protected void initChannel(SocketChannel socketChannel) throws Exception { |
| ChannelPipeline pipeline = socketChannel.pipeline(); |
| pipeline.addLast(new StringDecoder()); |
| pipeline.addLast(new StringEncoder()); |
| pipeline.addLast(new GroupChatClientHandler()); |
| } |
| }); |
| |
| ChannelFuture channelFuture = bootstrap.connect(HOST, PORT).sync(); |
| Channel channel = channelFuture.channel(); |
| System.out.println(channel.remoteAddress() + "登录"); |
| Scanner scanner = new Scanner(System.in); |
| while (scanner.hasNextLine()) { |
| channel.writeAndFlush(scanner.nextLine() + "\n"); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| eventLoopGroup.shutdownGracefully(); |
| } |
| |
| } |
| |
| public static void main(String[] args) { |
| new GroupChatClient("127.0.0.1", 7788).run(); |
| } |
| } |
- GroupChatClientHandler.java
| package nettygroupchat; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| |
| public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> { |
| @Override |
| protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { |
| System.out.println(s.trim()); |
| } |
| } |
Protobuf
Handler链调用机制
- 编解码handler接收到数据类型必须和待处理的数据类型一致,否则不会执行
- 解码器解码时,需要判断byteBuf中的数据是否足够
编码器
| package inboundhandler; |
| |
| import io.netty.buffer.ByteBuf; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.handler.codec.MessageToByteEncoder; |
| |
| public class MyLongToByteEncoder extends MessageToByteEncoder<Long> { |
| @Override |
| protected void encode(ChannelHandlerContext channelHandlerContext, Long aLong, ByteBuf byteBuf) throws Exception { |
| System.out.println("编码器被调用"); |
| byteBuf.writeLong(aLong); |
| } |
| } |
解码器
| package inboundhandler; |
| |
| import io.netty.buffer.ByteBuf; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.handler.codec.ByteToMessageDecoder; |
| |
| import java.util.List; |
| |
| public class MyByteToLongDecoder extends ByteToMessageDecoder { |
| |
| |
| @Override |
| protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { |
| System.out.println("解码器被调用"); |
| |
| if (byteBuf.readableBytes() >= 8) { |
| list.add(byteBuf.readLong()); |
| } |
| } |
| } |
服务器
| package inboundhandler; |
| |
| import io.netty.bootstrap.ServerBootstrap; |
| import io.netty.channel.ChannelFuture; |
| import io.netty.channel.EventLoopGroup; |
| import io.netty.channel.nio.NioEventLoopGroup; |
| import io.netty.channel.socket.nio.NioServerSocketChannel; |
| |
| public class MyServer { |
| public static void main(String[] args) { |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| try { |
| ServerBootstrap serverBootstrap = new ServerBootstrap() |
| .group(bossGroup, workerGroup) |
| .channel(NioServerSocketChannel.class) |
| .childHandler(new MyServerChannelInitializer()); |
| ChannelFuture sync = serverBootstrap.bind(9898).sync(); |
| sync.channel().closeFuture().sync(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| bossGroup.shutdownGracefully(); |
| workerGroup.shutdownGracefully(); |
| } |
| } |
| } |
- MyServerChannelInitializer.java
| package inboundhandler; |
| |
| import io.netty.channel.ChannelInitializer; |
| import io.netty.channel.ChannelPipeline; |
| import io.netty.channel.socket.SocketChannel; |
| |
| public class MyServerChannelInitializer extends ChannelInitializer<SocketChannel> { |
| @Override |
| protected void initChannel(SocketChannel socketChannel) throws Exception { |
| ChannelPipeline pipeline = socketChannel.pipeline(); |
| |
| pipeline.addLast(new MyByteToLongDecoder()); |
| pipeline.addLast(new MyLongToByteEncoder()); |
| pipeline.addLast(new MyServerHandler()); |
| } |
| } |
| package inboundhandler; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| |
| public class MyServerHandler extends SimpleChannelInboundHandler<Long> { |
| |
| @Override |
| protected void channelRead0(ChannelHandlerContext channelHandlerContext, Long aLong) throws Exception { |
| System.out.println("从客户端" + channelHandlerContext.channel().remoteAddress() + "读取到long:" + aLong); |
| System.out.println("服务器发送消息"); |
| |
| channelHandlerContext.writeAndFlush(8828L); |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| cause.printStackTrace(); |
| ctx.close(); |
| } |
| } |
客户端
| package inboundhandler; |
| |
| import io.netty.bootstrap.Bootstrap; |
| import io.netty.channel.ChannelFuture; |
| import io.netty.channel.nio.NioEventLoopGroup; |
| import io.netty.channel.socket.nio.NioSocketChannel; |
| |
| public class MyClient { |
| public static void main(String[] args) { |
| NioEventLoopGroup eventExecutors = new NioEventLoopGroup(); |
| try { |
| Bootstrap bootstrap = new Bootstrap() |
| .group(eventExecutors) |
| .channel(NioSocketChannel.class) |
| .handler(new MyClientChannelInitializer()); |
| ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9898); |
| channelFuture.channel().closeFuture().sync(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| eventExecutors.shutdownGracefully(); |
| } |
| } |
| } |
- MyClientChannelInitializer.java
| package inboundhandler; |
| |
| import io.netty.channel.ChannelInitializer; |
| import io.netty.channel.ChannelPipeline; |
| import io.netty.channel.socket.SocketChannel; |
| |
| public class MyClientChannelInitializer extends ChannelInitializer<SocketChannel> { |
| @Override |
| protected void initChannel(SocketChannel socketChannel) throws Exception { |
| ChannelPipeline pipeline = socketChannel.pipeline(); |
| |
| pipeline.addLast(new MyByteToLongDecoder()); |
| pipeline.addLast(new MyLongToByteEncoder()); |
| pipeline.addLast(new MyClientHandler()); |
| } |
| } |
| package inboundhandler; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| |
| public class MyClientHandler extends SimpleChannelInboundHandler<Long> { |
| @Override |
| protected void channelRead0(ChannelHandlerContext channelHandlerContext, Long aLong) throws Exception { |
| System.out.println("收到服务器[" + channelHandlerContext.channel().remoteAddress() + "]消息:" + aLong); |
| } |
| |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| System.out.println("MyClientHandler发送消息"); |
| ctx.writeAndFlush(1111L); |
| |
| |
| } |
| } |
其他编解码器
- ReplayingDecoder:不需要判断byteBuf是否足够读取
- LineBasedFrameDecoder:使用行尾控制字符作为分隔符(\n或者\r\n)
- DelimiterBasedFrameDecoder:自定义分隔符
- HttpObjectDecoder:HTTP数据解码器
- LengthFieldBasedFrameDecoder:指定长度标识整包信息,避免粘包半包
- ......
粘包
协议包
| package protocoltcp; |
| |
| public class MessageProtocol { |
| private int len ; |
| |
| public MessageProtocol() { |
| } |
| |
| public MessageProtocol(int len, byte[] content) { |
| this.len = len; |
| this.content = content; |
| } |
| |
| private byte[] content; |
| |
| public int getLen() { |
| return len; |
| } |
| |
| public void setLen(int len) { |
| this.len = len; |
| } |
| |
| public byte[] getContent() { |
| return content; |
| } |
| |
| public void setContent(byte[] content) { |
| this.content = content; |
| } |
| } |
编解码器
| package protocoltcp; |
| |
| import io.netty.buffer.ByteBuf; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.handler.codec.ReplayingDecoder; |
| |
| import java.util.List; |
| |
| public class MyMessageDecoder extends ReplayingDecoder<MessageProtocol> { |
| @Override |
| protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { |
| System.out.println("解码器被调用"); |
| int len = in.readInt(); |
| byte[] bytes = new byte[len]; |
| in.readBytes(bytes); |
| |
| MessageProtocol messageProtocol = new MessageProtocol(len, bytes); |
| out.add(messageProtocol); |
| } |
| } |
| package protocoltcp; |
| |
| import io.netty.buffer.ByteBuf; |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.handler.codec.MessageToByteEncoder; |
| |
| public class MyMessageEncoder extends MessageToByteEncoder<MessageProtocol> { |
| |
| @Override |
| protected void encode(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) throws Exception { |
| System.out.println("编码器被调用"); |
| out.writeInt(msg.getLen()); |
| out.writeBytes(msg.getContent()); |
| } |
| } |
服务器
| package protocoltcp; |
| |
| 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; |
| |
| public class NettyServer { |
| public static void main(String[] args) throws Exception { |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| try { |
| ServerBootstrap bootstrap = new ServerBootstrap(); |
| bootstrap.group(bossGroup, workerGroup) |
| .channel(NioServerSocketChannel.class) |
| .option(ChannelOption.SO_BACKLOG, 128) |
| .childOption(ChannelOption.SO_KEEPALIVE, true) |
| .childHandler(new ChannelInitializer<SocketChannel>() { |
| |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ch.pipeline().addLast(new MyMessageDecoder()); |
| ch.pipeline().addLast(new MyMessageEncoder()); |
| ch.pipeline().addLast(new NettyServerHandler()); |
| } |
| }); |
| |
| System.out.println(".....服务器 is ready..."); |
| ChannelFuture channelFuture = bootstrap.bind(8888).sync(); |
| channelFuture.addListener(new ChannelFutureListener() { |
| @Override |
| public void operationComplete(ChannelFuture future) throws Exception { |
| if (channelFuture.isSuccess()) { |
| System.out.println("服务已启动,端口号为8888..."); |
| } else { |
| System.out.println("服务启动失败..."); |
| } |
| } |
| }); |
| channelFuture.channel().closeFuture().sync(); |
| } finally { |
| bossGroup.shutdownGracefully(); |
| workerGroup.shutdownGracefully(); |
| } |
| } |
| } |
| package protocoltcp; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| |
| import java.nio.charset.StandardCharsets; |
| import java.util.UUID; |
| |
| public class NettyServerHandler extends SimpleChannelInboundHandler<MessageProtocol> { |
| private int count; |
| |
| @Override |
| protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { |
| System.out.println("服务器收到:" + "长度=" + msg.getLen() + ", " + "内容=" + new String(msg.getContent(), StandardCharsets.UTF_8)); |
| System.out.println("服务器收到消息包数量:" + ++this.count); |
| |
| String string = UUID.randomUUID().toString(); |
| byte[] content = string.getBytes(StandardCharsets.UTF_8); |
| int len = content.length; |
| MessageProtocol messageProtocol = new MessageProtocol(len, content); |
| ctx.writeAndFlush(messageProtocol); |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| cause.printStackTrace(); |
| ctx.close(); |
| } |
| } |
客户端
| package protocoltcp; |
| |
| import io.netty.bootstrap.Bootstrap; |
| import io.netty.channel.ChannelFuture; |
| import io.netty.channel.ChannelInitializer; |
| import io.netty.channel.ChannelPipeline; |
| import io.netty.channel.EventLoopGroup; |
| import io.netty.channel.nio.NioEventLoopGroup; |
| import io.netty.channel.socket.SocketChannel; |
| import io.netty.channel.socket.nio.NioSocketChannel; |
| |
| import java.io.PrintWriter; |
| |
| public class NettyClient { |
| public static void main(String[] args) throws Exception { |
| EventLoopGroup group = new NioEventLoopGroup(); |
| try { |
| Bootstrap bootstrap = new Bootstrap(); |
| bootstrap.group(group) |
| .channel(NioSocketChannel.class) |
| .handler(new ChannelInitializer<SocketChannel>() { |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ChannelPipeline pipeline = ch.pipeline(); |
| pipeline.addLast(new MyMessageEncoder()); |
| pipeline.addLast(new MyMessageDecoder()); |
| pipeline.addLast(new NettyClientHandler()); |
| } |
| }); |
| System.out.println("客户端 ok.."); |
| ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).sync(); |
| channelFuture.channel().closeFuture().sync(); |
| } finally { |
| group.shutdownGracefully(); |
| } |
| } |
| } |
| package protocoltcp; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.SimpleChannelInboundHandler; |
| |
| import java.nio.charset.StandardCharsets; |
| |
| public class NettyClientHandler extends SimpleChannelInboundHandler<MessageProtocol> { |
| private int count; |
| |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| for (int i = 0; i < 1000; i++) { |
| String s = "哈哈,呵呵。"; |
| byte[] bytes = s.getBytes(StandardCharsets.UTF_8); |
| int len = bytes.length; |
| MessageProtocol messageProtocol = new MessageProtocol(len, bytes); |
| ctx.writeAndFlush(messageProtocol); |
| } |
| } |
| |
| @Override |
| protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { |
| System.out.println("第" + ++count + "个数据包"); |
| System.out.println("客户端收到:长度=" + msg.getLen() + ", 内容=" + new String(msg.getContent(), StandardCharsets.UTF_8) + " "); |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| cause.printStackTrace(); |
| ctx.close(); |
| } |
| } |
RPC框架
公共接口
| package rpc.publicinterface; |
| |
| public interface HelloService { |
| String hello(String msg); |
| } |
服务器
| package rpc.provider; |
| |
| import rpc.netty.NettyServer; |
| |
| public class ServerBootstrap { |
| public static void main(String[] args) { |
| NettyServer.startServer("127.0.0.1", 7777); |
| } |
| } |
- 接口实现类HelloServiceImpl.java
| package rpc.provider; |
| |
| import rpc.publicinterface.HelloService; |
| |
| public class HelloServiceImpl implements HelloService { |
| |
| @Override |
| public String hello(String msg) { |
| System.out.println("收到客户端消息:" + msg); |
| if (msg != null) { |
| return "服务器返回:收到了" + msg; |
| } |
| return "服务器返回:没收到"; |
| } |
| } |
| package rpc.netty; |
| |
| import io.netty.bootstrap.ServerBootstrap; |
| import io.netty.channel.ChannelFuture; |
| 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.NioServerSocketChannel; |
| import io.netty.handler.codec.string.StringDecoder; |
| import io.netty.handler.codec.string.StringEncoder; |
| |
| public class NettyServer { |
| public static void startServer(String host, int port) { |
| NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| NioEventLoopGroup workerGroup = new NioEventLoopGroup(); |
| try { |
| ServerBootstrap serverBootstrap = new ServerBootstrap() |
| .group(bossGroup, workerGroup) |
| .channel(NioServerSocketChannel.class) |
| .childHandler(new ChannelInitializer<SocketChannel>() { |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ChannelPipeline pipeline = ch.pipeline(); |
| pipeline.addLast(new StringDecoder()); |
| pipeline.addLast(new StringEncoder()); |
| pipeline.addLast(new NettyServerHandler()); |
| } |
| }); |
| |
| ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync(); |
| System.out.println("服务器启动"); |
| channelFuture.channel().closeFuture().sync(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| bossGroup.shutdownGracefully(); |
| workerGroup.shutdownGracefully(); |
| } |
| } |
| } |
| package rpc.netty; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.ChannelInboundHandlerAdapter; |
| import rpc.provider.HelloServiceImpl; |
| |
| public class NettyServerHandler extends ChannelInboundHandlerAdapter { |
| |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| System.out.println("客户端:" + ctx.channel().remoteAddress() + "连接"); |
| } |
| |
| @Override |
| public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
| System.out.println("客户端:" + ctx.channel().remoteAddress() + "断开连接"); |
| } |
| |
| @Override |
| public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| |
| System.out.println("msg=" + msg); |
| |
| if (msg.toString().startsWith("HelloService#hello#")) { |
| String result = new HelloServiceImpl().hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1)); |
| ctx.writeAndFlush(result); |
| } |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| ctx.close(); |
| } |
| } |
客户端
| package rpc.customer; |
| |
| import rpc.netty.NettyClient; |
| import rpc.publicinterface.HelloService; |
| |
| public class ClientBootstrap { |
| public static final String providerName = "HelloService#hello#"; |
| |
| public static void main(String[] args) { |
| NettyClient nettyClient = new NettyClient(); |
| |
| HelloService bean = (HelloService) nettyClient.getBean(HelloService.class, providerName); |
| |
| String res = bean.hello("哈哈"); |
| System.out.println("调用结果:" + res); |
| } |
| } |
| package rpc.netty; |
| |
| import io.netty.bootstrap.Bootstrap; |
| import io.netty.channel.*; |
| 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.lang.reflect.Proxy; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| |
| public class NettyClient { |
| private static final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); |
| private static NettyClientHandler clientHandler; |
| |
| |
| public Object getBean(final Class<?> serviceClass, final String providerName) { |
| return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), |
| new Class<?>[]{serviceClass}, (proxy, method, args) -> { |
| |
| if (clientHandler == null) { |
| startClient(); |
| } |
| |
| clientHandler.setPara(providerName + args[0]); |
| return executor.submit(clientHandler).get(); |
| }); |
| } |
| |
| private static void startClient() { |
| clientHandler = new NettyClientHandler(); |
| NioEventLoopGroup eventExecutors = new NioEventLoopGroup(); |
| Bootstrap bootstrap = new Bootstrap() |
| .group(eventExecutors) |
| .channel(NioSocketChannel.class) |
| .option(ChannelOption.TCP_NODELAY, true) |
| .handler(new ChannelInitializer<SocketChannel>() { |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ChannelPipeline pipeline = ch.pipeline(); |
| pipeline.addLast(new StringDecoder()); |
| pipeline.addLast(new StringEncoder()); |
| pipeline.addLast(clientHandler); |
| } |
| }); |
| try { |
| ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7777).sync(); |
| channelFuture.addListener(new ChannelFutureListener() { |
| @Override |
| public void operationComplete(ChannelFuture future) throws Exception { |
| if (channelFuture.isSuccess()) { |
| System.out.println("连接成功"); |
| } else { |
| System.out.println("连接失败"); |
| } |
| } |
| }); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| package rpc.netty; |
| |
| import io.netty.channel.ChannelHandlerContext; |
| import io.netty.channel.ChannelInboundHandlerAdapter; |
| |
| import java.util.concurrent.Callable; |
| |
| public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable { |
| |
| private ChannelHandlerContext context; |
| private String result; |
| private String para; |
| |
| |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| |
| context = ctx; |
| } |
| |
| |
| @Override |
| public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| result = msg.toString(); |
| |
| notify(); |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| ctx.close(); |
| } |
| |
| |
| |
| |
| @Override |
| public synchronized Object call() throws Exception { |
| context.writeAndFlush(para); |
| |
| wait(); |
| return result; |
| } |
| |
| public String getPara() { |
| return para; |
| } |
| |
| |
| public void setPara(String para) { |
| this.para = para; |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步