Los juegos generalmente tienen conexiones largas y protocolos personalizados, y no usan protocolos HTTP. No hablaré de Bio, Nio, AIO, etc., consulte la información por sí mismo.
Estoy usando Spring+Netty para configurar un servidor de juego simple
Ideas: 1. Paquete de protocolo y protocolo personalizado; 2.spring+integración Netty; 3. Procesamiento de adhesivo de la parada del medio, mecanismo de latidos, etc.; 4. Distribución de requisitos (actualmente, estoy haciendo modo singleton)
Lo siguiente es para la prueba, la estructura es la siguiente.
Primero, personalice el encabezado del paquete
Encabezado.java
paquete com.test.netty.message; / ** * header.java * encabezado de protocolo personalizado * @author janehuang * @version 1.0 */ public class Header {private byte tag; /* Codificación*/ private byte code; /* Cifrado*/ private byte encrypt; /*Otros campos*/ byte privado extend1; /*Otro 2*/ byte privado extender2; /*SessionID*/ private String sessionId; /*Longitud del paquete*/ private int longitud = 1024; /*Comando*/ private int cammand; Encabezado public () {} public Public Header (String SessionId) {this.encode = 0; this.Enrypt = 0; this.sessionID = sessionId; } encabezado público (etiqueta de byte, byte code, byte encrypt, byte encrypt, byte extend1, byte extend2, string sessionId, int long, int bullet) {this.tag = tag; this.encode = encode; this.Encrypt = CiCrypt; this.extend1 = extend1; this.extend2 = extend2; this.sessionID = sessionId; this.length = longitud; this.cammand = Cammand; } @Override public string toString () {return "Header [tag =" + tag + "codeDe =" + codeD + ", encrypt =" + encrypt + ", extend1 =" + extend1 + ", extend2 =" + extend2 + ", sessionId =" + sessionId + ", longitud =" + longitud + "Cammand =" + cammand + "]"; } public byte gettag () {return etiqueta; } public void settag (etiqueta byte) {this.tag = tag; } public byte getEncode () {return codde; } public void setEncode (byte encode) {this.encode = codede; } public byte getenCrypt () {return cifrypt; } 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 longitud; } public void setLength (int long) {this.length = longitud; } public int getCammand () {return campmand; } public void setCammand (int campmand) {this.cammand = campmand; }} Simplemente proceso el uso de String a Bytecode. En general, muchos juegos usan series probuf para transformarse en binario
Message.java
paquete 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.messageCoder; / ** * Message.java * * @author Janehuang * @version 1.0 */ public class Message {Private Header Header; datos de cadena privada; Encabezado público GetHeader () {encabezado de retorno; } public void setheader (encabezado del encabezado) {this.header = encabezado; } public string getData () {return data; } public void setData (string data) {this.data = data; } Mensaje público (encabezado) {this.header = header; } mensaje público (encabezado de encabezado, datos de cadena) {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 = nuevo 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 Generado Bloque E.PrintStackTrace (); } Catch (ioException e) {// tODO Auto Generated BLOCK E.PrintStackTRace (); } return out.tobytearray (); } public static byte [] inttobyte (int NewInt) {byte [] intbyte = new byte [4]; intbyte [3] = (byte) ((newint >> 24) y 0xff); intbyte [2] = (byte) ((newint >> 16) y 0xff); intbyte [1] = (byte) ((newint >> 8) y 0xff); intbyte [0] = (byte) (newInt & 0xff); devolver intbyte; } public static int byTestoint (byte [] src, int offset) {int value; valor = (int) ((src [offset] & 0xff) | ((src [offset + 1] & 0xff) << 8) | ((src [offset + 2] & 0xff) << 16) | ((src [offset + 3] & 0xff) << 24)); valor de retorno; } public static byte [] inttobytes2 (int value) {byte [] src = new byte [4]; src [0] = (byte) ((valor >> 24) y 0xff); src [1] = (byte) ((valor >> 16) y 0xff); src [2] = (byte) ((valor >> 8) y 0xff); src [3] = (byte) (valor y 0xff); devolver src; } public static void main (string [] args) {bytebuf heapbuffer = sinoled.buffer (8); System.out.println (HeapBuffer); BytearRayOutputStream out = new byteArRaReOutputStream (); intente {out.write (inttobytes2 (1)); } Catch (ioException e) {// tODO Auto Generated BLOCK E.PrintStackTRace (); } byte [] data = out.tobytearray (); Heapbuffer.Writytes (datos); System.out.println (HeapBuffer); int a = HeapBuffer.ReadInt (); System.out.println (a); }} Descifrador
MessageCoder.java
paquete com.test.netty.decoder; import io.netty.buffer.bytebuf; import io.netty.channel.channelhanderContext; import io.netty.handler.codec.bytetomessageCoder; 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 extiende bytetomessageCoder {/ ** encabezado de longitud de paquete **/ public static final int head_lenght = 45; / ** encabezado de bandera **/ public static final byte paquete_tag = 0x01; @Override Protected void Decode (ChannelHandLerContext CTX, ByteBuf Buffer, List <S Object> out) lanza la excepción {buffer.markreaderIndex (); if (buffer.readableBytes () <Head_lenght) {Throw New CorruptedFrameException ("Problema de longitud del paquete"); } byte tag = buffer.readbyte (); if (tag! = paquete_tag) {tire nuevo corruptedFrameException ("error de bandera"); } byte encode = buffer.readbyte (); byte encrypt = buffer.readbyte (); byte extend1 = buffer.readbyte (); byte extend2 = buffer.readbyte (); byte sessionByte [] = nuevo byte [32]; buffer.readbytes (sessionbyte); String sessionID = new String (sessionByte, "UTF-8"); int longitud = buffer.readInt (); int cammand = buffer.readInt (); Encabezado encabezado = nuevo encabezado (etiqueta, codificar, encrypt, extend1, extend2, sessionID, longitud, cammand); byte [] data = new Byte [longitud]; buffer.readbytes (datos); Mensaje mensaje = nuevo mensaje (encabezado, nueva cadena (datos, "UTF-8")); out.Add (mensaje); }} Codificador
MessageEncoder.java
paquete com.test.netty.encoder; import com.test.netty.decoder.messageCoder; import com.test.netty.message.header; import com.test.netty.message.message; import io.netty.buffer.bytebuf; import io.netty.channel.channelhanderContext; import io.netty.handler.codec.messagetobyteencoder; / ** * MessageEncoder.java * * @author Janehuang * @version 1.0 */ public class MessageEncoder extiende MessageToByEncoder <Message> {@Override Protected void encode (channelHandexttext ctx, Message msg, byteBufUf out) Exception {header head = msg.getheader ();; out.writyte (MessageCoder.package_tag); out.writyte (header.getEncode ()); out.writyte (header.getEntrypt ()); out.writyte (header.getExtend1 ()); out.writyte (header.getExtend2 ()); out.writytes (Header.getSessionId (). GetBytes ()); out.WriteInt (Header.getLength ()); out.WriteInt (Header.getCammand ()); out.writytes (msg.getData (). getBytes ("utf-8")); }} servidor
Timeserver.java
paquete com.test.netty.server; importar 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.lineBasedframedCoder; import com.test.netty.decoder.messageCoder; 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 () lanza interruptedException {eventloopGroup bossGroup = new NioeventLoopGroup (); EventLoopGroup WorkerGroup = new NioEventLoopGroup (); Bytebuf HeapBuffer = Unpoled.Buffer (8); HeapBuffer.WrityBeTes ("/r" .getBytes ()); intente {ServerBootstrap b = new ServerBootstrap (); // (2) B.Group (BossGroup, WorkerGroup) .Channel (nioserversocketchannel.class) // (3) .childHandler (nuevo canalinitializer <Socketchannel> () {// (4) @Override public void initchannel (Socketchannel ch) arroja excepción {CH.PIPEline (). AddLast ("," nuevo "," nuevo "," nuevo "," nuevo "," nuevo "," nuevo "," nuevo "," CHODLAST ("", "", ",", ",", ",", ",", ",", ",", "", ",", ",", ",", ",", ",", ",", ",", ",", ",", ",", ",", "", ",", ",", ",", ",", ",", ",", ",". MessageEncoder ()). AddLast ("Decoder", New MessageCoder ()). AddFirst (new LineBasedFramedEcoder (65535)) .addlast (newerverHandler ()); // (6) Channelfuture f = B.Bind (puerto) .sync (); // (7) F.channel (). CloseFuture (). Sync (); } Finalmente {WorkerGroup.shutdownGRacefly (); bossgroup.shutdowngracefly (); }} public void start (int puerto) lanza interruptedException {this.port = port; this.run (); }} Procesador y distribuir
ServerHandler.java
paquete com.test.netty.handler; import io.netty.channel.channelhandleradapter; import io.netty.channel.channelhanderContext; import com.test.netty.invote.actionmaputil; import com.test.netty.message.header; import com.test.netty.message.message; / ** * * @author Janehuang * */ public class ServerHandler extiende ChannelHandlerAdapter {@Override public void ChannelActive (ChannelHandlerContext CTX) lanza excepción {String Content = "Recibí la conexión"; Encabezado encabezado = nuevo encabezado ((byte) 0, (byte) 1, (byte) 1, (byte) 1, (byte) 0, "713f17ca614361fb257dc6741332caf2", content.getBytes ("utf-8"). Longitud, 1); Mensaje mensaje = nuevo mensaje (encabezado, contenido); ctx.writeandflush (mensaje); } @Override public void ExceptionCaught (ChannelHandLerContext CTX, Throwable Causa) {causa.printstackTrace (); ctx.close (); } @Override public void canalread (ChannelHandLerContext CTX, Object Msg) lanza la excepción {mensaje m = (mensaje) msg; // (1)/* Distribución de solicitud*/ ActionMaputil.Invote (Header.getCammand (), CTX, M); }} Categoría de herramienta de distribución
ActionMaputil.java
paquete com.test.netty.invote; import java.lang.reflect.method; import java.util.hashmap; import java.util.map; Public Class ActionMaputil {Mapa estático privado <Integer, Action> MAP = New HashMap <Integer, Action> (); Public Static Object Invote (Key Integer, Object ... Args) lanza la excepción {Action Action = Map.get (Key); if (action! = null) {método método = acción.getMethod (); intente {return Method.invoke (Action.getObject (), args); } Catch (Exception e) {Throw E; }} return null; } public static void put (clave entera, acción de acción) {map.put (clave, acción); }} Objetos creados para la distribución
Action.java
paquete com.test.netty.invote; import java.lang.reflect.method; acción de clase pública {método privado método; objeto privado objeto; Método público getMethod () {Método de retorno; } public void setMethod (método método) {this.method = método; } objeto público getObject () {return Object; } public void setObject (objeto objeto) {this.object = object; }}Anotaciones personalizadas, similares a @Controller en SpringMVC
NettyController.java
paquete 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; importar org.springframework.stereotype.component; @Retention (retenciónPolicy.Runtime) @Target (elementType.type) @documented @Component public @Interface NettyController {} @ReqestMapping en type spring mvc
ActionMap.java
paquete 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 (retenciónPolicy.Runtime) @Target (elementType.method) @Documented public @Interface ActionMap {int Key (); } Estas anotaciones se agregan para almacenar estos objetos en el contenedor después de los frijoles de inicialización de primavera. Este frijol debe configurarse en Spring. Se llamará al frijol de primavera después de la instanciación.
ActionBeanPostProcessor.java
paquete com.test.netty.core; import java.lang.reflect.method; importar org.springframework.beans.beansexception; importar org.springframework.beans.factory.config.beanpostprocessor; import com.test.netty.invote.action; import com.test.netty.invote.actionmaputil; Public Clase ActionBeanPostProcessor implementa BeanPostProcessor {Public Object PostProcessBeForeinitialization (Object Bean, String BeanName) arroja BeanSexception {return Bean; } Public Object PostProcessAfterInitialization (Object Bean, String BeanName) lanza Beansexception {método [] métodos = bean.getClass (). getMethods (); para (método método: métodos) {ActionMap ActionMap = Method.GetAnnotation (ActionMap.Class); if (ActionMap! = NULL) {Action Action = New Action (); Action.SetMethod (método); Action.SetObject (frijol); ActionMaputil.put (ActionMap.Key (), Action); }} return bean; }} Instancia del controlador
UserController.java
paquete com.test.netty.controller; import io.netty.channel.channelhanderContext; importar 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 classationAction {@aUtoWired private UserService UserService; @ActionMap (Key = 1) Public String Login (ChannelHandLerContext CT, Mensaje Mensaje) {usermodel usermodel = this.userservice.findbymasterUserID (1000001); System.out.println (string.format ("Usando el apodo: %s; contraseña %d; contenido del transmisor %s", usermodel.getNickName (), usermodel.getID (), mensaje.getData ())); return usermodel.getNickName (); }} Recuerde agregar esto al archivo de configuración ApplicationContext.xml
<frijoles/>
Código de prueba
prueba de paquete; importar org.springframework.context.applicationContext; importar org.springframework.context.support.classpathxmlaPplicationContext; import com.test.netty.server.timeserver; Public Class Test {public static void main (String [] args) {applicationContext ac = new classpathxmlaPlaPlicationContext ("applicationContext.xml"); Timeserver Timeserver = ac.getBean (Timeserver.class); intente {Timeserver.Start (8888); } Catch (InterruptedException e) {// TODO Auto Generado Bloque E.PrintStackTrace (); }}} Extremo del interruptor de prueba
prueba de paquete; 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 {// conectarse al socket de servidor = nuevo socket ("127.0.0.1", 8888); Pruebe {// DataOutputStream que envía información al servidor OutputStream Out = Socket.getOutputStream (); // Decorar la secuencia de entrada estándar, utilizada para ingresar desde el escáner del escáner de consola = nuevo escáner (System.in); while (true) {string send = scanner.nextline (); System.out.println ("Cliente:" + Enviar); byte [] by = send.getBytes ("utf-8"); Encabezado encabezado = encabezado nuevo ((byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, "713f17ca614361fb257dc67413332caf2", por.length, 1); Mensaje mensaje = nuevo mensaje (encabezado, envío); out.write (message.tobyte ()); out.flush (); // Enviar la información obtenida de la consola al servidor // out.writeutf ("Cliente:" + Enviar); // leer la información del servidor}} Finalmente {Socket.close (); }} catch (ioException e) {E.PrintStackTrace (); }}}Resultados de la prueba, OK
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.