首页 > 编程 > Java > 正文

spring+netty服务器搭建的方法

2019-11-26 10:21:22
字体:
来源:转载
供稿:网友

游戏一般是长连接,自定义协议,不用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;    }  }  

包体,我简单处理用字符串转字节码,一般好多游戏用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);    }    }  

解码器

MessageDecoder.java

package com.test.netty.decoder; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException;  import java.util.List;  import com.test.netty.message.Header; import com.test.netty.message.Message; /**  * HeaderDecoder.java  *  * @author janehuang  * @version 1.0  */ public class MessageDecoder extends ByteToMessageDecoder {   /**包长度志头**/   public static final int HEAD_LENGHT = 45;   /**标志头**/   public static final byte PACKAGE_TAG = 0x01;   @Override   protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {     buffer.markReaderIndex();     if (buffer.readableBytes() < HEAD_LENGHT) {       throw new CorruptedFrameException("包长度问题");     }     byte tag = buffer.readByte();     if (tag != PACKAGE_TAG) {       throw new CorruptedFrameException("标志错误");     }     byte encode = buffer.readByte();     byte encrypt = buffer.readByte();     byte extend1 = buffer.readByte();     byte extend2 = buffer.readByte();     byte sessionByte[] = new byte[32];     buffer.readBytes(sessionByte);     String sessionid = new String(sessionByte,"UTF-8");     int length = buffer.readInt();     int cammand=buffer.readInt();     Header header = new Header(tag,encode, encrypt, extend1, extend2, sessionid, length, cammand);     byte[] data=new byte[length];     buffer.readBytes(data);     Message message = new Message(header,new String(data,"UTF-8"));     out.add(message);   } } 

编码器

MessageEncoder.java

package com.test.netty.encoder; import com.test.netty.decoder.MessageDecoder; import com.test.netty.message.Header; import com.test.netty.message.Message;  import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder;   /**  * MessageEncoder.java  *  * @author janehuang  * @version 1.0  */ public class MessageEncoder extends MessageToByteEncoder<Message> {    @Override   protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {       Header header = msg.getHeader();       out.writeByte(MessageDecoder.PACKAGE_TAG);       out.writeByte(header.getEncode());       out.writeByte(header.getEncrypt());       out.writeByte(header.getExtend1());       out.writeByte(header.getExtend2());       out.writeBytes(header.getSessionid().getBytes());       out.writeInt(header.getLength());       out.writeInt(header.getCammand());       out.writeBytes(msg.getData().getBytes("UTF-8"));   }  } 

服务器

TimeServer.java

