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