├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
├── org.eclipse.jdt.core.prefs
├── org.eclipse.m2e.core.prefs
└── org.maven.ide.eclipse.prefs
├── README.md
├── conf
├── spring-client.xml
└── spring-server.xml
├── pom.xml
└── src
└── main
└── java
└── com
└── netboy
└── netty
├── client
├── ClientThread.java
├── NettyClient.java
└── RunClient.java
├── handler
├── ClientHandler.java
└── ServerHandler.java
└── server
├── NettyServer.java
└── RunServer.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ChatNetty
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.maven.ide.eclipse.maven2Builder
15 |
16 |
17 |
18 |
19 | org.eclipse.m2e.core.maven2Builder
20 |
21 |
22 |
23 |
24 |
25 | org.eclipse.m2e.core.maven2Nature
26 | org.eclipse.jdt.core.javanature
27 | org.maven.ide.eclipse.maven2Nature
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/main/java=UTF-8
3 | encoding/=UTF-8
4 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | #Sat Mar 23 19:24:27 CST 2013
2 | eclipse.preferences.version=1
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
4 | org.eclipse.jdt.core.compiler.compliance=1.5
5 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
6 | org.eclipse.jdt.core.compiler.source=1.5
7 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/.settings/org.maven.ide.eclipse.prefs:
--------------------------------------------------------------------------------
1 | #Sat Mar 23 19:24:27 CST 2013
2 | activeProfiles=
3 | eclipse.preferences.version=1
4 | fullBuildGoals=process-test-resources
5 | resolveWorkspaceProjects=true
6 | resourceFilterGoals=process-resources resources\:testResources
7 | skipCompilerPlugin=true
8 | version=1
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ChatNetty
2 | =========
3 |
4 | 用Netty实现NIO Socket聊天系统示例
5 |
--------------------------------------------------------------------------------
/conf/spring-client.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/conf/spring-server.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.netboy.netty
6 | ChatNetty
7 | 0.0.1-SNAPSHOT
8 | jar
9 |
10 | ChatNetty
11 | http://maven.apache.org
12 |
13 |
14 | UTF-8
15 |
16 |
17 |
18 |
19 | junit
20 | junit
21 | 3.8.1
22 | test
23 |
24 |
25 |
26 |
27 | io.netty
28 | netty
29 | 3.5.6.Final
30 |
31 |
32 |
33 |
34 | org.springframework
35 | spring
36 | 2.5.6
37 |
38 |
39 |
40 | log4j
41 | log4j
42 | 1.2.16
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/client/ClientThread.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.client;
2 |
3 | import java.util.Scanner;
4 |
5 | import org.jboss.netty.channel.Channel;
6 |
7 | public class ClientThread extends Thread {
8 | private NettyClient nettyClient;
9 |
10 | private Scanner scanner = new Scanner(System.in);
11 |
12 | public void init() {
13 | nettyClient.init();
14 | nettyClient.start();
15 | }
16 |
17 | public void run() {
18 | while(true) {
19 |
20 | Channel channel = nettyClient.getChannelFuture().getChannel();
21 | System.out.println("发送消息(Enter发送):");
22 | Object msg = scanner.next();
23 | if(msg.toString().equals("quit")) {
24 | System.out.println("wait, you will quit..");
25 | nettyClient.stop();
26 |
27 | }
28 | channel.write(msg);
29 | }
30 | }
31 |
32 | public void setNettyClient(NettyClient nettyClient) {
33 | this.nettyClient = nettyClient;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/client/NettyClient.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.client;
2 |
3 | import java.net.InetSocketAddress;
4 | import java.util.concurrent.Executors;
5 |
6 | import org.jboss.netty.bootstrap.ClientBootstrap;
7 | import org.jboss.netty.channel.ChannelFuture;
8 | import org.jboss.netty.channel.ChannelPipeline;
9 | import org.jboss.netty.channel.ChannelPipelineFactory;
10 | import org.jboss.netty.channel.Channels;
11 | import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
12 | import org.jboss.netty.handler.codec.string.StringDecoder;
13 | import org.jboss.netty.handler.codec.string.StringEncoder;
14 |
15 | import com.netboy.netty.handler.ClientHandler;
16 |
17 | public class NettyClient {
18 | private int port=9090;
19 | private String host="127.0.0.1";
20 | private ClientBootstrap bootstrap;
21 | private ClientHandler handler;
22 | private ChannelFuture channelFuture;
23 | /**
24 | * 初始化客户端
25 | */
26 | public void init() {
27 | bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(
28 | Executors.newCachedThreadPool(),
29 | Executors.newCachedThreadPool()));
30 |
31 | bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
32 | public ChannelPipeline getPipeline() throws Exception {
33 | ChannelPipeline channelPipeline = Channels.pipeline();
34 | channelPipeline.addLast("encode", new StringEncoder());
35 | channelPipeline.addLast("decode", new StringDecoder());
36 | channelPipeline.addLast("handler", handler);
37 | return channelPipeline;
38 | }
39 | });
40 | bootstrap.setOption("tcpNoDelay", true);
41 | bootstrap.setOption("keepAlive", true);
42 | bootstrap.setOption("reuseAddress", true);
43 | }
44 |
45 | public void start() {
46 | channelFuture = bootstrap.connect(new InetSocketAddress(host,port));
47 | System.out.println("连接远程服务器"+host+":"+port+"端口成功,你现在可以开始发消息了。");
48 | }
49 |
50 |
51 | public void stop() {
52 | channelFuture.awaitUninterruptibly();
53 | if (!channelFuture.isSuccess()) {
54 | channelFuture.getCause().printStackTrace();
55 | }
56 | //等待或者监听数据全部完成
57 | channelFuture.getChannel().getCloseFuture().awaitUninterruptibly();
58 | //释放连接资源
59 | bootstrap.releaseExternalResources();
60 |
61 | }
62 |
63 |
64 | public void setBootstrap(ClientBootstrap bootstrap) {
65 | this.bootstrap = bootstrap;
66 | }
67 |
68 | public void setHandler(ClientHandler handler) {
69 | this.handler = handler;
70 | }
71 |
72 |
73 | public void setPort(int port) {
74 | this.port = port;
75 | }
76 |
77 | public ClientHandler getHandler() {
78 | return handler;
79 | }
80 |
81 | public void setHost(String host) {
82 | this.host = host;
83 | }
84 |
85 | public ChannelFuture getChannelFuture() {
86 | return channelFuture;
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/client/RunClient.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.client;
2 |
3 | import org.springframework.context.ApplicationContext;
4 | import org.springframework.context.support.FileSystemXmlApplicationContext;
5 |
6 | /**
7 | * TODO
8 | * Administrator 2013-3-23下午07:50:49
9 | */
10 | public class RunClient {
11 |
12 | public static void main(String[] args) {
13 |
14 | String contextFile = "./conf/spring-client.xml";
15 |
16 | ApplicationContext context = null;
17 | try {
18 | context = new FileSystemXmlApplicationContext(contextFile);
19 | } catch (Exception e) {
20 | System.out.println("RunMain has some exception");
21 | e.printStackTrace();
22 | }
23 | ClientThread client = (ClientThread)context.getBean("clientThread");
24 |
25 | client.init();
26 | client.start();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/handler/ClientHandler.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.handler;
2 |
3 | import java.util.Date;
4 |
5 | import org.jboss.netty.channel.ChannelHandlerContext;
6 | import org.jboss.netty.channel.MessageEvent;
7 | import org.jboss.netty.channel.SimpleChannelHandler;
8 |
9 | public class ClientHandler extends SimpleChannelHandler {
10 |
11 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
12 | throws Exception {
13 | String content = (String) e.getMessage();
14 | System.out.println(""+ new Date().toString() + "\n" + content);
15 | }
16 | }
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/handler/ServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.handler;
2 |
3 | import java.util.Date;
4 |
5 | import org.jboss.netty.channel.Channel;
6 | import org.jboss.netty.channel.ChannelHandlerContext;
7 | import org.jboss.netty.channel.ChannelStateEvent;
8 | import org.jboss.netty.channel.ExceptionEvent;
9 | import org.jboss.netty.channel.MessageEvent;
10 | import org.jboss.netty.channel.SimpleChannelHandler;
11 | import org.jboss.netty.channel.group.ChannelGroup;
12 | import org.jboss.netty.channel.group.DefaultChannelGroup;
13 |
14 | /**
15 | * 服务器端handler
16 | * TODO
17 | * Administrator 2013-3-24下午03:34:28
18 | */
19 | public class ServerHandler extends SimpleChannelHandler {
20 | private static ChannelGroup channelGroup;
21 |
22 | /**
23 | * 构造函数,在spring加载的时候初始化一次。
24 | */
25 | public ServerHandler() {
26 | super();
27 | /*获得客户端在服务器端注册的所有信息,用于向所有客户端分发消息*/
28 | channelGroup = new DefaultChannelGroup("client-channel-group");
29 | }
30 | /**
31 | * 用于扑捉客户端退出的消息。
32 | * 并将其从服务器端的注册表中删掉。
33 | */
34 | @Override
35 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
36 | throws Exception {
37 | e.getCause().printStackTrace();
38 | Channel channel = e.getChannel();
39 | channel.close();
40 | if (channelGroup.contains(channel)) {
41 | System.out.println("一个客户端退出:"+channel.getId());
42 | channelGroup.remove(channel);
43 | }
44 | }
45 | /**
46 | * 关键方法
47 | * 用于接收从客户端发来的消息,进行相应的逻辑处理
48 | * 这里,我们将任何一个客户端发来的消息,都将其转发给所有的客户端。
49 | */
50 | @Override
51 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
52 | throws Exception {
53 | String content = (String) e.getMessage();
54 | System.out.println("服务器收到" + e.getChannel().getId()
55 | + " 的消息 时间:" + new Date().toString() + " 消息内容:\n"+ content);
56 | content=e.getChannel().getId()+":"+content;
57 | if (content.equalsIgnoreCase("quit")) {
58 | e.getChannel().close();
59 | channelGroup.remove(e.getChannel());
60 | return;
61 | } else {
62 | System.out.println("开始转发到其他客户端!:size="+channelGroup.size());
63 | for(Channel ch:channelGroup){
64 | System.out.println("开始转发到其他客户端!:"+ch.getId());
65 | ch.write(content);
66 | }
67 | }
68 | }
69 | /**
70 | * 对新连接的用户进行注册
71 | */
72 | @Override
73 | public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)throws Exception {
74 | System.out.println("新的客户端连入:"+e.getChannel().getId());
75 | channelGroup.add(e.getChannel());
76 | }
77 |
78 | public ChannelGroup getChannelGroup() {
79 | return channelGroup;
80 | }
81 | }
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/server/NettyServer.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.server;
2 |
3 | import java.net.InetSocketAddress;
4 | import java.util.concurrent.Executors;
5 |
6 | import org.jboss.netty.bootstrap.ServerBootstrap;
7 | import org.jboss.netty.channel.ChannelPipeline;
8 | import org.jboss.netty.channel.ChannelPipelineFactory;
9 | import org.jboss.netty.channel.Channels;
10 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
11 | import org.jboss.netty.handler.codec.string.StringDecoder;
12 | import org.jboss.netty.handler.codec.string.StringEncoder;
13 |
14 | import com.netboy.netty.handler.ServerHandler;
15 |
16 | public class NettyServer {
17 | private int port = 9090;
18 | private ServerBootstrap bootstrap;
19 | private ServerHandler handler;
20 | /**
21 | * 初始化服务器端
22 | */
23 | public void init() {
24 | bootstrap = new ServerBootstrap(
25 | new NioServerSocketChannelFactory(
26 | Executors.newCachedThreadPool(), //boss 监听请求,并分派给slave进行处理
27 | Executors.newCachedThreadPool()//slave 处理请求,将其丢到线程池中处理
28 | )
29 | );
30 |
31 | bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
32 | public ChannelPipeline getPipeline() throws Exception {
33 | ChannelPipeline pipeline = Channels.pipeline();
34 | /*典型的过滤式处理*/
35 | pipeline.addLast("encode", new StringEncoder());
36 | pipeline.addLast("decode", new StringDecoder());
37 | /*添加自定义的handler,对请求进行处理*/
38 | pipeline.addLast("handler", handler);
39 | return pipeline;
40 | }
41 | });
42 |
43 | /*使用tcp长连接*/
44 | bootstrap.setOption("child.tcpNoDelay", true);
45 | bootstrap.setOption("child.keepAlive", true);
46 | bootstrap.setOption("reuseAddress", true);
47 | }
48 | /**
49 | * 绑定端口,启动netty服务
50 | */
51 | public void start() {
52 | bootstrap.bind(new InetSocketAddress(port));
53 | System.out.println("服务器启动,端口:" + port);
54 | }
55 | /**
56 | * 关闭netty,释放资源。
57 | */
58 | public void stop() {
59 | bootstrap.releaseExternalResources();
60 | }
61 |
62 | public void setPort(int port) {
63 | this.port = port;
64 | }
65 |
66 | public void setHandler(ServerHandler handler) {
67 | this.handler = handler;
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/src/main/java/com/netboy/netty/server/RunServer.java:
--------------------------------------------------------------------------------
1 | package com.netboy.netty.server;
2 |
3 | import org.springframework.context.ApplicationContext;
4 | import org.springframework.context.support.FileSystemXmlApplicationContext;
5 |
6 | /**
7 | * TODO
8 | * Administrator 2013-3-23下午07:49:32
9 | */
10 | public class RunServer {
11 |
12 | public static void main(String[] args) {
13 |
14 | String contextFile = "./conf/spring-server.xml";
15 |
16 | ApplicationContext context = null;
17 | try {
18 | context = new FileSystemXmlApplicationContext(contextFile);
19 | } catch (Exception e) {
20 | System.out.println("RunServer has some exception");
21 | e.printStackTrace();
22 | }
23 | final NettyServer server =(NettyServer)context.getBean("nettyServer");
24 |
25 | Runtime.getRuntime().addShutdownHook(new Thread() {
26 |
27 | @Override
28 | public void run() {
29 | try {
30 | server.stop();
31 | } catch (Exception e) {
32 | System.out.println("run main stop error!");
33 | }
34 | }
35 |
36 | } );
37 | server.init();
38 | server.start();
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------