Spring + netty Server SETUP method, springnetty
Games are generally persistent connections, custom protocols, and http protocols are not required, BIO, NIO, or AIO.
I now use spring + netty to build a simple game server
Ideas: 1. custom protocol and Protocol package; 2. spring + netty integration; 3. Half-package stick packet processing; heartbeat mechanism; 4. Request distribution (currently we are working on Singleton Mode)
The next one is for testing. The structure is as follows:
First, customize the header
Header. java
Package com. test. netty. message;/*** Header. java * custom protocol Header * @ author janehuang * @ version 1.0 */public class Header {private byte tag;/* encoding */private byte encode; /* encrypt */private byte encrypt;/* Other fields */private byte extend1;/* Other 2 */private byte extend2;/* session id */private String sessionid; /* package length */private int length = 1024;/* command */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 ;}}
Package body. I use strings to convert bytecode for simple processing. Generally, many games use probuf to serialize data into binary.
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); } }
Decoder
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 Messa GeDecoder extends ByteToMessageDecoder {/** Packet Length header **/public static final int HEAD_LENGHT = 45;/** flag header **/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 writable uptedframeexception ("packet length problem");} byte tag = buffer. read Byte (); if (tag! = PACKAGE_TAG) {throw new writable uptedframeexception ("flag error");} 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 );}}
Encoder
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")); } }
Server
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(); } }
Processor and distribution
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 = "I have received the connection "; 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) /* request Distribution */ActionMapUtil. invote (header. getCammand (), ctx, m );}}
Distribution tool
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); } }
Objects Created for Distribution
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; } }
Custom annotation, similar to @ Controller in springmvc
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 { }
@ ReqestMapping in spring mvc
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(); }
These annotations are added to save these objects to the container after spring initializes the bean. This bean needs to be configured in spring and will be called after spring bean is instantiated.
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 instance
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. the findByMasterUserId (1000001); System. out. println (String. format ("user nickname: % s; password % d; descendant content % s", userModel. getNickname (), userModel. getId (), message. getData (); return userModel. getNickname ();}}
Remember to add the applicationContext. xml configuration file
<bean class="com.test.netty.core.ActionBeanPostProcessor"/>
Test code
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(); } } }
Test the switch end
Package test; import java. io. IOException; import java. io. outputStream; import java.net. socket; import java. util. producer; import com. test. netty. message. header; import com. test. netty. message. message; public class ClientTest {public static void main (String [] args) {try {// connect to the server Socket socket = new Socket ("127.0.0.1", 8888 ); try {// DataOutputStream OutputStream out = socket. getOutputStream (); // enter the standard input stream for decoration. in); while (true) {String send = success. nextLine (); System. out. println ("client:" + send); byte [] by = send. getBytes ("UTF-8"); Header header = new Header (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, "713f17ca614361fb257dc6741332caf2",. length, 1); Message message = new Message (header, send); out. write (message. toByte (); out. flush (); // send the information obtained from the console to the server // out. writeUTF ("client:" + send); // read information from the server} finally {socket. close () ;}} catch (IOException e) {e. printStackTrace ();}}}
Test result, OK
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.