├── .gitignore ├── pom.xml ├── spring-with-netty.iml └── src ├── main ├── java │ └── com │ │ └── moo │ │ └── springnetty │ │ ├── cfg │ │ └── SpringConfig.java │ │ ├── handlers │ │ ├── KameProtocolInitalizer.java │ │ ├── KameServerHandler.java │ │ ├── ServerHandler.java │ │ ├── StringProtocolInitalizer.java │ │ └── codec │ │ │ └── kame │ │ │ ├── EncodeType.java │ │ │ ├── KameRequest.java │ │ │ ├── KameRequestDecoder.java │ │ │ ├── KameRequestEncoder.java │ │ │ ├── KameResponse.java │ │ │ ├── KameResponseDecoder.java │ │ │ └── KameResponseEncoder.java │ │ ├── models │ │ └── User.java │ │ └── server │ │ ├── Main.java │ │ └── TCPServer.java └── resources │ ├── Log4j.properties │ └── netty-server.properties └── test ├── java └── com │ └── moo │ └── springnetty │ ├── server │ └── TCPServerTest.java │ └── test │ ├── TestClient.java │ ├── TestClientHandler.java │ └── TestClientInitializer.java └── resources ├── Log4j.properties └── netty-server.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | Debug.log 4 | Server.log 5 | Server.log.* -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | spring-with-netty 6 | spring-with-netty 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | spring-with-netty 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 4.0.2.RELEASE 16 | 4.0.17.Final 17 | 1.6.1 18 | 4.3.1.Final 19 | 20 | 21 | 22 | 23 | junit 24 | junit 25 | 4.8.1 26 | 27 | 28 | org.springframework 29 | spring-test 30 | ${org.springframework.version} 31 | 32 | 33 | 34 | org.springframework 35 | spring-context 36 | ${org.springframework.version} 37 | 38 | 39 | commons-logging 40 | commons-logging 41 | 42 | 43 | 44 | 45 | 46 | mysql 47 | mysql-connector-java 48 | 5.1.22 49 | 50 | 51 | org.hibernate 52 | hibernate-core 53 | ${org.hibernate.version} 54 | 55 | 56 | org.slf4j 57 | slf4j-api 58 | ${org.slf4j.version} 59 | 60 | 61 | org.slf4j 62 | slf4j-log4j12 63 | ${org.slf4j.version} 64 | 65 | 66 | org.slf4j 67 | jcl-over-slf4j 68 | ${org.slf4j.version} 69 | 70 | 71 | io.netty 72 | netty-all 73 | ${netty.version} 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /spring-with-netty.iml: -------------------------------------------------------------------------------- 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 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/cfg/SpringConfig.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.cfg; 2 | 3 | import com.moo.springnetty.handlers.KameProtocolInitalizer; 4 | import com.moo.springnetty.handlers.StringProtocolInitalizer; 5 | import com.moo.springnetty.handlers.codec.kame.KameRequestDecoder; 6 | import com.moo.springnetty.handlers.codec.kame.KameRequestEncoder; 7 | import com.moo.springnetty.handlers.codec.kame.KameResponseDecoder; 8 | import com.moo.springnetty.handlers.codec.kame.KameResponseEncoder; 9 | import io.netty.bootstrap.ServerBootstrap; 10 | import io.netty.channel.ChannelOption; 11 | import io.netty.channel.nio.NioEventLoopGroup; 12 | import io.netty.channel.socket.nio.NioServerSocketChannel; 13 | import io.netty.handler.codec.string.StringDecoder; 14 | import io.netty.handler.codec.string.StringEncoder; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.beans.factory.annotation.Value; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.ComponentScan; 20 | import org.springframework.context.annotation.Configuration; 21 | import org.springframework.context.annotation.PropertySource; 22 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 23 | 24 | import java.net.InetSocketAddress; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import java.util.Set; 28 | 29 | @Configuration 30 | @ComponentScan("com.moo") 31 | @PropertySource("classpath:netty-server.properties") 32 | public class SpringConfig { 33 | 34 | @Value("${boss.thread.count}") 35 | private int bossCount; 36 | @Value("${worker.thread.count}") 37 | private int workerCount; 38 | @Value("${tcp.port}") 39 | private int tcpPort; 40 | @Value("${so.keepalive}") 41 | private boolean keepAlive; 42 | @Value("${so.backlog}") 43 | private int backlog; 44 | @Value("${log4j.configuration}") 45 | private String log4jConfiguration; 46 | @Autowired 47 | @Qualifier("springProtocolInitializer") 48 | private StringProtocolInitalizer protocolInitalizer; 49 | @Autowired 50 | @Qualifier("KameProtocolInitalizer") 51 | private KameProtocolInitalizer kameProtocolInitalizer; 52 | 53 | /** 54 | * Necessary to make the Value annotations work. 55 | * 56 | * @return 57 | */ 58 | @Bean 59 | public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { 60 | return new PropertySourcesPlaceholderConfigurer(); 61 | } 62 | 63 | @SuppressWarnings("unchecked") 64 | @Bean(name = "serverBootstrap") 65 | public ServerBootstrap bootstrap() { 66 | 67 | ServerBootstrap b = new ServerBootstrap(); 68 | b.group(bossGroup(), workerGroup()) 69 | .channel(NioServerSocketChannel.class) 70 | .childHandler(kameProtocolInitalizer); 71 | Map, Object> tcpChannelOptions = tcpChannelOptions(); 72 | Set> keySet = tcpChannelOptions.keySet(); 73 | for (@SuppressWarnings("rawtypes") 74 | ChannelOption option : keySet) { 75 | b.option(option, tcpChannelOptions.get(option)); 76 | } 77 | return b; 78 | } 79 | 80 | @Bean(name = "bossGroup", destroyMethod = "shutdownGracefully") 81 | public NioEventLoopGroup bossGroup() { 82 | return new NioEventLoopGroup(bossCount); 83 | } 84 | 85 | @Bean(name = "workerGroup", destroyMethod = "shutdownGracefully") 86 | public NioEventLoopGroup workerGroup() { 87 | return new NioEventLoopGroup(workerCount); 88 | } 89 | 90 | @Bean(name = "tcpSocketAddress") 91 | public InetSocketAddress tcpPort() { 92 | return new InetSocketAddress(tcpPort); 93 | } 94 | 95 | @Bean(name = "tcpChannelOptions") 96 | public Map, Object> tcpChannelOptions() { 97 | Map, Object> options = new HashMap, Object>(); 98 | options.put(ChannelOption.SO_KEEPALIVE, keepAlive); 99 | options.put(ChannelOption.SO_BACKLOG, backlog); 100 | return options; 101 | } 102 | 103 | @Bean(name = "stringEncoder") 104 | public StringEncoder stringEncoder() { 105 | return new StringEncoder(); 106 | } 107 | 108 | @Bean(name = "stringDecoder") 109 | public StringDecoder stringDecoder() { 110 | return new StringDecoder(); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/KameProtocolInitalizer.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers; 2 | 3 | import com.moo.springnetty.handlers.codec.kame.KameRequestDecoder; 4 | import com.moo.springnetty.handlers.codec.kame.KameResponseEncoder; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.channel.socket.SocketChannel; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | @Qualifier("KameProtocolInitalizer") 14 | public class KameProtocolInitalizer extends ChannelInitializer { 15 | 16 | @Autowired 17 | private KameServerHandler serverHandler; 18 | 19 | @Override 20 | protected void initChannel(SocketChannel ch) throws Exception { 21 | ChannelPipeline pipeline = ch.pipeline(); 22 | pipeline.addLast(new KameRequestDecoder(), serverHandler, new KameResponseEncoder()); 23 | 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/KameServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers; 2 | 3 | import com.moo.springnetty.handlers.codec.kame.KameRequest; 4 | import com.moo.springnetty.handlers.codec.kame.KameResponse; 5 | import io.netty.channel.*; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | 8 | import io.netty.util.ReferenceCountUtil; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | @Qualifier("serverHandler") 14 | @Sharable 15 | public class KameServerHandler extends ChannelInboundHandlerAdapter { 16 | 17 | @Override 18 | public void channelRead(ChannelHandlerContext ctx, Object msg) 19 | throws Exception { 20 | try { 21 | KameRequest request = (KameRequest) msg; 22 | System.out.println("Server Reciver Data: "+ msg); 23 | KameResponse response = buildResponse(); 24 | 25 | // 返回处理结果response 26 | ChannelFuture f = ctx.channel().writeAndFlush(response); 27 | f.addListener(ChannelFutureListener.CLOSE); 28 | } finally { 29 | ReferenceCountUtil.release(msg); 30 | } 31 | } 32 | 33 | @Override 34 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 35 | cause.printStackTrace(); 36 | ctx.close(); 37 | } 38 | 39 | public KameResponse buildResponse(){ 40 | KameResponse response = new KameResponse(); 41 | response.setEncode(new Byte("0")); 42 | response.setEncrypt(new Byte("1")); 43 | response.setExtend1(new Byte("1")); 44 | response.setExtend2(new Byte("2")); 45 | response.setResult(4); 46 | response.setSessionId(9); 47 | return response; 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/ServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.SimpleChannelInboundHandler; 5 | import io.netty.channel.ChannelHandler.Sharable; 6 | 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @Qualifier("serverHandler") 12 | @Sharable 13 | public class ServerHandler extends SimpleChannelInboundHandler { 14 | 15 | @Override 16 | public void channelRead0(ChannelHandlerContext ctx, String msg) 17 | throws Exception { 18 | System.out.print(msg); 19 | ctx.channel().writeAndFlush(msg); 20 | } 21 | 22 | @Override 23 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 24 | System.out.println("Channel is active\n"); 25 | super.channelActive(ctx); 26 | } 27 | 28 | @Override 29 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 30 | System.out.println("\nChannel is disconnected"); 31 | super.channelInactive(ctx); 32 | } 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/StringProtocolInitalizer.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.string.StringDecoder; 7 | import io.netty.handler.codec.string.StringEncoder; 8 | 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.beans.factory.annotation.Qualifier; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | @Qualifier("springProtocolInitializer") 15 | public class StringProtocolInitalizer extends ChannelInitializer { 16 | 17 | @Autowired 18 | StringDecoder stringDecoder; 19 | 20 | @Autowired 21 | StringEncoder stringEncoder; 22 | 23 | @Autowired 24 | ServerHandler serverHandler; 25 | 26 | @Override 27 | protected void initChannel(SocketChannel ch) throws Exception { 28 | ChannelPipeline pipeline = ch.pipeline(); 29 | pipeline.addLast("decoder", stringDecoder); 30 | pipeline.addLast("handler", serverHandler); 31 | pipeline.addLast("encoder", stringEncoder); 32 | } 33 | 34 | public StringDecoder getStringDecoder() { 35 | return stringDecoder; 36 | } 37 | 38 | public void setStringDecoder(StringDecoder stringDecoder) { 39 | this.stringDecoder = stringDecoder; 40 | } 41 | 42 | public StringEncoder getStringEncoder() { 43 | return stringEncoder; 44 | } 45 | 46 | public void setStringEncoder(StringEncoder stringEncoder) { 47 | this.stringEncoder = stringEncoder; 48 | } 49 | 50 | public ServerHandler getServerHandler() { 51 | return serverHandler; 52 | } 53 | 54 | public void setServerHandler(ServerHandler serverHandler) { 55 | this.serverHandler = serverHandler; 56 | } 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/EncodeType.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: zheng 6 | * Date: 14-3-4 7 | * Time: 下午9:22 8 | * 0:UTF-8,1:GBK,2:GB2312,3:ISO8859-1 9 | */ 10 | public class EncodeType { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/KameRequest.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | /** 4 | * Kame Request Data 5 | * 协议内容 6 | * 编码方式(1byte) 7 | * 加密(1byte) 8 | * 扩展1(1byte) 9 | * 扩展2(1byte) 10 | * 会话ID(4byte) 11 | * 命令(4byte) 12 | * 包长(4byte) 13 | */ 14 | public class KameRequest { 15 | 16 | private byte encode; //编码格式 17 | private byte encrypt; //加密类型 18 | private byte extend1; 19 | private byte extend2; 20 | private int sessionId; 21 | private int command; 22 | 23 | 24 | // 默认构造函数 25 | public KameRequest() { 26 | } 27 | 28 | public KameRequest(int command, int sessionId, byte extend1, byte extend2, byte encrypt, byte encode) { 29 | 30 | this.command = command; 31 | this.sessionId = sessionId; 32 | this.extend2 = extend2; 33 | this.extend1 = extend1; 34 | this.encrypt = encrypt; 35 | this.encode = encode; 36 | } 37 | 38 | public byte getEncode() { 39 | return encode; 40 | } 41 | 42 | public void setEncode(byte encode) { 43 | this.encode = encode; 44 | } 45 | 46 | public byte getEncrypt() { 47 | return encrypt; 48 | } 49 | 50 | public void setEncrypt(byte encrypt) { 51 | this.encrypt = encrypt; 52 | } 53 | 54 | public byte getExtend1() { 55 | return extend1; 56 | } 57 | 58 | public void setExtend1(byte extend1) { 59 | this.extend1 = extend1; 60 | } 61 | 62 | public byte getExtend2() { 63 | return extend2; 64 | } 65 | 66 | public void setExtend2(byte extend2) { 67 | this.extend2 = extend2; 68 | } 69 | 70 | public int getSessionId() { 71 | return sessionId; 72 | } 73 | 74 | public void setSessionId(int sessionId) { 75 | this.sessionId = sessionId; 76 | } 77 | 78 | public int getCommand() { 79 | return command; 80 | } 81 | 82 | public void setCommand(int command) { 83 | this.command = command; 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return "XLRequest [encode=" + encode + ", encrypt=" + encrypt + ", extend1=" + extend1 + ", extend2=" + extend2 89 | + ", sessionid=" + sessionId + ", command=" + command + "]"; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/KameRequestDecoder.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandler; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.ByteToMessageDecoder; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * . 12 | * User: zheng 13 | * Date: 14-3-5 14 | * Time: 上午10:03 15 | * 自定义协议解码器 16 | */ 17 | public class KameRequestDecoder extends ByteToMessageDecoder { 18 | 19 | @Override 20 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 21 | 22 | if (in.readableBytes() < 12) { 23 | return; 24 | } 25 | 26 | byte encode = in.readByte(); 27 | byte encrypt = in.readByte(); 28 | byte extend1 = in.readByte(); 29 | byte extend2 = in.readByte(); 30 | int sessionId = in.readInt(); 31 | int command = in.readInt(); 32 | 33 | // TODO 根据length大小获取数据包大小 34 | KameRequest request = new KameRequest(); 35 | request.setEncode(encode); 36 | request.setEncrypt(encrypt); 37 | request.setExtend1(extend1); 38 | request.setExtend2(extend2); 39 | request.setSessionId(sessionId); 40 | request.setCommand(command); 41 | 42 | System.out.println("KameRequestDecoder:" + request); 43 | 44 | out.add(request); 45 | } 46 | 47 | @Override 48 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 49 | //super.exceptionCaught(ctx, cause); 50 | ctx.close(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/KameRequestEncoder.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandler; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToByteEncoder; 7 | 8 | /** 9 | * User: zheng 10 | * Date: 14-3-5 11 | * Time: 上午10:04 12 | * KameRequest 编码器 13 | */ 14 | @ChannelHandler.Sharable 15 | public class KameRequestEncoder extends MessageToByteEncoder { 16 | 17 | @Override 18 | protected void encode(ChannelHandlerContext ctx, KameRequest msg, ByteBuf out) throws Exception { 19 | out.writeByte(msg.getEncode()); 20 | out.writeByte(msg.getEncrypt()); 21 | out.writeByte(msg.getExtend1()); 22 | out.writeByte(msg.getExtend2()); 23 | out.writeInt(msg.getSessionId()); 24 | out.writeInt(msg.getCommand()); 25 | System.out.println("KameRequestEncoder:" +msg); 26 | } 27 | 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/KameResponse.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: zheng 6 | * Date: 14-3-4 7 | * Time: 下午9:15 8 | */ 9 | public class KameResponse { 10 | 11 | private byte encode;// 数据编码格式。 12 | private byte encrypt;// 加密类型。0表示不加密 13 | private byte extend1;// 用于扩展协议 14 | private byte extend2;// 用于扩展协议 15 | private int sessionId;// 会话ID 16 | private int result;// 结果码 17 | 18 | public byte getEncode() { 19 | return encode; 20 | } 21 | 22 | public void setEncode(byte encode) { 23 | this.encode = encode; 24 | } 25 | 26 | public byte getEncrypt() { 27 | return encrypt; 28 | } 29 | 30 | public void setEncrypt(byte encrypt) { 31 | this.encrypt = encrypt; 32 | } 33 | 34 | public byte getExtend1() { 35 | return extend1; 36 | } 37 | 38 | public void setExtend1(byte extend1) { 39 | this.extend1 = extend1; 40 | } 41 | 42 | public byte getExtend2() { 43 | return extend2; 44 | } 45 | 46 | public void setExtend2(byte extend2) { 47 | this.extend2 = extend2; 48 | } 49 | 50 | public int getSessionId() { 51 | return sessionId; 52 | } 53 | 54 | public void setSessionId(int sessionId) { 55 | this.sessionId = sessionId; 56 | } 57 | 58 | public int getResult() { 59 | return result; 60 | } 61 | 62 | public void setResult(int result) { 63 | this.result = result; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "XLResponse [encode=" + encode + ", encrypt=" + encrypt + ", extend1=" + extend1 + ", extend2=" + extend2 69 | + ", sessionid=" + sessionId + ", result=" + result + "]"; 70 | } 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/KameResponseDecoder.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.ByteToMessageDecoder; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: zheng 12 | * Date: 14-3-4 13 | * Time: 下午9:16 14 | * 自定义协议解码器 15 | */ 16 | public class KameResponseDecoder extends ByteToMessageDecoder{ 17 | 18 | @Override 19 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 20 | System.out.println("KameResponseDecoder:" +in.readableBytes()); 21 | 22 | if(in.readableBytes()<12) { 23 | return; 24 | } 25 | byte encode = in.readByte(); 26 | byte encrypt = in.readByte(); 27 | byte extend1 = in.readByte(); 28 | byte extend2 = in.readByte(); 29 | int sessionId = in.readInt(); 30 | int result = in.readInt(); 31 | 32 | KameResponse response = new KameResponse(); 33 | response.setEncode(encode); 34 | response.setEncrypt(encrypt); 35 | response.setExtend1(extend1); 36 | response.setExtend2(extend2); 37 | response.setSessionId(sessionId); 38 | response.setResult(result); 39 | 40 | System.out.println("KameResponseDecoder:" + response); 41 | 42 | out.add(response); 43 | 44 | } 45 | 46 | @Override 47 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 48 | super.exceptionCaught(ctx, cause); //To change body of overridden methods use File | Settings | File Templates. 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/handlers/codec/kame/KameResponseEncoder.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.handlers.codec.kame; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelOutboundHandlerAdapter; 6 | import io.netty.channel.ChannelPromise; 7 | import io.netty.handler.codec.MessageToByteEncoder; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: zheng 12 | * Date: 14-3-4 13 | * Time: 下午9:16 14 | * 自定义协议编码器 15 | */ 16 | public class KameResponseEncoder extends MessageToByteEncoder { 17 | 18 | @Override 19 | protected void encode(ChannelHandlerContext ctx, KameResponse msg, ByteBuf out) throws Exception { 20 | System.out.println("KameResponseEncoder: " + msg); 21 | out.writeByte(msg.getEncode()); 22 | out.writeByte(msg.getEncrypt()); 23 | out.writeByte(msg.getExtend1()); 24 | out.writeByte(msg.getExtend2()); 25 | out.writeInt(msg.getSessionId()); 26 | out.writeInt(msg.getResult()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/models/User.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.models; 2 | 3 | public class User { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/server/Main.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.server; 2 | 3 | import com.moo.springnetty.cfg.SpringConfig; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 7 | import org.springframework.context.support.AbstractApplicationContext; 8 | 9 | public class Main { 10 | private static final Logger LOG = LoggerFactory.getLogger(Main.class); 11 | 12 | public static void main(String[] args) { 13 | LOG.debug("Starting application context"); 14 | @SuppressWarnings("resource") 15 | AbstractApplicationContext ctx = new AnnotationConfigApplicationContext( 16 | SpringConfig.class); 17 | ctx.registerShutdownHook(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/moo/springnetty/server/TCPServer.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.server; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.Channel; 5 | 6 | import java.net.InetSocketAddress; 7 | 8 | import javax.annotation.PostConstruct; 9 | import javax.annotation.PreDestroy; 10 | 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.beans.factory.annotation.Qualifier; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Component 16 | public class TCPServer { 17 | 18 | @Autowired 19 | @Qualifier("serverBootstrap") 20 | private ServerBootstrap b; 21 | 22 | @Autowired 23 | @Qualifier("tcpSocketAddress") 24 | private InetSocketAddress tcpPort; 25 | 26 | private Channel serverChannel; 27 | 28 | @PostConstruct 29 | public void start() throws Exception { 30 | System.out.println("Starting server at " + tcpPort); 31 | serverChannel = b.bind(tcpPort).sync().channel().closeFuture().sync() 32 | .channel(); 33 | } 34 | 35 | @PreDestroy 36 | public void stop() { 37 | serverChannel.close(); 38 | } 39 | 40 | public ServerBootstrap getB() { 41 | return b; 42 | } 43 | 44 | public void setB(ServerBootstrap b) { 45 | this.b = b; 46 | } 47 | 48 | public InetSocketAddress getTcpPort() { 49 | return tcpPort; 50 | } 51 | 52 | public void setTcpPort(InetSocketAddress tcpPort) { 53 | this.tcpPort = tcpPort; 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/main/resources/Log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to A1. 2 | 3 | log4j.rootLogger=DEBUG, toLogFile 4 | 5 | # Create appender 'toFile' to send log to 'Server.log' file 6 | log4j.appender.toLogFile=org.apache.log4j.RollingFileAppender 7 | log4j.appender.toLogFile.File=Server.log 8 | log4j.appender.toLogFile.MaxFileSize=1MB 9 | log4j.appender.toLogFile.MaxBackupIndex=1 10 | log4j.appender.toLogFile.layout=org.apache.log4j.PatternLayout 11 | log4j.appender.toLogFile.layout.ConversionPattern= %d [%F:%L][%p]:%m%n -------------------------------------------------------------------------------- /src/main/resources/netty-server.properties: -------------------------------------------------------------------------------- 1 | tcp.port=8090 2 | boss.thread.count=2 3 | worker.thread.count=2 4 | so.keepalive=true 5 | so.backlog=100 6 | log4j.configuration=SpringNettyLog4j.properties -------------------------------------------------------------------------------- /src/test/java/com/moo/springnetty/server/TCPServerTest.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.server; 2 | 3 | import com.moo.springnetty.cfg.SpringConfig; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.junit.Ignore; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; 9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 | 11 | @Ignore 12 | @RunWith(SpringJUnit4ClassRunner.class) 13 | @ContextConfiguration(classes = SpringConfig.class) 14 | public class TCPServerTest extends AbstractJUnit4SpringContextTests { 15 | 16 | @Test 17 | public void testStop() throws Exception { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/moo/springnetty/test/TestClient.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.test; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioSocketChannel; 9 | 10 | /** 11 | * Simplistic telnet client. 12 | */ 13 | public class TestClient { 14 | 15 | private final String host; 16 | private final int port; 17 | 18 | public TestClient(String host, int port) { 19 | this.host = host; 20 | this.port = port; 21 | } 22 | 23 | public static void main(String[] args) throws Exception { 24 | // Print usage if no argument is specified. 25 | if (args.length != 2) { 26 | System.err.println( 27 | "Usage: " + TestClient.class.getSimpleName() + 28 | " "); 29 | return; 30 | } 31 | 32 | // Parse options. 33 | String host = args[0]; 34 | int port = Integer.parseInt(args[1]); 35 | 36 | new TestClient(host, port).run(); 37 | } 38 | 39 | public void run() throws Exception { 40 | EventLoopGroup group = new NioEventLoopGroup(); 41 | try { 42 | Bootstrap b = new Bootstrap(); 43 | b.group(group) 44 | .channel(NioSocketChannel.class) 45 | .option(ChannelOption.TCP_NODELAY, true) 46 | //.option(ChannelOption.SO_KEEPALIVE, true) 47 | .handler(new TestClientInitializer()); 48 | 49 | // Start the client. 50 | ChannelFuture f = b.connect(host, port).sync(); 51 | 52 | // Wait until the connection is closed. 53 | f.channel().closeFuture().sync(); 54 | 55 | } finally { 56 | group.shutdownGracefully(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/moo/springnetty/test/TestClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.test; 2 | 3 | 4 | import com.moo.springnetty.handlers.codec.kame.KameRequest; 5 | import io.netty.channel.*; 6 | import io.netty.channel.ChannelHandler.Sharable; 7 | import io.netty.util.ReferenceCountUtil; 8 | 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | 12 | /** 13 | * Handles a client-side channel. 14 | */ 15 | @Sharable 16 | public class TestClientHandler extends ChannelInboundHandlerAdapter { 17 | 18 | private static final Logger logger = Logger.getLogger(TestClientHandler.class.getName()); 19 | 20 | @Override 21 | public void handlerAdded(ChannelHandlerContext ctx) { 22 | System.out.println("handlerAdded"); 23 | } 24 | 25 | @Override 26 | public void handlerRemoved(ChannelHandlerContext ctx) { 27 | System.out.println("handlerRemoved"); 28 | } 29 | 30 | @Override 31 | public void channelActive(ChannelHandlerContext ctx) { 32 | System.out.println("channelActive"); 33 | ctx.writeAndFlush(buildRequest()); 34 | } 35 | 36 | @Override 37 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 38 | try{ 39 | System.out.println("channelRead"); 40 | ctx.close(); 41 | }finally { 42 | ReferenceCountUtil.release(msg); 43 | } 44 | 45 | 46 | } 47 | 48 | @Override 49 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 50 | ctx.flush(); 51 | } 52 | 53 | @Override 54 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 55 | // Close the connection when an exception is raised. 56 | logger.log(Level.WARNING, "Unexpected exception from downstream.", cause); 57 | ctx.close(); 58 | } 59 | 60 | public KameRequest buildRequest(){ 61 | 62 | int command = 1; 63 | int sessionId = 2; 64 | byte extend1 = 3; 65 | byte extend2 = new Byte("1"); 66 | byte encrypt = new Byte("0"); 67 | byte encode = new Byte("1"); 68 | return new KameRequest(command, sessionId, extend1, extend2, encrypt, encode); 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/moo/springnetty/test/TestClientInitializer.java: -------------------------------------------------------------------------------- 1 | package com.moo.springnetty.test; 2 | 3 | import com.moo.springnetty.handlers.codec.kame.KameRequestEncoder; 4 | import com.moo.springnetty.handlers.codec.kame.KameResponseDecoder; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelPipeline; 8 | import io.netty.channel.socket.SocketChannel; 9 | 10 | /** 11 | * Creates a newly configured {@link io.netty.channel.ChannelPipeline} for a new channel. 12 | */ 13 | 14 | /** 15 | * Creates a newly configured {@link ChannelPipeline} for a new channel. 16 | */ 17 | public class TestClientInitializer extends ChannelInitializer { 18 | private static final KameResponseDecoder DECODER = new KameResponseDecoder(); 19 | private static final KameRequestEncoder ENCODER = new KameRequestEncoder(); 20 | private static final TestClientHandler CLIENTHANDLER = new TestClientHandler(); 21 | 22 | @Override 23 | public void initChannel(SocketChannel ch) throws Exception { 24 | ChannelPipeline pipeline = ch.pipeline(); 25 | 26 | // Add the text line codec combination first, 27 | pipeline.addLast("decoder", DECODER); 28 | pipeline.addLast("encoder", ENCODER); 29 | 30 | // and then business logic. 31 | pipeline.addLast("handler", CLIENTHANDLER); 32 | } 33 | 34 | @Override 35 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 36 | super.exceptionCaught(ctx, cause); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/resources/Log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to A1. 2 | 3 | log4j.rootLogger=DEBUG, toLogFile 4 | 5 | # Create appender 'toFile' to send log to 'Server.log' file 6 | log4j.appender.toLogFile=org.apache.log4j.RollingFileAppender 7 | log4j.appender.toLogFile.File=Debug.log 8 | log4j.appender.toLogFile.MaxFileSize=1MB 9 | log4j.appender.toLogFile.MaxBackupIndex=1 10 | log4j.appender.toLogFile.layout=org.apache.log4j.PatternLayout 11 | log4j.appender.toLogFile.layout.ConversionPattern= %d [%F:%L][%p]:%m%n -------------------------------------------------------------------------------- /src/test/resources/netty-server.properties: -------------------------------------------------------------------------------- 1 | tcp.port=8090 2 | boss.thread.count=2 3 | worker.thread.count=2 4 | so.keepalive=true 5 | so.backlog=100 6 | log4j.configuration=SpringNettyLog4j.properties --------------------------------------------------------------------------------