spring+netty服务器搭建

游戏一般是长连接,自定义协议,不用http协议,BIO,NIO,AIO这些我就不说了,自己查资料

我现在用spring+netty搭起简单的游戏服

思路:1自定义协议和协议包;2spring+netty整合;3半包粘包处理,心跳机制等;4请求分发(目前自己搞的都是单例模式)

下个是测试用的,结构如下




首先自定义包头

Header.java

package com.test.netty.message;  
  
  
/** 
 * Header.java 
 * 自定义协议包头 
 * @author janehuang 
 * @version 1.0 
 */  
public class Header {  
    private byte tag;  
  /*  编码*/  
    private byte encode;  
    /*加密*/  
    private byte encrypt;  
    /*其他字段*/  
    private byte extend1;  
    /*其他2*/  
    private byte extend2;  
    /*会话id*/  
    private String sessionid;  
    /*包的长度*/  
    private int length = 1024;  
    /*命令*/  
    private int cammand;  
  
    public Header() {  
  
    }  
  
    public Header(String sessionid) {  
        this.encode = 0;  
        this.encrypt = 0;  
        this.sessionid = sessionid;  
    }  
  
    public Header(byte tag, byte encode, byte encrypt, byte extend1, byte extend2, String sessionid, int length, int cammand) {  
        this.tag = tag;  
        this.encode = encode;  
        this.encrypt = encrypt;  
        this.extend1 = extend1;  
        this.extend2 = extend2;  
        this.sessionid = sessionid;  
        this.length = length;  
        this.cammand = cammand;  
    }  
  
    @Override  
    public String toString() {  
        return "header [tag=" + tag + "encode=" + encode + ",encrypt=" + encrypt + ",extend1=" + extend1 + ",extend2=" + extend2 + ",sessionid=" + sessionid + ",length=" + length + ",cammand="  
                + cammand + "]";  
    }  
  
    public byte getTag() {  
        return tag;  
    }  
  
    public void setTag(byte tag) {  
        this.tag = tag;  
    }  
  
    public byte getEncode() {  
        return encode;  
    }  
  
    public void setEncode(byte encode) {  
        this.encode = encode;  
    }  
  
    public byte getEncrypt() {  
        return encrypt;  
    }  
  
    public void setEncrypt(byte encrypt) {  
        this.encrypt = encrypt;  
    }  
  
    public byte getExtend1() {  
        return extend1;  
    }  
  
    public void setExtend1(byte extend1) {  
        this.extend1 = extend1;  
    }  
  
    public byte getExtend2() {  
        return extend2;  
    }  
  
    public void setExtend2(byte extend2) {  
        this.extend2 = extend2;  
    }  
  
    public String getSessionid() {  
        return sessionid;  
    }  
  
    public void setSessionid(String sessionid) {  
        this.sessionid = sessionid;  
    }  
  
    public int getLength() {  
        return length;  
    }  
  
    public void setLength(int length) {  
        this.length = length;  
    }  
  
    public int getCammand() {  
        return cammand;  
    }  
  
    public void setCammand(int cammand) {  
        this.cammand = cammand;  
    }  
  
      
  
}  

  view pl

包体,我简单处理用字符串转字节码,一般好多游戏用probuf系列化成二进制

Message.java

package com.test.netty.message;  
  
import io.netty.buffer.ByteBuf;  
import io.netty.buffer.Unpooled;  
  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.io.UnsupportedEncodingException;  
  
import com.test.netty.decoder.MessageDecoder;  
  
/** 
 * Message.java 
 *  
 * @author janehuang 
 * @version 1.0 
 */  
public class Message {  
  
    private Header header;  
  
    private String data;  
  
    public Header getHeader() {  
        return header;  
    }  
  
    public void setHeader(Header header) {  
        this.header = header;  
    }  
  
    public String getData() {  
        return data;  
    }  
  
    public void setData(String data) {  
        this.data = data;  
    }  
  
    public Message(Header header) {  
        this.header = header;  
    }  
  
    public Message(Header header, String data) {  
        this.header = header;  
        this.data = data;  
    }  
  
