欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring+netty服务器搭建的方法

程序员文章站 2023-12-04 16:54:40
游戏一般是长连接,自定义协议,不用http协议,bio,nio,aio这些我就不说了,自己查资料 我现在用spring+netty搭起简单的游戏服 思路:1自定...

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

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

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

spring+netty服务器搭建的方法

首先自定义包头

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+netty服务器搭建的方法

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