Os jogos geralmente têm conexões longas e protocolos personalizados, e não usam protocolos HTTP. Não vou falar sobre biografia, nio, aio etc., verifique as informações sozinho.
Estou usando o Spring+Netty para configurar um servidor de jogo simples
Ideias: 1. Pacote de protocolo e protocolo personalizado; 2.Spring+Integração da Netty; 3.falt-pacote Processamento de aderência, mecanismo de batimento cardíaco, etc.; 4. Request Distribution (atualmente, estou fazendo o modo Singleton)
Em seguida é para teste, a estrutura é a seguinte
Primeiro, personalize o cabeçalho do pacote
Cabeçalho.java
pacote com.test.netty.message; / ** * header.java * cabeçalho de protocolo personalizado * @author janehuang * @version 1.0 */ public classe cabeçalho {private byte tag; /* Codificação*/ byte privado Encode; /* Criptografia*/ byte privado Encrypt; /*Outros campos*/ byte privado estend1; /*Outro byte 2*/ privado estend2; /*SessionId*/ private String sessionID; /*Comprimento do pacote*/ Private int Comprimento = 1024; /*Comando*/ private int cammand; public header () {} public cabeçalho (String sessionId) {this.Encode = 0; this.Encrypt = 0; this.SessionId = sessionId; } cabeçalho público (tag byte, codificação de byte, byte criprypt, byte cripty, byte estend1, byte estend2, string sessionId, int length, int bullet) {this.tag = tag; this.Encode = Encode; this.Encrypt = Encrypt; this.extend1 = estend1; this.extend2 = estend2; this.SessionId = sessionId; this.length = comprimento; this.cammand = Cammand; } @Override public string tostring () {return "cabeçalho [tag =" + tag + "code =" + code + ", Encrypt =" + Encrypt + ", estend1 =" + estend1 + ", extensão2 =" + estend2 + "," SessionId + Session + ", comprimento" + " +", "; } public byte getTag () {return tag; } public void Settag (tag byte) {this.tag = tag; } public byte getEncode () {return code; } public void setencode (byte cody) {this.encode = code; } public byte getEncrypt () {return Encrypt; } public void setencrypt (byte cripty) {this.Encrypt = Encrypt; } public byte getExtend1 () {return estend1; } public void SetExtend1 (byte estend1) {this.extend1 = extend1; } public byte getExtend2 () {return estend2; } public void SetExtend2 (byte estend2) {this.extend2 = extend2; } public string getSessionId () {return sessionId; } public void setSessionId (String sessionId) {this.sessionId = sessionId; } public int getLength () {Return Comning; } public void setLength (int length) {this.length = length; } public int getCammand () {return Campmand; } public void setCammand (int Campmand) {this.Cammand = Campmand; }} Eu simplesmente processo o uso de string no bytecode. Geralmente, muitos jogos usam séries Probuf para se transformar em binário
Message.java
pacote com.test.netty.message; importar io.netty.buffer.bytebuf; importar io.netty.buffer.unpooled; importar java.io.byteArrayOutputStream; importar java.io.ioException; importar java.io.unsupportEnCodingException; importar com.test.netty.Decoder.Messagedecoder; / ** * message.java * * @author janehuang * @version 1.0 */ public classe mensagem {private cabeçalho; dados de string privada; public cabeçalho getheader () {retornar cabeçalho; } public void setheader (cabeçalho do cabeçalho) {this.header = cabeçalho; } public string getData () {return data; } public void setData (dados da string) {this.data = data; } mensagem pública (cabeçalho do cabeçalho) {this.Header = cabeçalho; } mensagem pública (cabeçalho do cabeçalho, dados da string) {this.header = cabeçalho; this.data = dados; } public byte [] tobyte () {bytearrayoutputStream out = new ByteArrayOutputStream (); out.Write (MessageCoder.package_tag); out.write (header.getEncode ()); out.write (header.getEncrypt ()); out.write (header.geTextend1 ()); out.write (header.geTextend2 ()); byte [] bb = novo byte [32]; byte [] bb2 = header.getSessionId (). getBytes (); for (int i = 0; i <bb2.Length; i ++) {bb [i] = bb2 [i]; } tente {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 BLOCO DE CATAGEM AUTOGERATO E.PRINTSTACKTRACE (); } catch (ioexception e) {// TODO GATO GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); } return } public static byte [] inttobyte (int newint) {byte [] intbyte = new byte [4]; intbyte [3] = (byte) ((newint >> 24) e 0xff); intbyte [2] = (byte) ((newint >> 16) e 0xff); intbyte [1] = (byte) ((newint >> 8) e 0xff); intbyte [0] = (byte) (newint & 0xff); retornar intbyte; } public static int bytestoint (byte [] src, int offset) {int value; value = (int) ((src [deslocamento] e 0xff) | ((src [deslocamento + 1] e 0xff) << 8) | ((src [deslocamento + 2] e 0xff) << 16) | ((src [deslocamento + 3] & 0xff) << 24); valor de retorno; } public static byte [] inttobytes2 (int valor) {byte [] src = new byte [4]; src [0] = (byte) ((valor >> 24) e 0xff); src [1] = (byte) ((valor >> 16) e 0xff); src [2] = (byte) ((valor >> 8) e 0xff); src [3] = (byte) (value & 0xff); retornar src; } public static void main (string [] args) {bytebuf heapbuffer = nãooled.buffer (8); System.out.println (heapbuffer); ByteArrayOutputStream Out = new ByteArrayOutputStream (); tente {out.write (inttobytes2 (1)); } catch (ioexception e) {// TODO GATO GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); } byte [] data = out.tobytearray (); heapbuffer.writebytes (dados); System.out.println (heapbuffer); int a = heapbuffer.readInT (); System.out.println (a); }} Decodificador
MessageCoder.java
pacote com.test.netty.decoder; importar io.netty.buffer.bytebuf; importar io.netty.channel.channelHandlerContext; importar io.netty.handler.codec.bytetomessagedecoder; importar io.netty.handler.codec.CorruptedFrameException; importar java.util.list; importar com.test.netty.message.header; importar com.test.netty.message.message; / *** headerdecoder.java** @author janehuang* @version 1.0*/ public class MessageCoder estende bytetomessagedecoder {/ ** cabeçalho do comprimento do pacote **/ public static final int head_lenght = 45; / ** Cabeçalho do sinalizador **/ public static final byte package_tag = 0x01; @Override Protected void decode (ChannelHandlerContext ctx, buffer bytebuf, list <ject> out) lança exceção {buffer.markreaderindex (); if (buffer.retableBytes () <head_lenght) {lança new corruptdFrameException ("edição do comprimento do pacote"); } tag byte = buffer.readbyte (); if (tag! = package_tag) {lança new corruptdFrameException ("error de sinalizador"); } byte code = buffer.readbyte (); byte Encrypt = buffer.readbyte (); byte estend1 = buffer.readbyte (); byte estend2 = buffer.readbyte (); byte sessionByte [] = novo byte [32]; buffer.readbytes (sessionByte); String sessionId = new String (SessionByte, "UTF-8"); int length = buffer.readInT (); int cammand = buffer.readInT (); Cabeçalho do cabeçalho = novo cabeçalho (tag, codificar, criptografar, estend1, estend2, sessionId, comprimento, Cammand); byte [] dados = novo byte [comprimento]; buffer.readbytes (dados); Mensagem mensagem = nova mensagem (cabeçalho, new string (dados, "utf-8")); out.add (mensagem); }} Codificador
Messagencoder.java
pacote com.test.netty.encoder; importar com.test.netty.Decoder.Messagedecoder; importar com.test.netty.message.header; importar com.test.netty.message.message; importar io.netty.buffer.bytebuf; importar io.netty.channel.channelHandlerContext; importar io.netty.handler.codec.messageTobyteEncoder; / ** * messagencoder.java * * @author janehuang * @version 1.0 */ public class Messagencoder estende MessageTobyteEncoder <ssage> {@Override Protected void Encode (ChannelHandlerContext CTX, Mensagem Msg, ByteBUF out) Exception {headherr = MSG = MSG. out.WriteByte (MessageCoder.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")); }} servidor
TimeServer.java
pacote com.test.netty.server; importar org.springframework.tereotype.component; importar io.netty.bootstrap.ServerbootStrap; importar io.netty.buffer.bytebuf; importar io.netty.buffer.unpooled; importar io.netty.channel.channelfuture; importar io.netty.channel.channelinitializer; importar io.netty.channel.channelOption; importar io.netty.channel.eventloopgroup; importar io.netty.channel.nio.nioeventloopgroup; importar io.netty.channel.socket.socketchannel; importar io.netty.channel.socket.nio.nioserversocketchannel; importar io.netty.handler.codec.linebasedfamedecoder; importar com.test.netty.Decoder.Messagedecoder; importar com.test.netty.encoder.messageEncoder; importar com.test.netty.handler.ServerHandler; / ** * ChatServer.java * * @Author Janehuang * @version 1.0 */ @Component public Class timeServer {private int porta = 88888; public void run () lança interruptedException {eventloopgroup bossGroup = new nioEventloopgroup (); Eventloopgroup WorkerGroup = new NioEventloopgroup (); Bytebuf heapbuffer = nãooled.Buffer (8); heapbuffer.writebytes ("/r" .getBytes ()); tente {serverBootStrap b = new ServerBootStrap (); // (2) B.Group (BossGroup, Workergroup) .Channel (nioserversocketchannel.class) // (3) .ChildHandler (novo canalinitializer <socketchannel> () {// (4) @Override public Void Initchannel (SockAnnel) () Exception (Ch.PiPelin MessageEncoder ()). Addlast ("decodificador", new MessagedEcoder ()). Addfirst (novo LineBasedFamedEcoder (65535)) .Addlast (new ServerHandler ()); // (6) canalfUture f = b.bind (porta) .sync (); // (7) f.Channel (). CloseFuture (). Sync (); } finalmente {workergroup.shutdownGracely (); bossgroup.shutdownGracely (); }} public void Start (int porta) lança interruptedException {this.port = porta; this.run (); }} Processador e distribuir
ServerHandler.java
pacote com.test.netty.handler; importar io.netty.channel.channelHandleratapter; importar io.netty.channel.channelHandlerContext; importar com.test.netty.invote.actionMaputil; importar com.test.netty.message.header; importar com.test.netty.message.message; / ** * * @Author Janehuang * */ public class ServerHandler estende o ChannelHandlerRatapter {@Override public void ChannelActive (ChannelHandlerContext ctx) lança a exceção {String Content = "Recebi a conexão"; Cabeçalho do cabeçalho = novo cabeçalho ((byte) 0, (byte) 1, (byte) 1, (byte) 1, (byte) 0, "713F17CA614361FB257DC6741332CAF2", content.Getbytes ("utf-8"). Mensagem mensagem = nova mensagem (cabeçalho, conteúdo); ctx.WriteAndflush (mensagem); } @Override public void ExceptionCaught (ChannelHandlerContext ctx, causa de arremesso) {caus.printStackTrace (); ctx.close (); } @Override public void ChannelRead (ChannelHandlerContext ctx, objeto msg) lança exceção {message m = (message) msg; // (1)/* Distribuição de solicitação*/ actionMaputil.invote (header.getCammand (), ctx, m); }} Categoria da ferramenta de distribuição
ActionMaputil.Java
pacote com.test.netty.invote; importar java.lang.reflect.method; importar java.util.hashmap; importar java.util.map; public class ActionMaputil {private estático mapa <Inteiro, ação> map = new Hashmap <Integer, Action> (); Public Static Object Invote (chave inteira, objeto ... args) lança exceção {ação action = map.get (chave); if (ação! = null) {método método = action.getMethod (); tente {return method.invoke (action.getObject (), args); } catch (Exceção e) {tiro e; }} retornar nulo; } public static void put (chave inteira, ação de ação) {map.put (chave, ação); }} Objetos criados para distribuição
Ação.java
pacote com.test.netty.invote; importar java.lang.reflect.method; public classe ação {método privado método; objeto privado objeto; Método público getMethod () {Método de retorno; } public void setMethod (método do método) {this.method = método; } public object getObject () {return objeto; } public void setObject (objeto objeto) {this.Object = object; }}Anotações personalizadas, semelhante ao @Controller em Springmvc
NettyController.java
pacote com.test.netty.core; importar java.lang.annotation.Documented; importar java.lang.annotation.ElementType; importar java.lang.annotation.retention; importar java.lang.annotation.retEntionPolicy; importar java.lang.annotation.target; importar org.springframework.tereotype.component; @Retention (retentionpolicy.runtime) @target (elementType.type) @documented @component public @interface nettycontroller {} @ReqestMapping no tipo Spring MVC
ActionMap.java
pacote com.test.netty.core; importar java.lang.annotation.Documented; importar java.lang.annotation.ElementType; importar java.lang.annotation.retention; importar java.lang.annotation.retEntionPolicy; importar java.lang.annotation.target; @Retention (retentionPolicy.Runtime) @target (elementType.method) @Documented public @Interface ActionMap {int key (); } Essas anotações são adicionadas para armazenar esses objetos no contêiner após os grãos de inicialização da mola. Este feijão precisa ser configurado na primavera. O feijão da primavera será chamado após a instanciação.
ActionBeanPostProcessor.java
pacote com.test.netty.core; importar java.lang.reflect.method; importar org.springframework.beans.beansception; importar org.springframework.beans.factory.config.beanPostProcessor; importar com.test.netty.invote.action; importar com.test.netty.invote.actionMaputil; public class ActionBeanPostProcessor implementa o BEANPOSTProcessor {public Object Postprocessbeinitialization (Object Bean, String beanname) lança Beansexception {retornar Bean; } Public Object PostprocessaFterinitialization (objeto Bean, String beanname) lança beansexception {métodos [] métodos = bean.getclass (). getMethods (); para (método Método: Métodos) {ActionMap ActionMap = Method.getAnnotation (ActionMap.class); if (ActionMap! = null) {ação action = new Action (); ação.setMethod (método); ação.setObject (Bean); ActionMaputil.put (ActionMap.Key (), Action); }} retornar Bean; }} Instância do controlador
UserController.java
pacote com.test.netty.Controller; importar io.netty.channel.channelHandlerContext; importar org.springframework.beans.factory.annotation.autowired; importar com.test.model.usermodel; importar com.test.netty.core.actionMap; importar com.test.netty.core.nettyController; importar com.test.netty.message.message; importar com.test.service.userService; @NettyController () public class Useraction {@AUTOWIRED UserService privado UserService; @ActionMap (key = 1) public String Login (ChannelHandlerContext CT, mensagem da mensagem) {UserModel userModel = this.UserService.findbyMasterUserID (1000001); System.out.println (string.format ("Usando o apelido: %s return usermodel.getnickname (); }} Lembre -se de adicionar isso ao arquivo de configuração ApplicationContext.xml
<bean/>
Código de teste
teste de pacote; importar org.springframework.context.ApplicationContext; importar org.springframework.context.support.classPathXMLApplicationContext; importar 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); tente {timeServer.start (8888); } catch (interruptedException e) {// TODO BLOCO DE CATCH AUTOGERATIDO E.PRINTSTACKTRACE (); }}} Teste da chave do interruptor
teste de pacote; importar java.io.ioException; importar java.io.OutputStream; importar java.net.socket; importar java.util.scanner; importar com.test.netty.message.header; importar com.test.netty.message.message; public class ClientTest {public static void main (string [] args) {try {// conecte -se ao soquete do servidor = new Socket ("127.0.0.1", 8888); tente {// dataOutputStream que envia informações para o servidor outputStream out = socket.getOutputStream (); // Decora o fluxo de entrada padrão, usado para inserir a partir do scanner de console scanner = new Scanner (System.in); while (true) {string send = scanner.NextLine (); System.out.println ("Client:" + send); byte [] por = send.getBytes ("utf-8"); Cabeçalho do cabeçalho = novo cabeçalho ((byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, "713F17CA614361FB257DC6741332CAF2", por.Length, 1); Mensagem mensagem = nova mensagem (cabeçalho, send); out.write (message.tobyte ()); out.flush (); // Envie as informações obtidas do console para o servidor // out.Writeutf ("Client:" + send); // Leia as informações do servidor}} finalmente {Socket.Close (); }} catch (ioexception e) {e.printStackTrace (); }}}Resultados dos testes, OK
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.