服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|JavaScript|易语言|

服务器之家 - 编程语言 - JAVA教程 - spring+netty服务器搭建的方法

spring+netty服务器搭建的方法

2021-03-29 10:09病毒先生 JAVA教程

本篇文章主要介绍了spring+netty服务器搭建的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

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

spring+netty服务器搭建的方法

首先自定义包头

header.java

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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

?
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
45
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

?
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
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

?
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
45
46
47
48
49
50
51
52
53
54
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

?
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
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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

?
1
2
3
4
5
6
7
8
9
10
11
12
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

?
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
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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配置文件记得加入这个

?
1
<bean class="com.test.netty.core.actionbeanpostprocessor"/>

测试代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
    }
  }
}

测试开关端

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

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

原文链接:http://blog.csdn.net/u012930316/article/details/73743305

延伸 · 阅读

精彩推荐