package com.test.netty.server; import org.springframework.stereotype.Component; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder;  import com.test.netty.decoder.MessageDecoder; import com.test.netty.encoder.MessageEncoder; import com.test.netty.handler.ServerHandler; /**  * ChatServer.java  *  * @author janehuang  * @version 1.0  */ @Component public class TimeServer {    private int port=88888;   public void run() throws InterruptedException {     EventLoopGroup bossGroup = new NioEventLoopGroup();     EventLoopGroup workerGroup = new NioEventLoopGroup();     ByteBuf heapBuffer = Unpooled.buffer(8);     heapBuffer.writeBytes("/r".getBytes());     try {       ServerBootstrap b = new ServerBootstrap(); // (2)       b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3)           .childHandler(new ChannelInitializer<SocketChannel>() { // (4)                 @Override                 public void initChannel(SocketChannel ch) throws Exception {                   ch.pipeline().addLast("encoder", new MessageEncoder()).addLast("decoder", new MessageDecoder()).addFirst(new LineBasedFrameDecoder(65535))                       .addLast(new ServerHandler());                 }               }).option(ChannelOption.SO_BACKLOG, 1024) // (5)           .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)       ChannelFuture f = b.bind(port).sync(); // (7)       f.channel().closeFuture().sync();     } finally {       workerGroup.shutdownGracefully();       bossGroup.shutdownGracefully();     }   }      public void start(int port) throws InterruptedException{    this.port=port;    this.run();   }  } 

处理器并分发

ServerHandler.java

package com.test.netty.handler;  import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext;  import com.test.netty.invote.ActionMapUtil; import com.test.netty.message.Header; import com.test.netty.message.Message; /**  *  * @author janehuang  *  */ public class ServerHandler extends ChannelHandlerAdapter {   @Override   public void channelActive(ChannelHandlerContext ctx) throws Exception {     String content="我收到连接";     Header header=new Header((byte)0, (byte)1, (byte)1, (byte)1, (byte)0, "713f17ca614361fb257dc6741332caf2",content.getBytes("UTF-8").length, 1);     Message message=new Message(header,content);     ctx.writeAndFlush(message);        }    @Override   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {     cause.printStackTrace();     ctx.close();   }    @Override   public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {      Message m = (Message) msg; // (1)           /* 请求分发*/     ActionMapUtil.invote(header.getCammand(),ctx, m);   }      } 

分发工具类

ActionMapUtil.java

package com.test.netty.invote; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class ActionMapUtil {   private static Map<Integer, Action> map = new HashMap<Integer, Action>();    public static Object invote(Integer key, Object... args) throws Exception {     Action action = map.get(key);     if (action != null) {       Method method = action.getMethod();       try {         return method.invoke(action.getObject(), args);       } catch (Exception e) {         throw e;       }     }     return null;   }    public static void put(Integer key, Action action) {     map.put(key, action);   }  } 

为分发创建的对象

Action.java

package com.test.netty.invote;  import java.lang.reflect.Method;  public class Action {      private Method method;   private Object object;    public Method getMethod() {     return method;   }    public void setMethod(Method method) {     this.method = method;   }    public Object getObject() {     return object;   }    public void setObject(Object object) {     this.object = object;   } } 

自定义注解,类似springmvc 里面的@Controller

NettyController.java

package com.test.netty.core;  import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  import org.springframework.stereotype.Component;  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Component public @interface NettyController { } 

类型spring mvc里面的@ReqestMapping

ActionMap.java

package com.test.netty.core; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface ActionMap {     int key();     } 

加了这些注解是为了spring初始化bean后把这些对象存到容器,此bean需要在spring配置,spring bean 实例化后会调用

ActionBeanPostProcessor.java

package com.test.netty.core; import java.lang.reflect.Method; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import com.test.netty.invote.Action; import com.test.netty.invote.ActionMapUtil; public class ActionBeanPostProcessor implements BeanPostProcessor {   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {     return bean;   }    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {     Method[] methods=bean.getClass().getMethods();     for (Method method : methods) {       ActionMap actionMap=method.getAnnotation(ActionMap.class);       if(actionMap!=null){         Action action=new Action();         action.setMethod(method);         action.setObject(bean);         ActionMapUtil.put(actionMap.key(), action);       }     }     return bean;   } } 

controller实例

UserController.java

package com.test.netty.controller;  import io.netty.channel.ChannelHandlerContext;  import org.springframework.beans.factory.annotation.Autowired; import com.test.model.UserModel; import com.test.netty.core.ActionMap; import com.test.netty.core.NettyController; import com.test.netty.message.Message; import com.test.service.UserService;  @NettyController() public class UserAction {   @Autowired   private UserService userService;      @ActionMap(key=1)   public String login(ChannelHandlerContext ct,Message message){     UserModel userModel=this.userService.findByMasterUserId(1000001);     System.out.println(String.format("用户昵称:%s;密码%d;传人内容%s", userModel.getNickname(),userModel.getId(),message.getData()));     return userModel.getNickname();   }  } 

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

<bean class="com.test.netty.core.ActionBeanPostProcessor"/> 

测试代码

package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.test.netty.server.TimeServer; public class Test {   public static void main(String[] args) {      ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");       TimeServer timeServer= ac.getBean(TimeServer.class);      try {       timeServer.start(8888);     } catch (InterruptedException e) {       // TODO Auto-generated catch block       e.printStackTrace();     }   } } 

测试开关端

package test;  import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; import com.test.netty.message.Header; import com.test.netty.message.Message;  public class ClientTest {    public static void main(String[] args) {     try {       // 连接到服务器       Socket socket = new Socket("127.0.0.1", 8888);        try {         // 向服务器端发送信息的DataOutputStream         OutputStream out = socket.getOutputStream();         // 装饰标准输入流,用于从控制台输入         Scanner scanner = new Scanner(System.in);         while (true) {           String send = scanner.nextLine();           System.out.println("客户端:" + send);           byte[] by = send.getBytes("UTF-8");           Header header = new Header((byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, "713f17ca614361fb257dc6741332caf2", by.length, 1);           Message message = new Message(header, send);           out.write(message.toByte());           out.flush();           // 把从控制台得到的信息传送给服务器           // out.writeUTF("客户端:" + send);           // 读取来自服务器的信息         }        } finally {         socket.close();       }     } catch (IOException e) {       e.printStackTrace();     }   } } 

测试结果,ok了

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表