问题所在:
目前为止,互联网上的通讯都是通过已有的应用程序或者软件库来实现。例如,我们最常使用的就是利用HTTP协议客户端(浏览器或者其他)从远程服务器上获取信息或者远程web服务。但是,大多数协议都不是为了我们的应用而特殊定制的,就像我们几乎不会用HTTP协议来传输大规模文件,发送电子邮件或是进行实时通讯。我们需要的是一个高度优化的应用框架可以支持我们特殊的需求。例如,你可以搭建一个HTTP server却能同时支持优化的AJAX聊天应用,大型文件传输等等。你甚至可以设计自己的一个网络通讯协议。
解决方法:
Netty是一个高效的、异步的、基于事件驱动的网络通讯框架。使用Netty 可以快速开发出可维护的,高性能、高扩展能力的协议服务及其客户端应用。也就是说,Netty 是一个基于NIO的客户,服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
“快速”和“简单”并不意味着会让你的最终应用产生维护性或性能上的问题。Netty是一个吸收了多种协议的实现经验,这些协议包括FTP,SMPT,HTTP,各种二进制,文本协议,并经过相当精心设计的项目,最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。
一些用户可能找到了某些同样声称具有这些特性的编程框架,因此你们可能想问Netty又有什么不一样的地方。这个问题的答案是Netty项目的设计哲学。从创立之初,无论是在API还是在其实现上Netty都致力于为你提供最为舒适的使用体验。虽然这并不是显而易见的,但你终将会认识到这种设计哲学将令你在阅读本指南和使用Netty时变得更加得轻松和容易。
客户端与服务器的搭建: 在详细介绍使用之前,先给出客户端与服务器端基本框架的搭建,以下程序以一个简单的时间服务为例,即客户端向服务器发送连接请求,服务器在收到请求之后就向客户端发送当前系统时间。在后续博客中会介绍每个环节的具体注意事项。废话就不多说了,先上代码:
服务器端:
TimeServer.java:
package org.jboss.netty.example.time;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
public class TimeServer {
//DefaultChannelGroup requires the name of the group as a constructor parameter. The group
//name is solely used to distinguish one group from others.
static final ChannelGroup allChannels = new DefaultChannelGroup("time-server" );
public static void main(String[] args) throws Exception{
ChannelFactory factory = new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap bootstrap = new ServerBootstrap(factory);
bootstrap.setPipelineFactory(new ChannelPipelineFactory(){
public ChannelPipeline getPipeline(){
return Channels.pipeline(
new TimeServerHandler(),
new TimeEncoder()
);
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
Channel channel = bootstrap.bind(new InetSocketAddress(8080));
allChannels.add(channel);
waitForShutdownCommand();
ChannelGroupFuture future = allChannels.close();
future.awaitUninterruptibly();
factory.releaseExternalResources();
}
}
TimeServerHandler.java:
package org.jboss.netty.example.time;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
public class TimeServerHandler extends SimpleChannelHandler{
// @Override
// public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e){
// Channel ch = e.getChannel();
// ChannelBuffer time = ChannelBuffers.buffer(4);
// time.writeInt((int)(System.currentTimeMillis()/1000));
//
// ChannelFuture f = ch.write(time);
// f.addListener(new ChannelFutureListener(){
// public void operationComplete(ChannelFuture future){
// Channel ch = future.getChannel();
// ch.close();
// }
// });
// }
private static String a = "Hello";
public TimeServerHandler(){
System.out.println("Hello world " + a);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
UnixTime time = new UnixTime(System.currentTimeMillis() / 1000);
ChannelFuture f = e.getChannel().write(time);
f.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
TimeServer.allChannels.add(e.getChannel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e){
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
TimeEncoder.java:
package org.jboss.netty.example.time;
import static org.jboss.netty.buffer.ChannelBuffers.buffer;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
public class TimeEncoder extends SimpleChannelHandler{
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) {
UnixTime time = (UnixTime) e.getMessage();
ChannelBuffer buf = buffer(4);
buf.writeInt((int)time.getValue());
Channels.write(ctx, e.getFuture(), buf);
}
}
客户端代码:
TimeClient.java:
package org.jboss.netty.example.time;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
public class TimeClient {
public static void main(String[] args) throws Exception{
String host = args[0];
int port = Integer.parseInt(args[1]);
// String host = "localhost";
// int port = 8080;
ChannelFactory factory = new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ClientBootstrap bootstrap = new ClientBootstrap(factory);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline(){
return Channels.pipeline(
new TimeDecoder(),
new TimeClientHandler());
}
});
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host,port));
future.awaitUninterruptibly();
if (!future.isSuccess()) {
future.getCause().printStackTrace();
}
future.getChannel().getCloseFuture().awaitUninterruptibly();
factory.releaseExternalResources();
}
}
TimeDecoder.java:
package org.jboss.netty.example.time;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
public class TimeDecoder extends FrameDecoder{
// @Override
// protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer){
// if(buffer.readableBytes() < 4){
// return null;
// }
// return buffer.readBytes(4);
// }
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer){
if(buffer.readableBytes() < 4){
return null;
}
return new UnixTime(buffer.readInt());
}
}
TimeClientHandler.java:
package org.jboss.netty.example.time;
import java.util.Date;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
public class TimeClientHandler extends SimpleChannelHandler{
// @Override
// public void messageReceived(ChannelHandlerContext ctx, MessageEvent e){
// ChannelBuffer buf = (ChannelBuffer)e.getMessage();
// long currentTimeMillis = buf.readInt() * 1000L;
// System.out.println(new Date(currentTimeMillis));
// e.getChannel().close();
// }
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e){
UnixTime m = (UnixTime)e.getMessage();
System.out.println(m);
e.getChannel().close();
}
}
共用数据结构:
UnixTime.java:
package org.jboss.netty.example.time;
import java.util.Date;
public class UnixTime {
private final long value;
public UnixTime(long value){
this.value = value;
}
public long getValue(){
return value;
}
@Override
public String toString(){
return new Date(value * 1000L).toString();
}
}