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

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

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

您可能感兴趣的文章:

  • Spring Boot实战之netty-socketio实现简单聊天室(给指定用户推送消息)
(0)

相关推荐

  • Spring Boot实战之netty-socketio实现简单聊天室(给指定用户推送消息)

    网上好多例子都是群发的,本文实现一对一的发送,给指定客户端进行消息推送 1.本文使用到netty-socketio开源库,以及MySQL,所以首先在pom.xml中添加相应的依赖库 <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.11</version&

  • spring+netty服务器搭建的方法

    游戏一般是长连接,自定义协议,不用http协议,BIO,NIO,AIO这些我就不说了,自己查资料 我现在用spring+netty搭起简单的游戏服 思路:1自定义协议和协议包:2spring+netty整合:3半包粘包处理,心跳机制等:4请求分发(目前自己搞的都是单例模式) 下个是测试用的,结构如下 首先自定义包头 Header.java package com.test.netty.message; /** * Header.java * 自定义协议包头 * @author janehuang

  • Spring MVC 框架搭建配置方法及详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理. 一.Spring MVC环境搭建:(Spring 2.5.6 + Hibernate 3.2.0) 1. jar包引入 Spring 2.5.6:spring.jar.spring-webmvc.jar.comm

  • windows server 2019 服务器搭建的方法步骤(图文)

    一.windows server 2019 安装 Vmware 下安装 windows server 2019 . 二.服务器配置 1. 先启用远程功能 右键点击"此电脑"--"属性",进入"控制面板\系统和安全\系统",点击远程设置. 2.在"远程桌面"下方,点击"允许远程连接到此计算机",还有去掉下方"仅允许运行使用网络级别身份验证的远程桌面的计算机连接" 3.在运行中运行gpedi

  • Spring+Mybatis+Mysql搭建分布式数据库访问框架的方法

    一.前言 用Java开发企业应用软件, 经常会采用Spring+MyBatis+Mysql搭建数据库框架.如果数据量很大,一个MYSQL库存储数据访问效率很低,往往会采用分库存储管理的方式.本文讲述如何通过Spring+Mybatis构建多数据库访问的架构,并采用多线程提升数据库的访问效率. 需要说明一下,这种方式只适合数据库数量.名称固定,且不是特别多的情况.针对数据库数量不固定的情况,后面再写一篇处理方案. 二.整体方案 三.开发环境准备 3.1 下载Spring.Mybatis.Mysql

  • 本地搭建微信小程序服务器的实现方法

    本地搭建微信小程序服务器的实现方法 现在开发需要购买服务器,价格还是有点贵的,可以花费小代价就可以搭建一个服务器,可以用来开发小程序,博客等. 1.域名(备案过的)  2.阿里云注册免费的https证书  3.配置本地的nginx  4.内网映射(本地安装wampserver 服务器) 一.域名 注册花生壳,开通内网映射需要8元(我开通时需要,现在不清楚还要不要),里面可以注册2个免费的域名,都是免备案的.具体的请自行百度,花生壳注册地址 二.申请阿里云免费的https证书 阿里云免费的http

  • Windows SVN服务器搭建方法

    这里我就介绍一个在Windows环境下简单快速搭建SVN服务器的方法.通常的SVN服务器是搭建在Linux等系统下,例如用Apache+SVN配置,Linux下的SVN性能会非常好,但配置有些繁琐,如果SVN服务器只有自己使用,那么可以直接把SVN服务器搭建在个人Windows环境下使用. 目前较为简单的方案是VisualSVN Server.该SVN服务器是免费的,支持Windows NT, 2000, XP and 2003等环境,安装非常简单. 安装的时候可以选择SVN走http协议还是h

  • Windows下SVN服务器搭建方法整理(apache)

    本节和大家谈谈Windows下SVN服务器搭建问题,在这里拿出来和大家分享一下,希望对大家有用. 1,软件下载 Windows下SVN服务器搭建,下载Subversion服务器程序.到官方网站的下载二进制安装文件,来到二进制包下载部分,找到WindowsNT,2000,XPand2003部分,然后选择"thisdirectory",这样我们可以看到许多下载的内容,目前可以下载svn-1.4.0-setup.exe.下载Subversion的Windows客户端TortoiseSVN.T

  • 利用nodeJs anywhere搭建本地服务器环境的方法

    公司有个微信端项目,需要前端在手机上随时查看网页,于是乎用Node搭建了一个本地服务器环境,把网页地址发到QQ并用手机打开来查看. 首先去nodeJs官网下载最新版nodeJs https://nodejs.org/en/ 安装成功后win+r打开cmd 输入node -help 或者node -v查看是否安装成功 装好后输入 npm install anywhere -g来安装anywhere.注意如果是mac系统会提示你权限不够,需要在代码前加上 sudo获取管理员权限.即sudo npm

  • Centos7.3服务器搭建LNMP环境的方法

    本文实例讲述了Centos7.3服务器搭建LNMP环境的方法.分享给大家供大家参考,具体如下: 需求:在Centos7.3下搭建LNMP环境 1. 关闭防火墙和selinux 打开文件selinux vim /etc/sysconfig/selinux 将文件中SELINUX=enforcing改为disabled,然后执行"setenforce 0″不用重启地关闭selinux. SELINUX=disabled 关闭放火墙 systemctl stop firewalld.service 2

  • 服务器购买和初步搭建的方法

    有段时间没有去搞过服务器了,趁现在没有太多的事情,去搬wa工买个国外的服务器用(为什么要买国外的服务器?比较便宜,而且可以FQ,你懂得( • ̀ω•́ )✧),可以用支付宝支付,顺便把购买和初步搭建的过程记录下来方便以后使用. 一.购买服务器 进入搬wa工官网https://bwh88.net/ 选择你想要购买的服务器类型,我这里选了个49.99美刀一年的 点Order,进入购买详情页面,选择服务地址,我选择的是洛杉矶的,然后Add to Cart 然后进行购买结算,输入优惠码:BWH26FXH

随机推荐