工作中用到netty,先用起来,再深入研究,这里我们一起谈谈聊天群发的小实例。
实例是maven工程,用到spring。我们先从建立服务器端开始,然后再介绍client端。
工程目录:
************************************************************************************
************************************************************************************
io.netty
netty
3.5.6.Final
org.springframework
spring
2.5.6
netty框架采用handler代理过滤链的方式,留有messageReceived()方法接口,用于处理事件的业务逻辑,和jetty中的handler和filter一样。
先实现一个服务器端handler,命名为ServerHandler
package com.netboy.netty.handler;
import java.util.Date;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
/**
* 服务器端handler
* TODO
* Administrator 2013-3-24下午03:34:28
*/
public class ServerHandler extends SimpleChannelHandler {
private static ChannelGroup channelGroup;
/**
* 构造函数,在spring加载的时候初始化一次。
*/
public ServerHandler() {
super();
/*获得客户端在服务器端注册的所有信息,用于向所有客户端分发消息*/
channelGroup = new DefaultChannelGroup("client-channel-group");
}
/**
* 用于扑捉客户端退出的消息。
* 并将其从服务器端的注册表中删掉。
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
Channel channel = e.getChannel();
channel.close();
if (channelGroup.contains(channel)) {
System.out.println("一个客户端退出:"+channel.getId());
channelGroup.remove(channel);
}
}
/**
* 关键方法
* 用于接收从客户端发来的消息,进行相应的逻辑处理
* 这里,我们将任何一个客户端发来的消息,都将其转发给所有的客户端。
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
String content = (String) e.getMessage();
System.out.println("服务器收到" + e.getChannel().getId()
+ " 的消息 时间:" + new Date().toString() + " 消息内容:\n"+ content);
content=e.getChannel().getId()+":"+content;
if (content.equalsIgnoreCase("quit")) {
e.getChannel().close();
channelGroup.remove(e.getChannel());
return;
} else {
System.out.println("开始转发到其他客户端!:size="+channelGroup.size());
for(Channel ch:channelGroup){
System.out.println("开始转发到其他客户端!:"+ch.getId());
ch.write(content);
}
}
}
/**
* 对新连接的用户进行注册
*/
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)throws Exception {
System.out.println("新的客户端连入:"+e.getChannel().getId());
channelGroup.add(e.getChannel());
}
public ChannelGroup getChannelGroup() {
return channelGroup;
}
}
package com.netboy.netty.server;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import com.netboy.netty.handler.ServerHandler;
public class NettyServer {
private int port = 8080;
private ServerBootstrap bootstrap;
private ServerHandler handler;
/**
* 初始化服务器端
*/
public void init() {
bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), //boss 监听请求,并分派给slave进行处理
Executors.newCachedThreadPool()//slave 处理请求,将其丢到线程池中处理
)
);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
/*典型的过滤式处理*/
pipeline.addLast("encode", new StringEncoder());
pipeline.addLast("decode", new StringDecoder());
/*添加自定义的handler,对请求进行处理*/
pipeline.addLast("handler", handler);
return pipeline;
}
});
/*使用tcp长连接*/
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.setOption("reuseAddress", true);
}
/**
* 绑定端口,启动netty服务
*/
public void start() {
bootstrap.bind(new InetSocketAddress(port));
System.out.println("服务器启动,端口:" + port);
}
/**
* 关闭netty,释放资源。
*/
public void stop() {
bootstrap.releaseExternalResources();
}
public void setPort(int port) {
this.port = port;
}
public void setHandler(ServerHandler handler) {
this.handler = handler;
}
}
package com.netboy.netty.server;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* TODO
* Administrator 2013-3-23下午07:49:32
*/
public class RunServer {
public static void main(String[] args) {
String contextFile = "./conf/spring-server.xml";
ApplicationContext context = null;
try {
context = new FileSystemXmlApplicationContext(contextFile);
} catch (Exception e) {
System.out.println("RunServer has some exception");
e.printStackTrace();
}
final NettyServer server =(NettyServer)context.getBean("nettyServer");
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
server.stop();
} catch (Exception e) {
System.out.println("run main stop error!");
}
}
} );
server.init();
server.start();
}
}
先定义handler,接收到服务器的消息,然后打印出来。
package com.netboy.netty.handler;
import java.util.Date;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
public class ClientHandler extends SimpleChannelHandler {
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
String content = (String) e.getMessage();
System.out.println(""+ new Date().toString() + "\n" + content);
}
}
客户端NettyClient
package com.netboy.netty.client;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
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;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import com.netboy.netty.handler.ClientHandler;
public class NettyClient {
private int port=8080;
private String host="127.0.0.1";
private ClientBootstrap bootstrap;
private ClientHandler handler;
private ChannelFuture channelFuture;
/**
* 初始化客户端
*/
public void init() {
bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline channelPipeline = Channels.pipeline();
channelPipeline.addLast("encode", new StringEncoder());
channelPipeline.addLast("decode", new StringDecoder());
channelPipeline.addLast("handler", handler);
return channelPipeline;
}
});
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
bootstrap.setOption("reuseAddress", true);
}
public void start() {
channelFuture = bootstrap.connect(new InetSocketAddress(host,port));
System.out.println("连接远程服务器"+host+":"+port+"端口成功,你现在可以开始发消息了。");
}
public void stop() {
channelFuture.awaitUninterruptibly();
if (!channelFuture.isSuccess()) {
channelFuture.getCause().printStackTrace();
}
//等待或者监听数据全部完成
channelFuture.getChannel().getCloseFuture().awaitUninterruptibly();
//释放连接资源
bootstrap.releaseExternalResources();
}
public void setBootstrap(ClientBootstrap bootstrap) {
this.bootstrap = bootstrap;
}
public void setHandler(ClientHandler handler) {
this.handler = handler;
}
public void setPort(int port) {
this.port = port;
}
public ClientHandler getHandler() {
return handler;
}
public void setHost(String host) {
this.host = host;
}
public ChannelFuture getChannelFuture() {
return channelFuture;
}
}
客户端线程:
package com.netboy.netty.client;
import java.util.Scanner;
import org.jboss.netty.channel.Channel;
public class ClientThread extends Thread {
private NettyClient nettyClient;
private Scanner scanner = new Scanner(System.in);
public void init() {
nettyClient.init();
nettyClient.start();
}
public void run() {
while(true) {
Channel channel = nettyClient.getChannelFuture().getChannel();
System.out.println("发送消息(Enter发送):");
Object msg = scanner.next();
if(msg.toString().equals("quit")) {
System.out.println("wait, you will quit..");
nettyClient.stop();
}
channel.write(msg);
}
}
public void setNettyClient(NettyClient nettyClient) {
this.nettyClient = nettyClient;
}
}
客户端的配置文件spring-client.xml
客户端启动程序:
package com.netboy.netty.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* TODO
* Administrator 2013-3-23下午07:50:49
*/
public class RunClient {
public static void main(String[] args) {
String contextFile = "./conf/spring-client.xml";
ApplicationContext context = null;
try {
context = new FileSystemXmlApplicationContext(contextFile);
} catch (Exception e) {
System.out.println("RunMain has some exception");
e.printStackTrace();
}
ClientThread client = (ClientThread)context.getBean("clientThread");
client.init();
client.start();
}
}
server:
client:
********************************************************************************************************************************
源码在这里下载:点击打开链接
**********************************************************************************************************
注意: 以上至可以传输字符流,如果你想传输对象,那么在bootstrap.setPipelineFactory()的时候需要将
channelPipeline.addLast("encode", new StringEncoder());
channelPipeline.addLast("decode", new StringDecoder());
换为:
channelPipeline.addLast("encode", new ObjectDecoder(
ClassResolvers.cacheDisabled(
this.getClass().getClassLoader()
)
)
);
channelPipeline.addLast("decode", new ObjectDecoder(
ClassResolvers.cacheDisabled(
this.getClass().getClassLoader()
)
)
);