完全是零基础开始的后端开发,所以该系列笔记(或者称为教程也可以)是完全假设没有任何预备知识的,但是前提是:1. Java基础;2. maven配置知识(gradle);
环境配置
本项目基于maven进行学习启动,我们需要用到的工具和依赖:
- maven安装与配置(详见上一篇)
- eclipe(IDE for Java web)
- JDK 1.7 +
- netty latest (netty download)
- telnet(linux/unix 默认可用,windows需配置)
配置十分容易,就是将相关的jar包放置到lib目录中,同时在pom.xml中添加依赖即可。
在pom.xml中添加依赖如下:
io.netty
netty-all
5.0.0.Alpha2
项目说明
此次教程旨在通过netty搭建一个本地服务器,采用telnet连接的方式实现向服务端口发送消息,验证客户端和服务器的连通性,在服务端实时打印出客户端发送过来的信息,借此对netty的用法有一个初步的理解。
同时为了进阶netty,还有以下资料参考:
先扔几个参考学习的网页:
netty 官方API: netty link
netty 中文指南:netty 中文
netty 优秀博客netty 入门
关于NIO基础的知识:
https://my.oschina.net/andylucc/blog/614295
http://www.cnblogs.com/dolphin0520/p/3919162.html
http://blog.csdn.net/wuxianglong/article/details/6604817
服务代码构建
服务器的作用是根据客户端发送过来的信息进行解析后执行对应的事务处理策略,姑且我们将这个称之为“事务策略”。
不同的事务策略是为了满足客户端各种请求(比如:数据库增删改查、文件上传下载等),而通过定义协议,通过特定的请求字段及参数等,发送到服务器进行特定事务的执行并返回结果。
在本例中,则通过一个简单的事务策略实例-打印客户端消息。
创建打印客户端消息的事务策略
package geekfish.NettyServer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
public class SimpleServerTestHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// get the msg from clients
try {
ByteBuf in = (ByteBuf) msg;
System.out.println("the msg from client is :" + in.toString(CharsetUtil.UTF_8) + " from "
+ ctx.hashCode());
} finally {
// 我们必须要对引用计数的对象进行手动释放
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
super.channelReadComplete(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(ctx, cause);
}
}
如我们之前所说,当客户端发送特定的请求时,我们在服务器端接收到客户端报文后并进行解析从而确定选用哪一个事务策略,在此我们引入两个问题:
- 如何制定请求类型和在服务器端解析
在此,我们简化了流程,只要客户端与服务器建立了连接,那么服务器就会将客户端发送过来的信息无脑地打印出来,并不涉及多业务类型的解析;
- 谁来负责接收并处理客户端信息
因此,仅有一个事务策略肯定是不够的,需要有主体去执行才行,可以想象这个主体主要负责的工作:接收所有客户端信息、解析客户端信息、执行客户端请求的任务、返回执行结果给客户端。所以我们还有一个主体需要实现,这里我们姑且将之称为“服务端主体”。
创建服务端主体
package geekfish.NettyServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
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 MainServer {
private int port;
public MainServer(int port) {
this.port = port;
};
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
System.out.println("ready to run port =>" + port);
try {
// 我们需要创建一个ServerBootstrap启动NIO服务
ServerBootstrap bootstrap = new ServerBootstrap();
// 我们需要设置boss Group 和 worker Group
bootstrap = bootstrap.group(bossGroup, workerGroup);
// 设置channel
bootstrap = bootstrap.channel(NioServerSocketChannel.class);
// 我们通过增加pipeline的方式给channel增加事务处理监听
bootstrap = bootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 我们可以在此处增加各种事务处理的监听,比如xxxServerHandler
ch.pipeline().addLast(new SimpleServerTestHandler());
// you can still add other handlers here
}
});
// 我们可以在此处进行参数的设置
bootstrap = bootstrap.option(ChannelOption.SO_BACKLOG, 128);
// 设置子连接的参数
bootstrap = bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口并启动接收客户端信息
ChannelFuture channelFuture = bootstrap.bind(port).sync();
// 一直等待循环接收信息直到socket关闭
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
//the main entrance to run this instance
public final static void main(String[] args) throws Exception{
int port = 8080;
if(args.length > 0)
port = Integer.parseInt(args[0]);
new MainServer(port).run();
System.out.println("the server start listening...");
}
}
代码验证测试
到此为止,简化版的项目已经基本完成,现在既可以通过talnet连接进行简单会话测试。
- 启动运行 服务端主体;
run MainServer,当然此处你可以带上端口号参数,如果懒到啥都不干,那就是8080了,记住这个端口号,因为客户端需要与这个端口创建连接;
- 在terminal中创建连接;
telnet localhost 8080
当然这里的8080需要替换为你当前启动的端口号,当连接创建之后,则可以在terminal会话窗口内向服务端发送消息啦,此时打开eclipse的控制台就可以看到每一条从客户端发过来的信息。
如果使用的是windows,注意windows下默认未开启telnet连接,所以直接在cmd中输入以上指令可能无法生效,需要去到'控制面板>>程序>>打开或者关闭windows功能'里面勾选上'telnet客户端'。
写在最后的话
楼主是计算机视觉这块的,所以比较偏重于应用层,对于前后端的开发基本算是零起步,但是通过接触前后端开发,最深的感触就是各种各样并且飞速迭代的框架,开发者入门时可能会遇到各种选择问题,但是从楼主的经历来看,框架是封装好的工具,切不可不求甚解地强记框架API,而是要大致理解后端框架共性的业务流程及模块划分。
由于走过弯路,没有使用任何框架上来就是继承Servlet去实现,虽然最后堆了一坨代码(包括自定义json封装解析类、数据库操作类、文件操作类等),所以即使实现了简单的后台功能,但是对于高并发、数据库高性能开发等均一无所知。然而与此同时,因为DIY过一个这样简易的结构,麻雀虽小,但是也具备基本必要模块,所以在此基础上,再去看各大流行框架,就会禁不住思考各个模块对应的实现细节,这一点对上手新框架以及对现有框架的理解都是挺有帮助的。
最后,希望这个教程能够坚持更新和创造一定的价值。