首先,整理NIO进行服务端开发的步骤:

  (1)创建ServerSocketChannel,配置它为非阻塞模式

  (2)绑定监听配置TCP参数backlog的大小

  (3)创建一个独立的I/O线程,用于轮询多路复用器Selector

  (4)创建Selector,将之前创建的ServerSocketChannel注册到Selector上监听SelectionKeyACCEPT

  (5)启动I/O线程,在循环体中执行Selector.select()方法,轮训就绪的Channel。

  (6)当轮询到了处于就绪状态的Channel时,需要对其进行判断,如果是OP_ACCEPT状态,说明是新的客户端接入,则调用ServerSocketChannel.accept()方法接受新的客户端。

  (7)设置新接入的客户端链路SocketChannel为非阻塞模式,配置其他的一些TCP参数。

  (8)将SocketChannel注册到Selector,监听OP_READ操作位

  (9)如果轮询的Channel为OP_READ,则说明SocketChannel中有新的就绪的数据包需要读取,则构造ByteBuffer对象,读取数据包。

  (10)如果轮询的Channel为OP_WRITE,则说明还有数据没有发送完成,需要继续发送。  

 Netty时间服务器服务端 TimeServer:

 package netty;

 import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel; public class TimeServer { public void bind(int port) throws Exception{
//配置服务端的NIO线程组 一个用于服务端接收客户端的连接,另一个用于进行SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
//绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
//等待服务器监听端口关闭
f.channel().closeFuture().sync();
}finally{
//优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
protected void initChannel(SocketChannel arg0) throws Exception{
arg0.pipeline().addLast(new TimeServerHandler());
}
}
}

ServerBootstrap是Netty用于启动NIO服务端的辅助类,目的是降低服务端的开发难度。

绑定childChannelHandler,其作用类似于Reactor模式中的handler类,主要用于处理网络I/O事件,例如记录日志、对消息进行编解码等。

使用bind绑定监听端口,随后,调用它的同步阻塞方法sync等待绑定操作完成,完成后Netty会返回一个ChannelFuture,主要用于异步操作的通知回调

Netty时间服务器服务端 TimeServerHandler:

 package netty;

 import java.io.IOException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class TimeServerHandler extends ChannelHandlerAdapter{ public void channelRead(ChannelHandlerContext ctx,Object msg) throws IOException{
//将msg转换成Netty的ByteBuf对象
ByteBuf buf = (ByteBuf)msg;
//将缓冲区中的字节数组复制到新建的byte数组中,
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
//获取请求消息
String body = new String(req,"UTF-8");
System.out.println("The time server receive order:" + body);
//如果是"QUERY TIME ORDER"则创建应答消息
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(
System.currentTimeMillis()).toString() : "BAD ORDER";
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
//异步发送应答消息给客户端
ctx.write(resp);
} public void channelReadComplete(ChannelHandlerContext ctx) throws Exception{
ctx.flush();
} public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
ctx.close();
}
}

相比昨天原生的NIO服务端,代码量大大减少。

 Netty时间服务器客户端 TimeClient:

 package netty;

 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; public class TimeClient { public void connect(int port,String host) throws Exception{
//创建客户端处理I/O读写的NioEventLoopGroup Group线程组
EventLoopGroup group = new NioEventLoopGroup();
try{
//创建客户端辅助启动类Bootstrap
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>(){
//将ChannelHandler设置到ChannelPipleline中,用于处理网络I/O事件
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
//发起异步连接操作,然后调用同步方法等待连接成功。
ChannelFuture f = b.connect(host,port).sync(); //等待客户端链路关闭
f.channel().closeFuture().sync();
}finally{
//优雅退出,释放NIO线程组
group.shutdownGracefully();
}
} public static void main(String[] args) throws Exception{
int port = 8080;
if(args != null && args.length > 0){
try{
port = Integer.valueOf(args[0]);
}catch(NumberFormatException e){
//采用默认值
}
}
new TimeClient().connect(port, "127.0.0.1");
} }

 Netty时间服务器客户端 TimeClientHandler:

 package netty;

 import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import java.util.logging.Logger; public class TimeClientHandler extends ChannelHandlerAdapter{ private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName()); private final ByteBuf firstMessage; public TimeClientHandler(){
byte[] req = "QUERY TIME ORDER".getBytes();
firstMessage = Unpooled.buffer(req.length);
firstMessage.writeBytes(req);
} //当客户端与服务端TCP链路简历成功后,Netty的NIO线程会调用该方法,发送查询时间的指令给服务器
public void channelActive(ChannelHandlerContext ctx){
//将请求消息发送给服务端
ctx.writeAndFlush(firstMessage);
} //当服务器返回应答消息时,该方法被调用
public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req,"UTF-8");
System.out.println("Now is :" + body);
} public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){ //释放资源
logger.warning("Unexpected exception from downstream :" + cause.getMessage());
ctx.close();
}
}

