问题所在:

        目前为止,互联网上的通讯都是通过已有的应用程序或者软件库来实现。例如,我们最常使用的就是利用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();
	}

}