【Netty】bio和nio写时间服务器
一、bio写的时间服务器
1、一个请求对应一个线程版本
server端代码
package com.spring.test.service.netty.server.bio; import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /** * @author <a href="">尚晓飞</a> * @date 7:47 PM 2019/7/19 */ public class BIOTimerServer { public static void main(String[] args) { //启动服务端 BIOTimerServer server=new BIOTimerServer(); server.startServer(); } private int port = 8080; private ServerSocket serverSocket; public void startServer() { try { System.out.println("=====服务初始化开始====="); serverSocket = new ServerSocket(port); System.out.println("=====服务初始化完毕====="); while (true) { Socket socket = serverSocket.accept(); System.out.println("=====服务端接收一个请求====="); new SocketHandlerThread(socket).start(); } } catch (Exception e) { e.printStackTrace(); } finally { if (this.serverSocket != null) { try { this.serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } /** * socket处理线程 */ class SocketHandlerThread extends Thread { private Socket socket; SocketHandlerThread(Socket socket) { this.socket = socket; } @Override public void run() { System.out.println("======请求处理开始========"); BufferedReader in = null; PrintWriter out = null; try { //step1:初始化输入流(读请求) in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //step2:初始化输出流(写响应) out = new PrintWriter(this.socket.getOutputStream(),true); //step3:读取请求内容 StringBuilder req = new StringBuilder(); String temp; while (true){ temp= in.readLine(); if(StringUtils.isNotBlank(temp)){ req.append(temp); break; } } //step4:处理请求 System.out.println("请求内容为=>" + req.toString()); String response = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"); System.out.println("响应内容为=>" + response); //step5:响应请求 out.println(response); } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { out.close(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("======请求处理结束========"); } }
client端代码
package com.spring.test.service.netty.server.bio; import org.apache.commons.lang3.StringUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /** * @author <a href="">尚晓飞</a> * @date 6:19 PM 2019/7/21 */ public class BIOTimerClient { public static void main(String[] args) { //客户端发送请求 BIOTimerClient bioTimerClient=new BIOTimerClient(8080,"127.0.0.1"); String resp=bioTimerClient.req("query Current Time"); System.out.println("响应内容为=>"+resp); } private int port; private String localhost; public BIOTimerClient(int port, String address) { this.port = port; this.localhost = address; } public String req(String req) { Socket socket = null; BufferedReader in = null; PrintWriter out = null; StringBuilder response; try { socket = new Socket(this.localhost, this.port); //发送请求的输出流 out = new PrintWriter(socket.getOutputStream(),true); //接收响应的输入流 in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //发送请求 System.out.println("==开始发送请求==>" + req); out.println(req); //读取响应 response = new StringBuilder(); String temp; while (StringUtils.isNotBlank(temp = in.readLine())) { response.append(temp); } System.out.println("==客户端接收响应成功=="); return response.toString(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if(in!=null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(out!=null){ out.close(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
1、服务端使用线程池版本
server端代码
package com.spring.test.service.netty.server.bio; import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang3.StringUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * @author <a href="">尚晓飞</a> * @date 7:47 PM 2019/7/19 */ public class BIOTimerServer { public static void main(String[] args) { //启动服务端 BIOTimerServer server = new BIOTimerServer(); server.startServer(); } private int port = 8080; private ServerSocket serverSocket; /** * 初始化一个工作线程池,用来处理客户端的请求 * 优点:相对于第一版的一个请求一个工作线程,不会出现客户单大量请求,导致服务端开启大量线程,把服务端资源耗尽的问题。 * 缺点:固定的线程池大小,代表服务端有一定的处理极限(并发数就是线程池的最大数),一但出现服务端或客户端的I/O因为网络原因出现阻塞 * 则服务端线程资源就会被长时间占用,线程池占满,服务端无法处理新的请求,客户端就会出现超时或访问被拒绝的情况 */ private Executor executor= Executors.newFixedThreadPool(20); public void startServer() { try { System.out.println("=====服务初始化开始====="); serverSocket = new ServerSocket(port); System.out.println("=====服务初始化完毕====="); while (true) { Socket socket = serverSocket.accept(); System.out.println("=====服务端接收一个请求====="); //模拟一个工作线程池,用来处理客户端请求 executor.execute(new SocketHandlerThread(socket)); } } catch (Exception e) { e.printStackTrace(); } finally { if (this.serverSocket != null) { try { this.serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } /** * socket处理线程 */ class SocketHandlerThread extends Thread { private Socket socket; SocketHandlerThread(Socket socket) { this.socket = socket; } @Override public void run() { System.out.println("======请求处理开始========"); BufferedReader in = null; PrintWriter out = null; try { //step1:初始化输入流(读请求) in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //step2:初始化输出流(写响应) out = new PrintWriter(this.socket.getOutputStream(), true); //step3:读取请求内容 StringBuilder req = new StringBuilder(); String temp; while (true) { temp = in.readLine(); if (StringUtils.isNotBlank(temp)) { req.append(temp); break; } } //step4:处理请求 System.out.println("请求内容为=>" + req.toString()); String response = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"); System.out.println("响应内容为=>" + response); //step5:响应请求 out.println(response); } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { out.close(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("======请求处理结束========"); } }
client端代码
package com.spring.test.service.netty.server.bio; import org.apache.commons.lang3.StringUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /** * @author <a href="">尚晓飞</a> * @date 6:19 PM 2019/7/21 */ public class BIOTimerClient { public static void main(String[] args) { //客户端发送请求 BIOTimerClient bioTimerClient=new BIOTimerClient(8080,"127.0.0.1"); String resp=bioTimerClient.req("query Current Time"); System.out.println("响应内容为=>"+resp); } private int port; private String localhost; public BIOTimerClient(int port, String address) { this.port = port; this.localhost = address; } public String req(String req) { Socket socket = null; BufferedReader in = null; PrintWriter out = null; StringBuilder response; try { socket = new Socket(this.localhost, this.port); //发送请求的输出流 out = new PrintWriter(socket.getOutputStream(),true); //接收响应的输入流 in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //发送请求 System.out.println("==开始发送请求==>" + req); out.println(req); //读取响应 response = new StringBuilder(); String temp; while (StringUtils.isNotBlank(temp = in.readLine())) { response.append(temp); } System.out.println("==客户端接收响应成功=="); return response.toString(); } catch (IOException e) { e.printStackTrace(); return null; } finally { if(in!=null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(out!=null){ out.close(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
二、nio写的时间服务器
server端代码
package com.spring.test.service.netty.server.nio; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateFormatUtils; 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.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; /** * @author <a href="">尚晓飞</a> * @date 7:24 PM 2019/7/21 */ public class NIOTimerServer extends Thread { public static void main(String[] args) { //启动服务端 NIOTimerServer server=new NIOTimerServer(8080); server.start(); } private int port; /** * 多路复用器 */ private Selector selector; /** * 服务端通信的管道 */ private ServerSocketChannel serverSocketChannel; /** * 服务端是否停止 */ private volatile boolean isStop; public NIOTimerServer(int port) { try { System.out.println("========初始化服务端开始========="); this.port=port; //新建多路复用器 selector = Selector.open(); //新建服务端管道,用于监听客户端连接 serverSocketChannel = ServerSocketChannel.open(); //将I/O设置为非阻塞模式 serverSocketChannel.configureBlocking(false); //绑定端口号 serverSocketChannel.socket().bind(new InetSocketAddress(this.port), 1024); //将serverSocketChannel注册到多路复用器上,用于监听客户端接入的OP_ACCEPT事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("========初始化服务端结束=========="); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while (!isStop) { //通过多路复用器,监听网络事件 try { //如果没有网络事件,则阻塞(1秒钟退出阻塞),若存在网络事件,则直接向下一句代码执行。 selector.select(1000); //获取已准备就绪的网络事件 Set<SelectionKey> selectionKeys = selector.selectedKeys(); //遍历已经就绪的网络事件,并进行处理 Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator(); SelectionKey selectionKey; while (selectionKeyIterator.hasNext()) { selectionKey = selectionKeyIterator.next(); selectionKeyIterator.remove(); handlerKey(selectionKey); } } catch (IOException e) { e.printStackTrace(); } } if(selector!=null){ try { selector.close(); } catch (IOException e) { e.printStackTrace(); } } } private void handlerKey(SelectionKey selectionKey) throws IOException { //判断selectionKey是否是有效的 if (selectionKey.isValid()) { if (selectionKey.isAcceptable()) { //客户端新接入 ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel(); SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); //注册监听读取请求的网络事件 socketChannel.register(selector, SelectionKey.OP_READ); } if (selectionKey.isReadable()) { //当前网络事件,是读取客户端的请求内容 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); int readBytes = socketChannel.read(byteBuffer); if (readBytes > 0) { //读取的客户端请求内容 byteBuffer.flip(); byte[] bytes=new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); String requestBoy=new String(bytes,"utf-8"); System.out.println("客户端请求内容=>"+requestBoy); //处理请求 String response = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"); //响应请求 doResponse(socketChannel,response); }else if(readBytes<0){ //客户端链路关闭 selectionKey.cancel(); socketChannel.close(); }else { //读到0字节忽略 } } } } private void doResponse(SocketChannel socketChannel,String response) throws IOException { if(StringUtils.isNotBlank(response)){ byte[] resp=response.getBytes(); ByteBuffer byteBuffer=ByteBuffer.allocate(resp.length); byteBuffer.put(resp); byteBuffer.flip(); socketChannel.write(byteBuffer); } } }
client端代码
package com.spring.test.service.netty.server.nio; 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.util.Iterator; import java.util.Set; /** * @author <a href="">尚晓飞</a> * @date 8:48 PM 2019/7/21 */ public class NIOTimerClient extends Thread{ public static void main(String[] args) { NIOTimerClient nioTimerClient=new NIOTimerClient("127.0.0.1",8080); nioTimerClient.start(); } private String address; private int port; private Selector selector; private SocketChannel socketChannel; private volatile boolean isStop; NIOTimerClient(String address,int port){ this.address=address; this.port=port; try { selector= Selector.open(); socketChannel=SocketChannel.open(); socketChannel.configureBlocking(false); }catch (Exception e){ } } @Override public void run() { try { //向服务端发送请求 doConnection(); }catch (Exception e){ e.printStackTrace(); } while (!isStop){ try { selector.select(1000); Set<SelectionKey> selectionKeys= selector.selectedKeys(); Iterator<SelectionKey> selectionKeyIterator= selectionKeys.iterator(); SelectionKey selectionKey=null; while (selectionKeyIterator.hasNext()){ selectionKey=selectionKeyIterator.next(); selectionKeyIterator.remove(); try { handler(selectionKey); }catch (Exception e){ if(selectionKey!=null){ selectionKey.cancel(); if(selectionKey.channel()!=null){ selectionKey.channel().close(); } } } } }catch (Exception e){ e.printStackTrace(); }finally { } } if(selector!=null){ try { selector.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 向服务端发起连接 * @throws IOException */ private void doConnection() throws IOException { if(socketChannel.connect(new InetSocketAddress(address,port))){ //连接成功 socketChannel.register(selector, SelectionKey.OP_READ); doRequest(socketChannel); }else { socketChannel.register(selector,SelectionKey.OP_CONNECT); } } /** * 向服务端发起请求 */ private void doRequest(SocketChannel socketChannel) throws IOException { byte[] request="query current time!".getBytes(); ByteBuffer byteBuffer=ByteBuffer.allocate(request.length); byteBuffer.put(request); byteBuffer.flip(); socketChannel.write(byteBuffer); if(!byteBuffer.hasRemaining()){ System.out.println("向服务端请求发送成功"); } } private void handler(SelectionKey selectionKey) throws IOException { if(selectionKey.isValid()){ SocketChannel socketChannel= (SocketChannel) selectionKey.channel(); //当前是网络连接完成事件 if(selectionKey.isConnectable()){ if(socketChannel.finishConnect()){ socketChannel.register(selector,SelectionKey.OP_READ); doRequest(socketChannel); }else { //连接失败 System.out.println("连接失败"); } } //当前是网络读取响应事件 if(selectionKey.isReadable()){ ByteBuffer byteBuffer=ByteBuffer.allocate(1024); int readResp=socketChannel.read(byteBuffer); if(readResp>0){ byteBuffer.flip(); byte[] bytes=new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); String resp=new String(bytes,"utf-8"); System.out.println("服务端响应为=>"+resp); isStop=true; }else if(readResp<0){ //对端链路关闭 selectionKey.cancel(); socketChannel.close(); }else { //读到0字节忽略 } } } } }
NIO比BIO的编程复杂度高,为什么应用却广泛使用?
- 网络连接I/O非阻塞:客户端的链接操作是异步的,可以通过在多路复用器注册OP_CONNECT等后续结果,不需要像BIO客户端那样被同步阻塞。
- 网络读写I/O非阻塞,异步化:SocketChannel的读写操作都是异步的,如果没有可读写的数据,它不会同步等待,直接返回。这样I/O通信线程就可以处理其他链路,不需要同步等待这个链路可用。
- 网络连接数无限制:线程模型优化:由于JDK的Selector在Liunx等主流操作系统上通过epoll实现,它没有句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制),这意味着一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降。因此,它非常适合做高性能,高负载的网络服务器。
三、AIO写的时间服务器
1、server代码
package com.spring.test.service.netty.server.aio; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateFormatUtils; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.CountDownLatch; /** * * @date 12:55 PM 2019/8/11 */ public class AsyncTimeServerHandler implements Runnable { private int port; CountDownLatch countDownLatch; AsynchronousServerSocketChannel asynchronousServerSocketChannel; public static void main(String[] args) { //启动时间服务器 Thread timerServer=new Thread(new AsyncTimeServerHandler(8080)); timerServer.start(); } public AsyncTimeServerHandler(int port) { this.port = port; try { asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open(); asynchronousServerSocketChannel.bind(new InetSocketAddress(port)); System.out.println("======The time server is start in port:" + port + "======"); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { countDownLatch = new CountDownLatch(1); doAccept(); try { countDownLatch.await(); } catch (Exception e) { e.printStackTrace(); } } public void doAccept() { //获取客户单接入的请求 asynchronousServerSocketChannel.accept(this,new AcceptCompletionHandler()); } } /** * 获取客户端连接的回调函数 */ class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHandler> { @Override public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) { //用于接收下一个客户端接入的请求 attachment.asynchronousServerSocketChannel.accept(attachment, this); //处理当前请求 ByteBuffer buffer=ByteBuffer.allocate(1024); result.read(buffer,buffer,new ReadCompletionHandler(result)); } @Override public void failed(Throwable exc, AsyncTimeServerHandler attachment) { //接收请求失败,则把服务端关闭 exc.printStackTrace(); attachment.countDownLatch.countDown(); } } /** * 读取连接的请求内容回调函数 */ class ReadCompletionHandler implements CompletionHandler<Integer,ByteBuffer>{ private AsynchronousSocketChannel asynchronousSocketChannel; public ReadCompletionHandler(AsynchronousSocketChannel asynchronousSocketChannel) { this.asynchronousSocketChannel = asynchronousSocketChannel; } @Override public void completed(Integer result, ByteBuffer attachment) { attachment.flip(); byte[] requestBody=new byte[attachment.remaining()]; attachment.get(requestBody); try { String req=new String(requestBody,"UTF-8"); System.out.println("The time server receive order:"+req); String currentTime="QUERY TIME ORDER".equals(req)? DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"):"BAD ORDER"; doWrite(currentTime); }catch (Exception e){ e.printStackTrace(); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { //关闭请求 try { this.asynchronousSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } private void doWrite(String currentTime){ if(StringUtils.isNotBlank(currentTime)){ byte[] bytes=currentTime.getBytes(); ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length); writeBuffer.put(bytes); writeBuffer.flip(); asynchronousSocketChannel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { //如果没有发送完,则继续发送 if(attachment.hasRemaining()){ asynchronousSocketChannel.write(attachment,attachment,this); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { //如果发送失败,则关闭channel try { asynchronousSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }); } } }
2、client代码
package com.spring.test.service.netty.server.aio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.CountDownLatch; /** * @author * @date 2:33 PM 2019/8/11 */ public class AsyncTimeClientHandler implements CompletionHandler<Void,AsyncTimeClientHandler>,Runnable{ private AsynchronousSocketChannel asynchronousSocketChannel; private String host; private int port; private CountDownLatch countDownLatch; public static void main(String[] args) { //启动客户单发送请求 Thread timerClient=new Thread(new AsyncTimeClientHandler("127.0.0.1",8080)); timerClient.start(); } public AsyncTimeClientHandler(String host,int port){ this.host=host; this.port=port; try { asynchronousSocketChannel=AsynchronousSocketChannel.open(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { countDownLatch=new CountDownLatch(1); asynchronousSocketChannel.connect(new InetSocketAddress(host,port),this,this); try { countDownLatch.await(); }catch (Exception e){ e.printStackTrace(); } try { asynchronousSocketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } @Override public void completed(Void result, AsyncTimeClientHandler attachment) { //连接成功,发送请求 byte[] req="QUERY TIME ORDER".getBytes(); ByteBuffer writeBuffer=ByteBuffer.allocate(req.length); writeBuffer.put(req); writeBuffer.flip(); asynchronousSocketChannel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { if(attachment.hasRemaining()){ //半包写,则继续发送,将请求数据完整发送给服务端 asynchronousSocketChannel.write(attachment,attachment,this); }else{ //写成功了,则需要读取服务端响应 ByteBuffer readBuffer=ByteBuffer.allocate(1024); asynchronousSocketChannel.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { //读取服务端响应成功 attachment.flip(); byte[] bytes=new byte[attachment.remaining()]; attachment.get(bytes); String responseBody; try { //读取响应成功 responseBody=new String(bytes,"UTF-8"); System.out.println("Now is:"+responseBody); //读取响应成功,关闭客户端连接 countDownLatch.countDown(); }catch (Exception e){ e.printStackTrace(); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { //读取服务端响应失败 try { asynchronousSocketChannel.close(); countDownLatch.countDown(); }catch (Exception e){ e.printStackTrace(); } } }); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { //向服务端发送请求报文失败,关闭客户端 try { asynchronousSocketChannel.close(); countDownLatch.countDown(); }catch (Exception e){ e.printStackTrace(); } } }); } @Override public void failed(Throwable exc, AsyncTimeClientHandler attachment) { //向服务端发起连接失败 try { asynchronousSocketChannel.close(); countDownLatch.countDown(); }catch (Exception e){ e.printStackTrace(); } } }
四、netty写时间服务器
jdkversion:1.8
nettyversion:5.0.0.Alpha1
1、server代码
package com.spring.test.service.netty.nettydemo.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * @date 4:13 PM 2019/8/11 */ public class TimerServer { public static void main(String[] args) { TimerServer timerServer=new TimerServer(); timerServer.bind(8080); } public void bind(int port){ //step1:配置服务端的NIO线程组(一个线程组用于服务端接收客户端连接,一个线程组用于进行socketChannel的网络读写) EventLoopGroup bossGroup=new NioEventLoopGroup(); EventLoopGroup workerGroup=new NioEventLoopGroup(); try { //netty用于启动NIO服务的辅助启动类,目的四降低服务端开发复杂度 ServerBootstrap serverBootstrap=new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,1024) .childHandler(new ChildChannelHandler()); //step2:绑定端口,同步等待成功 ChannelFuture f=serverBootstrap.bind(port).sync(); //step3:等待服务端监听端口关闭 f.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { //step4:优雅退出,释放线程池资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } package com.spring.test.service.netty.nettydemo.server; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; /** * @author * @date 4:21 PM 2019/8/11 */ public class ChildChannelHandler extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new TimerServerHandler()); } } package com.spring.test.service.netty.nettydemo.server; import org.apache.commons.lang.time.DateFormatUtils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; /** * @author * @date 4:26 PM 2019/8/11 */ public class TimerServerHandler extends ChannelHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //读取请求 ByteBuf buffer= (ByteBuf) msg; byte[] req=new byte[buffer.readableBytes()]; buffer.readBytes(req); String reqbody=new String(req,"utf-8"); System.out.println("The time server receive order:"+reqbody); String currentTime="QUERY TIME ORDER".equals(reqbody)? DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"):"BAD ORDER"; //请求响应 ByteBuf resp=Unpooled.copiedBuffer(currentTime.getBytes()); System.out.println("The time server resp order:"+currentTime); //将相应结果,异步发送给客户端 ctx.write(resp); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { //将消息发送队列中的消息写入socketChannel中发送给对方。 ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
2、client代码
package com.spring.test.service.netty.nettydemo.client; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; /** * @author * @date 5:41 PM 2019/8/11 */ public class TimerClient { public static void main(String[] args) throws InterruptedException { TimerClient timerClient=new TimerClient(); timerClient.connect(8080,"127.0.0.1"); } public void connect(int port,String host) throws InterruptedException { //step1:配置NIO客户端线程组 EventLoopGroup group=new NioEventLoopGroup(); try { Bootstrap bootstrap=new Bootstrap(); bootstrap.group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY,true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new TimerClientHandler()); } }); //发起异步连接操作(调用同步方法,等待连接成功) ChannelFuture f=bootstrap.connect(host,port).sync(); //等待客户端链路关闭 f.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { //优雅的退出,释放NIO线程组 group.shutdownGracefully(); } } } package com.spring.test.service.netty.nettydemo.client; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; /** * @author * @date 5:49 PM 2019/8/11 */ public class TimerClientHandler extends ChannelHandlerAdapter { private final ByteBuf firstMessage; public TimerClientHandler(){ byte[] req="QUERY TIME ORDER".getBytes(); firstMessage= Unpooled.buffer(req.length); firstMessage.writeBytes(req); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("释放资源"); ctx.close(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //发送请求 ctx.writeAndFlush(firstMessage); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //读取响应 ByteBuf buf= (ByteBuf) msg; byte[] resp=new byte[buf.readableBytes()]; buf.readBytes(resp); String responseBody=new String(resp,"utf-8"); System.out.println("Now time is:"+responseBody); } }
浙公网安备 33010602011771号