运行结果:

Server:

Client:

 

Netty服务端与客户端(源码一)的更多相关文章

  1. netty服务端启动--ServerBootstrap源码解析

    netty服务端启动--ServerBootstrap源码解析 前面的第一篇文章中,我以spark中的netty客户端的创建为切入点,分析了netty的客户端引导类Bootstrap的参数设置以及启动 ...

  2. Netty服务端的启动源码分析

    ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...

  3. ZooKeeper单机服务端的启动源码阅读

    程序的入口QuorumPeerMain public static void main(String[] args) { // QuorumPeerMain main = new QuorumPeer ...

  4. netty(二) 创建一个netty服务端和客户端

    服务端 NettyServer package com.zw.netty.config; import com.zw.netty.channel.ServerInitializer;import io ...

  5. Netty之旅三:Netty服务端启动源码分析,一梭子带走!

    Netty服务端启动流程源码分析 前记 哈喽,自从上篇<Netty之旅二:口口相传的高性能Netty到底是什么?>后,迟迟两周才开启今天的Netty源码系列.源码分析的第一篇文章,下一篇我 ...

  6. Netty(6)源码-服务端与客户端创建

    原生的NIO类图使用有诸多不便,Netty向用户屏蔽了细节,在与用户交界处做了封装. 一.服务端创建时序图 步骤一:创建ServerBootstrap实例 ServerBootstrap是Netty服 ...

  7. 【转】TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端

    [转]TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端).UDP客户端 目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP ...

  8. 【Netty源码分析】Netty服务端bind端口过程

    这一篇博客我们介绍一下Netty服务端绑定端口的过程,我们通过跟踪代码一直到NIO原生绑定端口的操作. 绑定端口操作 ChannelFuture future = serverBootstrap.bi ...

  9. Netty 学习(一):服务端启动 & 客户端启动

    Netty 学习(一):服务端启动 & 客户端启动 作者: Grey 原文地址: 博客园:Netty 学习(一):服务端启动 & 客户端启动 CSDN:Netty 学习(一):服务端启 ...

随机推荐

  1. Exercise 24: More Practice

    puts "Let's practice everything." puts 'You\'d need to know \'bout escapes with \\ that do ...

  2. 一条SQL查询MYSQL最大内存用量

    // max_mem_usage

  3. 2016年5月面试题(Unity&iOS)

    NGUI的DrawCall drawcall定义:对底层图形程序(比如:OpenGL ES)接口的调用,以在屏幕上画出东西. List在内存中连续的空间保存 List是线性直接存储类型 mipMap是 ...

  4. 简单的射击游戏HTML+JS实现

    一直想自己写一个游戏玩,时间和精力都不太允许,最近几天刚好有空闲时间,就琢磨了这个小游戏. 刚开始想着计算图片重叠事件,然后让炮弹和飞机消失,傻乎乎写了一天,越整越乱.今天一大早晕过来了,改用数组以后 ...

  5. 怎么学JavaScript?

    作者:小不了链接:https://zhuanlan.zhihu.com/p/23265155来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 鉴于时不时,有同学私信问我( ...

  6. SQL Server 2008 R2 未能加载文件或程序集Microsoft.SqlServer.Sqm...

    错误提示:未能加载文件或程序集“Microsoft.SqlServer.Sqm, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8 ...

  7. 【jq】c#零基础学习之路(4)抽象类和密封

    一.抽象类 1.抽象类不能被实例化 2.抽象类方法必需要实现 3.如何类中函数为抽象函数,其类也需要定义成抽象类 4.关键字 abstract ,函数重写 override. 二.密封类 1.密封类不 ...

  8. assign、copy 、retain等关键字的含义

    assign: 简单赋值,不更改索引计数copy: 建立一个索引计数为1的对象,然后释放旧对象retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1Copy其实是建立了一个 ...

  9. CC2540中的电压检测

    /** * Battery level conversion from ADC to a percentage: * * The maximum ADC value for the battery v ...

  10. 在Visual Studio中设置多核并行编译

    VS是一款非常强大实用的IDE,是在Windows环境下学习编程的首选软件. 有些时候大一点的工程项目编译要耗时挺长时间,随便修改一下代码就可能要编译将近一分钟,甚至更多.即便在开启的增量编译的情况下 ...