netty简单实例

3 篇文章 0 订阅
2 篇文章 0 订阅

一: 简介   

  工作中用到netty,先用起来,再深入研究,这里我们一起谈谈聊天群发的小实例。

实例是maven工程,用到spring。我们先从建立服务器端开始,然后再介绍client端。

工程目录:

************************************************************************************

************************************************************************************

二:依赖

    <!-- netty -->
	<dependency>  
	    <groupId>io.netty</groupId>  
	    <artifactId>netty</artifactId>  
	    <version>3.5.6.Final</version>  
	</dependency>
	
	<!-- spring -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring</artifactId>
		<version>2.5.6</version>
	</dependency>

三:server端

    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;
	}

}

spring配置文件spring-server.xml:

	<bean id="nettyServer" class="com.netboy.netty.server.NettyServer" >
	    <property name="port" value="8080" />
	    <property name="handler" ref="serverHandler" />
	</bean>

    <bean id="serverHandler" class="com.netboy.netty.handler.ServerHandler" />

server启动程序:

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


    <bean id="clientThread" class="com.netboy.netty.client.ClientThread" >
	    <property name="nettyClient" ref="nettyClient" />
	</bean>
    
	<bean id="nettyClient" class="com.netboy.netty.client.NettyClient" >
	    <property name="host" value="127.0.0.1" />
	    <property name="port" value="8080" />
	    <property name="handler" ref="clientHandler" />
	</bean>
	    
    <bean id="clientHandler" class="com.netboy.netty.handler.ClientHandler" />
    

客户端启动程序:

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:

********************************************************************************************************************************


 like it ,enjoy it,insert it.

源码在这里下载:点击打开链接

**********************************************************************************************************


注意: 以上至可以传输字符流,如果你想传输对象,那么在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()
                       		 )
                       )
					);






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值