    public byte[] toByte() {  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        out.write(MessageDecoder.PACKAGE_TAG);  
        out.write(header.getEncode());  
        out.write(header.getEncrypt());  
        out.write(header.getExtend1());  
        out.write(header.getExtend2());  
        byte[] bb = new byte[32];  
        byte[] bb2 = header.getSessionid().getBytes();  
        for (int i = 0; i < bb2.length; i++) {  
            bb[i] = bb2[i];  
        }  
  
        try {  
            out.write(bb);  
  
            byte[] bbb = data.getBytes("UTF-8");  
            out.write(intToBytes2(bbb.length));  
            out.write(intToBytes2(header.getCammand()));  
            out.write(bbb);  
            out.write('\n');  
        } catch (UnsupportedEncodingException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        return out.toByteArray();  
    }  
  
    public static byte[] intToByte(int newint) {  
        byte[] intbyte = new byte[4];  
        intbyte[3] = (byte) ((newint >> 24) & 0xFF);  
        intbyte[2] = (byte) ((newint >> 16) & 0xFF);  
        intbyte[1] = (byte) ((newint >> 8) & 0xFF);  
        intbyte[0] = (byte) (newint & 0xFF);  
        return intbyte;  
    }  
  
    public static int bytesToInt(byte[] src, int offset) {  
        int value;  
        value = (int) ((src[offset] & 0xFF) | ((src[offset + 1] & 0xFF) << 8) | ((src[offset + 2] & 0xFF) << 16) | ((src[offset + 3] & 0xFF) << 24));  
        return value;  
    }  
  
    public static byte[] intToBytes2(int value) {  
        byte[] src = new byte[4];  
        src[0] = (byte) ((value >> 24) & 0xFF);  
        src[1] = (byte) ((value >> 16) & 0xFF);  
        src[2] = (byte) ((value >> 8) & 0xFF);  
        src[3] = (byte) (value & 0xFF);  
        return src;  
    }  
  
    public static void main(String[] args) {  
        ByteBuf heapBuffer = Unpooled.buffer(8);  
        System.out.println(heapBuffer);  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        try {  
            out.write(intToBytes2(1));  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        byte[] data = out.toByteArray();  
        heapBuffer.writeBytes(data);  
        System.out.println(heapBuffer);  
        int a = heapBuffer.readInt();  
        System.out.println(a);  
    }  
  
}  



 view plain cop

解码器

MessageDecoder.java

[java] view plain copy
  1. package com.test.netty.decoder;  
  2.   
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.handler.codec.ByteToMessageDecoder;  
  6. import io.netty.handler.codec.CorruptedFrameException;  
  7.   
  8. import java.util.List;  
  9.   
  10. import com.test.netty.message.Header;  
  11. import com.test.netty.message.Message;  
  12.   
  13.   
  14.   
  15. /** 
  16.  * HeaderDecoder.java 
  17.  *  
  18.  * @author janehuang 
  19.  * @version 1.0 
  20.  */  
  21. public class MessageDecoder extends ByteToMessageDecoder {  
  22.     /**包长度志头**/  
  23.     public static final int HEAD_LENGHT = 45;  
  24.     /**标志头**/  
  25.     public static final byte PACKAGE_TAG = 0x01;  
  26.     @Override  
  27.     protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception {  
  28.         buffer.markReaderIndex();  
  29.         if (buffer.readableBytes() < HEAD_LENGHT) {  
  30.             throw new CorruptedFrameException("包长度问题");  
  31.         }  
  32.         byte tag = buffer.readByte();  
  33.         if (tag != PACKAGE_TAG) {  
  34.             throw new CorruptedFrameException("标志错误");  
  35.         }  
  36.         byte encode = buffer.readByte();  
  37.         byte encrypt = buffer.readByte();  
  38.         byte extend1 = buffer.readByte();  
  39.         byte extend2 = buffer.readByte();  
  40.         byte sessionByte[] = new byte[32];  
  41.         buffer.readBytes(sessionByte);  
  42.         String sessionid = new String(sessionByte,"UTF-8");  
  43.         int length = buffer.readInt();  
  44.         int cammand=buffer.readInt();  
  45.         Header header = new Header(tag,encode, encrypt, extend1, extend2, sessionid, length, cammand);  
  46.         byte[] data=new byte[length];  
  47.         buffer.readBytes(data);  
  48.         Message message = new Message(header,new String(data,"UTF-8"));  
  49.         out.add(message);  
  50.     }  
  51. }  



  52. 编码器

    MessageEncoder.java

    [java] view plain copy
    1. package com.test.netty.encoder;  
    2.   
    3.   
    4.   
    5. import com.test.netty.decoder.MessageDecoder;  
    6. import com.test.netty.message.Header;  
    7. import com.test.netty.message.Message;  
    8.   
    9. import io.netty.buffer.ByteBuf;  
    10. import io.netty.channel.ChannelHandlerContext;  
    11. import io.netty.handler.codec.MessageToByteEncoder;  
    12.   
    13.   
    14. /** 
    15.  * MessageEncoder.java 
    16.  *  
    17.  * @author janehuang 
    18.  * @version 1.0  
    19.  */  
    20. public class MessageEncoder extends MessageToByteEncoder {  
    21.   
    22.     @Override  
    23.     protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {  
    24.             Header header = msg.getHeader();  
    25.             out.writeByte(MessageDecoder.PACKAGE_TAG);  
    26.             out.writeByte(header.getEncode());  
    27.             out.writeByte(header.getEncrypt());  
    28.             out.writeByte(header.getExtend1());  
    29.             out.writeByte(header.getExtend2());  
    30.             out.writeBytes(header.getSessionid().getBytes());  
    31.             out.writeInt(header.getLength());  
    32.             out.writeInt(header.getCammand());  
    33.             out.writeBytes(msg.getData().getBytes("UTF-8"));  
    34.     }  
    35.   
    36. }  
    服务器

    TimeServer.java

    [java] view plain copy
    1. package com.test.netty.server;  
    2.   
    3.   
    4. import org.springframework.stereotype.Component;  
    5.   
    6.   
    7. import io.netty.bootstrap.ServerBootstrap;  
    8. import io.netty.buffer.ByteBuf;  
    9. import io.netty.buffer.Unpooled;  
    10. import io.netty.channel.ChannelFuture;  
    11. import io.netty.channel.ChannelInitializer;  
    12. import io.netty.channel.ChannelOption;  
    13. import io.netty.channel.EventLoopGroup;  
    14. import io.netty.channel.nio.NioEventLoopGroup;  
    15. import io.netty.channel.socket.SocketChannel;  
    16. import io.netty.channel.socket.nio.NioServerSocketChannel;  
    17. import io.netty.handler.codec.LineBasedFrameDecoder;  
    18.   
    19.   
    20. import com.test.netty.decoder.MessageDecoder;  
    21. import com.test.netty.encoder.MessageEncoder;  
    22. import com.test.netty.handler.ServerHandler;  
    23.   
    24.   
    25. /** 
    26.  * ChatServer.java 
    27.  *  
    28.  * @author janehuang 
    29.  * @version 1.0 
    30.  */  
    31.   
    32.   
    33. @Component  
    34. public class TimeServer {  
    35.   
    36.     private int port=88888;  
    37.   
    38.   
    39.     public void run() throws InterruptedException {  
    40.         EventLoopGroup bossGroup = new NioEventLoopGroup();  
    41.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
    42.         ByteBuf heapBuffer = Unpooled.buffer(8);  
    43.         heapBuffer.writeBytes("\r".getBytes());  
    44.         try {  
    45.             ServerBootstrap b = new ServerBootstrap(); // (2)  
    46.             b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class// (3)  
    47.                     .childHandler(new ChannelInitializer() { // (4)  
    48.                                 @Override  
    49.                                 public void initChannel(SocketChannel ch) throws Exception {  
    50.                                     ch.pipeline().addLast("encoder"new MessageEncoder()).addLast("decoder"new MessageDecoder()).addFirst(new LineBasedFrameDecoder(65535))  
    51.                                             .addLast(new ServerHandler());  
    52.                                 }  
    53.                             }).option(ChannelOption.SO_BACKLOG, 1024// (5)  
    54.                     .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)  
    55.             ChannelFuture f = b.bind(port).sync(); // (7)  
    56.             f.channel().closeFuture().sync();  
    57.         } finally {  
    58.             workerGroup.shutdownGracefully();  
    59.             bossGroup.shutdownGracefully();  
    60.         }  
    61.     }  
    62.       
    63.     public void start(int port) throws InterruptedException{  
    64.       this.port=port;  
    65.       this.run();  
    66.     }  
    67.   
    68. }  


    处理器并分发

    ServerHandler.java

    [java] view plain copy
    1. package com.test.netty.handler;  
    2.   
    3. import io.netty.channel.ChannelHandlerAdapter;  
    4. import io.netty.channel.ChannelHandlerContext;  
    5.   
    6. import com.test.netty.invote.ActionMapUtil;  
    7. import com.test.netty.message.Header;  
    8. import com.test.netty.message.Message;  
    9.   
    10. /** 
    11.  *  
    12.  * @author janehuang 
    13.  * 
    14.  */  
    15. public class ServerHandler extends ChannelHandlerAdapter {  
    16.       
    17.   
    18.   
    19.     @Override  
    20.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
    21.         String content="我收到连接";  
    22.         Header header=new Header((byte)0, (byte)1, (byte)1, (byte)1, (byte)0"713f17ca614361fb257dc6741332caf2",content.getBytes("UTF-8").length, 1);  
    23.         Message message=new Message(header,content);  
    24.         ctx.writeAndFlush(message);  
    25.           
    26.     }  
    27.   
    28.     @Override  
    29.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {  
    30.         cause.printStackTrace();  
    31.         ctx.close();  
    32.     }  
    33.   
    34.     @Override  
    35.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
    36.          Message m = (Message) msg; // (1)  
    37.            
    38.         /* 请求分发*/  
    39.         ActionMapUtil.invote(header.getCammand(),ctx, m);  
    40.     }  
    41.       
    42.       
    43. }  

    分发工具类

    ActionMapUtil.java

    [java] view plain copy
    1. package com.test.netty.invote;  
    2.   
    3. import java.lang.reflect.Method;  
    4. import java.util.HashMap;  
    5. import java.util.Map;  
    6.   
    7. public class ActionMapUtil {  
    8.   
    9.     private static Map map = new HashMap();  
    10.   
    11.     public static Object invote(Integer key, Object... args) throws Exception {  
    12.         Action action = map.get(key);  
    13.         if (action != null) {  
    14.             Method method = action.getMethod();  
    15.             try {  
    16.                 return method.invoke(action.getObject(), args);  
    17.             } catch (Exception e) {  
    18.                 throw e;  
    19.             }  
    20.         }  
    21.         return null;  
    22.     }  
    23.   
    24.     public static void put(Integer key, Action action) {  
    25.         map.put(key, action);  
    26.     }  
    27.   
    28. }  

    为分发创建的对象

    Action.java

    [java] view plain copy
    1. package com.test.netty.invote;  
    2.   
    3. import java.lang.reflect.Method;  
    4.   
    5. public class Action {  
    6.       
    7.     private Method method;  
    8.       
    9.     private Object object;  
    10.   
    11.     public Method getMethod() {  
    12.         return method;  
    13.     }  
    14.   
    15.     public void setMethod(Method method) {  
    16.         this.method = method;  
    17.     }  
    18.   
    19.     public Object getObject() {  
    20.         return object;  
    21.     }  
    22.   
    23.     public void setObject(Object object) {  
    24.         this.object = object;  
    25.     }  
    26.       
    27.   
    28. }  
    自定义注解,类似springmvc 里面的@Controller

    NettyController.java

    [java] view plain copy
    1. package com.test.netty.core;  
    2.   
    3. import java.lang.annotation.Documented;  
    4. import java.lang.annotation.ElementType;  
    5. import java.lang.annotation.Retention;  
    6. import java.lang.annotation.RetentionPolicy;  
    7. import java.lang.annotation.Target;  
    8.   
    9. import org.springframework.stereotype.Component;  
    10.   
    11. @Retention(RetentionPolicy.RUNTIME)  
    12. @Target(ElementType.TYPE)  
    13. @Documented  
    14. @Component  
    15. public @interface NettyController {  
    16.       
    17.         
    18. }  

    类型spring mvc里面的@ReqestMapping

    ActionMap.java

    [java] view plain copy
    1. package com.test.netty.core;  
    2.   
    3. import java.lang.annotation.Documented;  
    4. import java.lang.annotation.ElementType;  
    5. import java.lang.annotation.Retention;  
    6. import java.lang.annotation.RetentionPolicy;  
    7. import java.lang.annotation.Target;  
    8.   
    9. @Retention(RetentionPolicy.RUNTIME)  
    10. @Target(ElementType.METHOD)  
    11. @Documented  
    12. public @interface ActionMap {  
    13.       
    14.       int key();  
    15.         
    16. }  
    加了这些注解是为了spring初始化bean后把这些对象存到容器,此bean需要在spring配置,spring bean 实例化后会调用

    ActionBeanPostProcessor.java

    [java] view plain copy
    1. package com.test.netty.core;  
    2.   
    3. import java.lang.reflect.Method;  
    4.   
    5. import org.springframework.beans.BeansException;  
    6. import org.springframework.beans.factory.config.BeanPostProcessor;  
    7.   
    8. import com.test.netty.invote.Action;  
    9. import com.test.netty.invote.ActionMapUtil;  
    10.   
    11. public class ActionBeanPostProcessor implements BeanPostProcessor  {  
    12.   
    13.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
    14.         return bean;  
    15.     }  
    16.   
    17.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
    18.         Method[] methods=bean.getClass().getMethods();  
    19.         for (Method method : methods) {  
    20.             ActionMap actionMap=method.getAnnotation(ActionMap.class);  
    21.             if(actionMap!=null){  
    22.                 Action action=new Action();  
    23.                 action.setMethod(method);  
    24.                 action.setObject(bean);  
    25.                 ActionMapUtil.put(actionMap.key(), action);  
    26.             }  
    27.         }  
    28.         return bean;  
    29.     }  
    30.   
    31. }  

    controller实例

    UserController.java

    [java] view plain copy
    1. "code" class="java">package com.test.netty.controller;  
    2.   
    3. import io.netty.channel.ChannelHandlerContext;  
    4.   
    5. import org.springframework.beans.factory.annotation.Autowired;  
    6.   
    7. import com.test.model.UserModel;  
    8. import com.test.netty.core.ActionMap;  
    9. import com.test.netty.core.NettyController;  
    10. import com.test.netty.message.Message;  
    11. import com.test.service.UserService;  
    12.   
    13.   
    14.   
    15. @NettyController()  
    16. public class UserAction {  
    17.       
    18.       
    19.     @Autowired  
    20.     private UserService userService;  
    21.       
    22.       
    23.     @ActionMap(key=1)  
    24.     public String login(ChannelHandlerContext ct,Message message){  
    25.         UserModel userModel=this.userService.findByMasterUserId(1000001);  
    26.         System.out.println(String.format("用户昵称:%s;密码%d;传人内容%s", userModel.getNickname(),userModel.getId(),message.getData()));  
    27.         return userModel.getNickname();  
    28.     }  
    29.   
    30. }  


    applicationContext.xml配置文件记得加入这个

    [html] view plain copy
    1. <bean class="com.test.netty.core.ActionBeanPostProcessor"/>  


    测试代码

    [java] view plain copy
    1. package test;  
    2.   
    3. import org.springframework.context.ApplicationContext;  
    4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
    5.   
    6. import com.test.netty.server.TimeServer;  
    7.   
    8. public class Test {  
    9.   
    10.       
    11.     public static void main(String[] args) {  
    12.           ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");    
    13.           TimeServer timeServer=  ac.getBean(TimeServer.class);  
    14.           try {  
    15.             timeServer.start(8888);  
    16.         } catch (InterruptedException e) {  
    17.             // TODO Auto-generated catch block  
    18.             e.printStackTrace();  
    19.         }  
    20.             
    21.     }  
    22.       
    23.       
    24. }  

    测试开关端

    [java] view plain copy
    1. package test;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.OutputStream;  
    5. import java.net.Socket;  
    6. import java.util.Scanner;  
    7.   
    8. import com.test.netty.message.Header;  
    9. import com.test.netty.message.Message;  
    10.   
    11. public class ClientTest {  
    12.   
    13.     public static void main(String[] args) {  
    14.         try {  
    15.             // 连接到服务器  
    16.             Socket socket = new Socket("127.0.0.1"8888);  
    17.   
    18.             try {  
    19.                 // 向服务器端发送信息的DataOutputStream  
    20.                 OutputStream out = socket.getOutputStream();  
    21.                 // 装饰标准输入流,用于从控制台输入  
    22.                 Scanner scanner = new Scanner(System.in);  
    23.                 while (true) {  
    24.                     String send = scanner.nextLine();  
    25.                     System.out.println("客户端:" + send);  
    26.                     byte[] by = send.getBytes("UTF-8");  
    27.                     Header header = new Header((byte1, (byte1, (byte1, (byte1, (byte1"713f17ca614361fb257dc6741332caf2", by.length, 1);  
    28.                     Message message = new Message(header, send);  
    29.                     out.write(message.toByte());  
    30.                     out.flush();  
    31.                     // 把从控制台得到的信息传送给服务器  
    32.                     // out.writeUTF("客户端:" + send);  
    33.                     // 读取来自服务器的信息  
    34.                 }  
    35.   
    36.             } finally {  
    37.                 socket.close();  
    38.             }  
    39.         } catch (IOException e) {  
    40.             e.printStackTrace();  
    41.         }  
    42.     }  
    43. }  

    测试结果,ok了

    你可能感兴趣的:(java,netty)