Netty服务器
在学习了一段时间Netty后写的一个小demo
1.—添加Maven依赖
io.netty
netty-all
5.0.0.Alpha1
2.—服务端代码
/**
* @program: AGPS
* @description: Netty服务器
* @author: Mr.Liu
* @create: 2019-02-23 16:51
**/
public class NettyServer {
public void bind(int port) throws Exception{
//配置服务端的NIO线程组
// Boss线程:由这个线程池提供的线程是boss种类的,用于创建、连接、绑定socket, (有点像门卫)然后把这些socket传给worker线程池。
// 在服务器端每个监听的socket都有一个boss线程来处理。在客户端,只有一个boss线程来处理所有的socket。
EventLoopGroup bossGroup = new NioEventLoopGroup();
// Worker线程:Worker线程执行所有的异步I/O,即处理操作
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
// ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器,并且开始监听端口的socket请求
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)//将两个NIO线程组当作参数传入ServerBootstrap
.channel(NioServerSocketChannel.class)// 设置非阻塞,用它来建立新accept的连接,用于构造serversocketchannel的工厂类
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new ChildChannelHandler());// ChildChannelHandler 对出入的数据进行的业务操作,其继承ChannelInitializer
ChannelFuture f = b.bind(port).addListener(future -> { //添加监听
if(future.isSuccess()) {
System.out. println( new Date() + ": 端口["+ port + "]绑定成功!");
} else{
System.err. println( "端口["+ port + "]绑定失败!");
}})
.sync();//绑定并堵塞 等待服务器链路关闭后main才退出
//等待服务器监听端口关闭
f.channel().closeFuture().sync();
}finally {
//优雅的退出,释放线程池资源
bossGroup.shutdownGracefully();//使用shutdownGracefully()优雅的退出
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel arg0) throws Exception{
//arg0.pipeline().addLast("decoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
//arg0.pipeline().addLast("encoder", new LengthFieldPrepender(4, false));
//arg0.pipeline().addLast(new LineBasedFrameDecoder(1024)); //以\n 或者\r\n为截止的结束符 --回车换行符解决TCP半包/粘包问题
arg0.pipeline().addLast(new StringDecoder());
arg0.pipeline().addLast(new TimeServerHandler()); //自己的Handler
}
}
public static void main(String[] args) throws Exception{
int port = 8090; // 先设置一个默认的,要是有其他的端口传进来就是执行下面的代码
if(args != null & args.length>0){
try {
port = Integer.valueOf(args[0]);
}catch (NumberFormatException e){
}
}
System.out.println("服务器启动成功:开始监听端口:"+port);
new TimeServer().bind(port);
}
}
3.—Handler类
/**
* @program: Agps
* @description:
* @author: Mr.Liu
* @create: 2019-02-23 17:01
**/
public class TimeServerHandler extends ChannelHandlerAdapter {
private final static Logger logger = LoggerFactory.getLogger(TimeServerHandler.class);
//$数据#
private int counter;
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{ //msg是服务端接收到客户端的消息
String body = (String) msg;
String currentTime;
//获取客户端ip
InetSocketAddress ipSocket = (InetSocketAddress)ctx.channel().remoteAddress();
String clientIp = ipSocket.getAddress().getHostAddress();
System.out.println("The time server receive order :"+body+"; the counter is :"+ ++counter);
//数据效验
currentTime = DataFormatCheck(body);
//System.getProperty("line.separator") 这也是换行符,功能和"\n"是一致的,但是此种写法屏蔽了 Windows和Linux的区别 ,更保险一些
currentTime = "<<>>"+currentTime + System.getProperty("line.separator");
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.writeAndFlush(resp);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause){
ctx.close();
}
/**
* 数据效验
* @param body
* @return
*/
public String DataFormatCheck(String body){
String currentTime = "";
int lenth = body.length();
if("QUERY TIME ORDER".equalsIgnoreCase(body)){
currentTime = new java.util.Date(
System.currentTimeMillis()).toString();
}else if(!"".equalsIgnoreCase(body)){ 简单的判断协议
if(body.substring(0, 1).equals("$")){
if(body.substring(lenth-1,lenth).equals("#")){
String news = body.substring(1,lenth-1);
try{
currentTime = TimeServerHandler.insertMysql(news);
}catch (Exception e){
currentTime = Const.ERROR;
}
}else {
currentTime = Const.BAD_FORMAT;
}
}else {
currentTime = Const.BAD_FORMAT;
}
}else {
currentTime = Const.BAD_ORDER;
}
return currentTime;
}
/**
* 数据存入数据库
* @param msg
* @return
*/
public static String insertMysql(String msg){
String str;
// 传递sql语句
Statement stt;
Connection conn = null;
String sql = "insert into env_data(datas) values ('"+msg+"')"; //env_data()一定要连起来不然会有错误
try {
conn = mysqlimages.getConn(); //一个连接数据库的方法,这就不贴了,很简单的
//获取Statement对象
stt = conn.createStatement();
//执行sql语句
stt.executeUpdate(sql);
logger.info(">>>插入数据库成功");
str = Const.SECCESS;
} catch (Exception e) {
logger.error("<<