游戏一般是长连接,自定义协议,不用http协议,bio,nio,aio这些我就不说了,自己查资料
思路:1自定义协议和协议包;2spring+netty整合;3半包粘包处理,心跳机制等;4请求分发(目前自己搞的都是单例模式)
下个是测试用的,结构如下
首先自定义包头
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了
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://blog.csdn.net/u012930316/article/details/73743305