Netty和Mina。
一、优势对比:
Netty是由JBoss提供的Java开发框架,适用于开发高性能、高稳定性的客户端或者服务端网络通信框架,相较于Mina有如下几点优点:
1.支持多种编解码方式,当然也包括当前游戏开发中应用最广的Google提供的Protobuff编解码方式;
2.典型的NIO架构,非阻塞异步的事件驱动网络通信架构,吞吐量高,支持高并发;
二、资源下载:
1.netty-all-4.0.42.Final.jar:在Netty官网下载所需版本的jar包,不同版本的Netty差别比较大,我选择的是Netty4.0.42 Final版本。
2.Log4j或common-logging:这是用于打印和记录日志的工具,非常实用,详细的使用方法可以参考:最详细的Log4j使用教程,当然也有更简单方便的一个工具:common-logging,使用方法参考: Apache Commons-logging使用实例。
三、Socket服务:
1.新建一个Java工程,步骤:File —— new —— Java Project;
2.资源导入:
解压上面下载的Netty4.0.42压缩包,将netty-4.0.42.Final\jar\all-in-one目录下面的netty-all-4.0.42.Final.jar和commons-logging-1.2中的commons-logging-1.2.jar一起复制到eclipse中项目的libs目录下面(没有这个目录的话可以自行创建,专门用于存放外部导入的jar包):
3.编写服务代码:
在src目录下新建一个包名com.tw.login.server,然后新建两个类:
LoginSocketServer.java:用于管理Socket,例如:ip端口绑定,Socket启动和关闭等;
SocketServerHandler.java:Socket数据接收监听和解析中心;
LoginSocketServer中启动一个ServerBootsrap服务器,并且绑定线程组,并且绑定用来监听数据的继承自SocketChannel的自定义监听器类,在其中设置网络传输数据的编码方式,然后设置连接到此服务器需要访问的IP和端口。
关于传输数据协议,这里我们先以最简单的字符串为例,以("\n")为结尾分割的解码器作为数据协议格式,关于更复杂的数据类型协议在后面的篇章会继续设计。
LoginSocketServer源码:
public class LoginSocketServer {
private static final Log logger = LogFactory.getLog(LoginSocketServer.class);
private static final String IP = "127.0.0.1";
private static final int PORT = 8088;
//分配用于处理业务的线程组数量
protected static final int BisGroupSize = Runtime.getRuntime().availableProcessors()*2;
//每个线程组中线程的数量
protected static final int worGroupSize = 4;
private static final EventLoopGroup bossGroup = new NioEventLoopGroup(BisGroupSize);
private static final EventLoopGroup workerGroup = new NioEventLoopGroup(worGroupSize);
protected static void run() throws Exception {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 以("\n")为结尾分割的 解码器
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new SocketServerHandler());
}
});
bootstrap.bind(IP,PORT).sync();
logger.info("Socket服务器已启动完成");
}
protected static void shutdown() {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
public static void main(String[] args) throws Exception {
logger.info("开始启动Socket服务器...");
run();
}
}
SocketServerHandler源码:
public class SocketServerHandler extends SimpleChannelInboundHandler<String> {
private static final Log logger = LogFactory.getLog(LoginSocketServer.class);
@Override
public void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception {
// TODO Auto-generated method stub
logger.info("数据内容:data="+data);
}
}
为了测试还需要创建一个客户端,其实创建过程很简单:直接复制一份上述服务器的代码,稍作修改即可:
LoginSocketClient源码:
private static final Log logger = LogFactory.getLog(LoginSocketClient.class);
private static final String IP = "127.0.0.1";
private static final int PORT = 8088;
private static final EventLoopGroup group = new NioEventLoopGroup();
@SuppressWarnings("rawtypes")
protected static void run() throws Exception {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = ch.pipeline();
/*
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*
* 解码和编码 我将会在下一张为大家详细的讲解。再次暂时不做详细的描述
*
* */
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new SocketClientHandler());
}
});
// 连接服务端
Channel ch = bootstrap.connect(IP,PORT).sync().channel();
ch.writeAndFlush("客户端数据"+"\r\n");
logger.info("向Socket服务器发送数据:"+"客户端数据"+"\r\n");
}
public static void main(String[] args){
logger.info("开始连接Socket服务器...");
try {
run();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
}
SocketClientHandler源码:
public class SocketClientHandler extends SimpleChannelInboundHandler<String> {
private static final Log logger = LogFactory.getLog(LoginSocketClient.class);
@Override
public void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception {
// TODO Auto-generated method stub
String data = msg.toString();
logger.info("数据内容:data="+data);
}
@Override
protected void channelRead0(ChannelHandlerContext arg0, String data) throws Exception {
// TODO Auto-generated method stub
logger.info("数据内容:data="+data);
}
}
4.运行:
运行方式其实很简单,直接在Eclipse中选中项目根目录,右键——Run As——Java Application,注意要先运行服务端工程再运行客户端工程,运行结果如下:
服务端Console结果:
客户端Console结果: