Spiele haben normalerweise lange Verbindungen und benutzerdefinierte Protokolle und verwenden keine HTTP -Protokolle. Ich werde nicht über Bio, NIO, AIO usw. sprechen und die Informationen selbst überprüfen.
Ich verwende Spring+Netty, um einen einfachen Spielserver einzurichten
Ideen: 1. benutzerdefiniertes Protokoll und Protokollpaket; 2. Spring+Netty Integration; 3.Half-Packet-Sticking-Verarbeitung, Herzschlagmechanismus usw.; 4. Erhöhung der Verteilung (derzeit mache ich Singleton -Modus)
Als nächstes steht die Struktur wie folgt vor
Passen Sie zunächst den Paketheader an
Header.java
Paket com.test.netty.message; / ** * Header.java * benutzerdefinierter Protokollheader * @Author Janehuang * @Version 1.0 */ Public Class Header {private byte tag; /* Codieren*/ private byte codieren; /* Verschlüsselung*/ Private Byte Verschlüsseln; /*Andere Felder*/ privates Byte erweitert1; /*Andere 2*/ private Byte Extend2; /*SessionID*/ private String SessionID; /*Paketlänge*/ private int länge = 1024; /*Befehl*/ privat int cammand; public header () {} public header (String SessionID) {this.code = 0; this.encrypt = 0; this.SessionId = sessionId; } public Header (Byte -Tag, Byte codieren, Byte Encrypt, Byte Encrypt, Byte Extend1, Byte Extend2, String SessionID, int Länge, int Bullet) {this.tag = Tag; this.encode = codieren; this.encrypt = verschlüsseln; this.extend1 = extend1; this.extend2 = extend2; this.SessionId = sessionId; diese.länge = Länge; this.cammand = cammand; } @Override public String toString () {return "header [tag =" + tag + "enCode =" + codod + ", Encrypt =" + Encrypt + ", extend1 =" + extend1 + ", extend2 =" + extend2 + ", sessionID =" + sessionID + ", Länge =" + Länge + ". } public byte gettag () {return tag; } public void settag (byte tag) {this.tag = tag; } public byte getcode () {return codes; } public void setEneCode (byte codieren) {this.encode = codieren; } 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 () {Rückgabelänge; } public void setLength (int länge) {this.length = Länge; } public int getCammand () {return campmand; } public void setCammand (int campmand) {this.cammand = campmand; }} Ich verarbeite einfach die Verwendung von String zu Bytecode. Im Allgemeinen verwenden viele Spiele die Probuf -Serie, um sich in binäre zu verwandeln
Message.java
Paket com.test.netty.message; import IO.NETTY.Buffer.Bytebuf; importieren io.netty.buffer.unpooled; importieren java.io.BytearrayoutputStream; importieren java.io.ioException; importieren java.io.unsupportedenCodingException; import com.test.netty.decoder.messageCoder; / ** * message.java * * @Author Janehuang * @Version 1.0 */ public class meldung {private Header Header; private String -Daten; öffentlicher Header Getheader () {Return Header; } public void Setheader (Header Header) {this.header = header; } public String getData () {returndaten; } public void setData (String -Daten) {this.data = data; } öffentliche Nachricht (Header Header) {this.Header = Header; } öffentliche Nachricht (Header Header, String -Daten) {this.header = header; this.data = Daten; } public byte [] tobyte () {bytearrayoutputStream out = new bytearrayoutputStream (); out.write (MessageDeCoder.package_tag); out.write (Header.getEcode ()); out.write (Header.getEcrypt ()); out.write (Header.getExtend1 ()); out.write (Header.getExtend2 ()); Byte [] BB = neues Byte [32]; byte [] bb2 = header.getSessionId (). getBytes (); für (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 (unportedenCodingException e) {// todo automatisch generierter Catch-Block e.printstacktrace (); } catch (ioException e) {// Todo automatisch generierter 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); intbyte zurückgeben; } 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)); Rückgabewert; } public static byte [] inttobytes2 (int value) {byte [] src = new Byte [4]; src [0] = (byte) ((Wert >> 24) & 0xff); src [1] = (byte) ((Wert >> 16) & 0xff); src [2] = (byte) ((Wert >> 8) & 0xff); src [3] = (byte) (Wert & 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 automatisch generierter Catch-Block e.printstacktrace (); } byte [] data = out.tobytearray (); heapbuffer.writebytes (Daten); System.out.println (Heapbuffer); int a = heapbuffer.readint (); System.out.println (a); }} Decoder
MessageDeCoder.java
Paket com.test.netty.decoder; import IO.NETTY.Buffer.Bytebuf; Import IO.NETTY.Channel.ChannelHandlerContext; import IO.NETTY.Handler.Codec.ByTETomessAnedEcoder; importieren io.netty.handler.codec.CorruptedFrameException; importieren 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 erweitert BytETomessAnedeCoder {/ ** Paketlänge Header **/ public static endgültig int int head_lenght = 45; / ** Flag -Header **/ public static final byte package_tag = 0x01; @Override Protected void Decodes (ChannelHandlerContext CTX, Bytebuf Buffer, List <Ontoces> out) löst eine Ausnahme aus {buffer.markreaderIndex (); if (buffer.readableBytes () <head_lenght) {Neue beschädigte FrameException ("Paketlängenausgabe"); } byte tag = buffer.readbyte (); if (Tag! } byte enCode = buffer.readbyte (); Byte Encrypt = buffer.readbyte (); byte extend1 = buffer.readbyte (); byte extend2 = buffer.readbyte (); Byte SessionByte [] = neues Byte [32]; Buffer.ReadBytes (SessionByte); String SessionID = new String (SessionByte, "UTF-8"); int länge = buffer.readint (); int cammand = buffer.readint (); Header Header = neuer Header (Tag, codieren, verschlüsseln, extend1, extend2, sessionId, länge, cammand); byte [] data = new Byte [Länge]; buffer.readBytes (Daten); Message Message = New Message (Header, neue String (Daten, "UTF-8")); out.Add (Nachricht); }} Encoder
MessageCoder.java
Paket 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.ChannelHandlerContext; Import IO.NETTY.Handler.Codec.MessAgetobyteenCoder; / ** * messageCoder.java * * @Author Janehuang * @Version 1.0 */ public class messageCoder erweitert MessersagetobyteEnencoder <message> {@Override Protected void codieren (ChannelHandlerContext CTX, Message msg, byTebuf), aus der Ausnahme {Header Header = msg.g.gethe Leder (). out.writebyte (MessageDeCoder.package_Tag); out.writebyte (Header.getEcode ()); out.writebyte (Header.getEcrypt ()); 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
Paket com.test.netty.server; import org.springframework.stereotype.comPonent; importieren io.netty.bootstrap.serverbootstrap; import IO.NETTY.Buffer.Bytebuf; importieren io.netty.buffer.unpooled; Import IO.NETTY.Channel.Channelfuture; Import IO.NETTY.Channel.Channelinitializer; importieren io.netty.channel.channeloption; Import io.netty.channel.eventloopGroup; import IO.NETTY.Channel.nio.nioEventLoopGroup; import io.netty.channel.socket.socketchannel; importieren io.netty.channel.socket.nio.nioServerSocketchannel; import IO.NETTY.Handler.Codec.lineBasedFramedeCoder; import com.test.netty.decoder.messageCoder; import com.test.netty.encoder.messageCoder; import com.test.netty.handler.serverhandler; / ** * chatserver.java * * @author janehuang * @version 1.0 */ @Component öffentlicher Klasse Timeserver {private int port = 88888; public void run () löst InterruptedException aus {EventLoopGroup BossGroup = new NioeventLoopGroup (); EventLoopGroup WorkerGroup = new NioeVentLoopGroup (); Bytebuf heapbuffer = unpooled.buffer (8); heapbuffer.writebytes ("/r" .getBytes ()); versuche {serverbootstrap b = new Serverbootstrap (); // (2) B.Group (Bossgroup, WorkerGroup) .Channel (NioServerSocketchannel.Class) // (3) .ChildHandler (neuer ChannelInitializer <Socketchannel> () {// (4) @Override public void Initchannel (Socketchannel CH) Ausnahme {ch.pipeline (). MessageCoder ()). AddLast ("Decoder", New MindaderdeCoder ()). Addfirst (new LineBasedFramedEcoder (65535)) .AddLast (New ServerHandler ()); // (6) ChannelFuture f = B.Bind (Port) .Sync (); // (7) f.channel (). Closefuture (). Sync (); } endlich {WorkerGroup.ShutdownGracefully (); bossGroup.Shutdowngraceduty (); }} public void start (int port) löscht InterruptedException {this.port = port; this.run (); }} Prozessor und verteilen
Serverhandler.java
Paket 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 erweitert ChannelHandlerAdapter {@Override public void ChannelActive (ChannelHandlerContext CTX) löst eine Ausnahme aus {String content = "Ich habe die Verbindung erhalten"; Header Header = neuer Header ((Byte) 0, (Byte) 1, (Byte) 1, (Byte) 1, (Byte) 0, "713F17CA614361FB257DC6741332CAF2", Content.GetBytes ("Utf-8"). Länge, 1); Message Message = New Message (Header, Inhalt); ctx.writeAndFlush (Nachricht); } @Override public void exceptioncaught (ChannelHandlerContext CTX, Throwable Cause) {cause.printstacktrace (); ctx.close (); } @Override public void ChannelRead (ChannelHandlerContext CTX, Object MSG) löst die Ausnahme aus {message m = (message) msg; // (1)/* Anforderungsverteilung*/ actionMaputil.invote (Header.getCAMMAND (), CTX, M); }} Distribution Tool Kategorie
ActionMaputil.java
Paket com.test.netty.invote; import Java.lang.reflect.Method; import Java.util.hashMap; import Java.util.map; public Class actionMaputil {private statische Karte <Integer, Aktion> map = new Hashmap <Integer, Aktion> (); öffentliches statisches Objekt invote (Ganzzahlschlüssel, Objekt ... args) löst eine Ausnahme aus {action action = map.get (Schlüssel); if (action! = null) {method method = action.getMethod (); try {return methode.invoke (action.getObject (), args); } catch (Ausnahme e) {throw e; }} return null; } public static void put (Ganzzahlschlüssel, Aktion Aktion) {map.put (Schlüssel, Aktion); }} Objekte, die für die Verteilung erstellt wurden
Action.java
Paket com.test.netty.invote; import Java.lang.reflect.Method; öffentliche Sammelklage {private Methode; privates Objekt; öffentliche Methode getMethod () {Rückgabemethode; } public void setMethod (Methode Methode) {this.method = Methode; } public Object getObject () {Rückgabeobjekt; } public void setObject (Objektobjekt) {this.Object = Object; }}Benutzerdefinierte Anmerkungen, ähnlich wie @Controller in SpringMVC
NettyController.java
Paket com.test.netty.core; import Java.lang.Annotation.Documented; Import Java.lang.Annotation.Elementtype; importieren java.lang.annotation.retention; Import Java.lang.annotation.RetentionPolicy; importieren java.lang.annotation.target; import org.springframework.stereotype.comPonent; @Retention (retentionPolicy.runtime) @target (elementtype.type) @documented @Component public @Interface NettyController {} @Reqestmapping im Typ Spring MVC
ActionMap.java
Paket com.test.netty.core; import Java.lang.Annotation.Documented; Import Java.lang.Annotation.Elementtype; importieren java.lang.annotation.retention; Import Java.lang.annotation.RetentionPolicy; importieren java.lang.annotation.target; @Retention (retentionPolicy.runtime) @Target (elementtype.method) @Documented public @Interface actionMap {int key (); } Diese Annotationen werden nach der Federinitialisierungsbohnen hinzugefügt, um diese Objekte im Container zu speichern. Diese Bohne muss im Frühjahr konfiguriert werden. Die Frühlingsbohne wird nach der Instanziierung gerufen.
ActionBeanPostProcessor.java
Paket 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; öffentliche SammelklagenbeanPostPostProcessor implementiert BeanPostProcessor {public Object postProcessBeforeInitialisierung (Objektbean, String -Beanname) löscht Beansexception {return bean; } öffentliches Objekt postprozessafteritialisierung (Objektbean, String beanname) löscht Beansexception {method [] methoden = bean.getClass (). getMethods (); für (Methode Methode: Methoden) {actionMap actionMap = methode.getannotation (actionMap.class); if (actionMap! = null) {action action = new Action (); Action.SetMethod (Methode); Action.SetObject (Bean); ActionMapUtil.put (actionMap.key (), action); }} return bean; }} Controller -Instanz
UserController.java
Paket com.test.netty.controller; Import IO.NETTY.Channel.ChannelHandlerContext; import org.springframework.beans.factory.annotation.autowired; import com.test.model.usmodel; import com.test.netty.core.actionMap; import com.test.netty.core.nettycontroller; import com.test.netty.message.message; import com.test.service.userservice; @NettyController () öffentliche Klasse UserAction {@autowired Private UserService UserService; @ActionMap (KEY = 1) public String login (ChannelHandlerContext CT, Nachrichtennachricht) {UsModel UsModel = this.userService.findByMasterUserid (1000001); System.out.println (String.Format ("Verwenden von Spitzname: %s; Passwort %d; Transmitter -Inhalt %S", UserModel.getNickname (), UsModel.getId (), Message.getData ())); return usermodel.getNickname (); }} Denken Sie daran, dies der Konfigurationsdatei applicationContext.xml hinzuzufügen
<Bean/>
Testcode
Pakettest; 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 classPlaPlApplicationContext ("applicationContext.xml"); Timeserver Timeserver = ac.getbean (Timeserver.class); try {Timeserver.start (8888); } catch (InterruptedException e) {// Todo automatisch generierter Catch-Block e.printstacktrace (); }}} Testschalterende
Pakettest; importieren java.io.ioException; importieren java.io.outputstream; importieren 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 {// Verbindung zum Server -Socket -Socket eine Verbindung zwischen Socket = neuer Socket ("127.0.1", 8888); Versuchen Sie {// DataOutputStream, der Informationen an den Server -Ausgangsstream out = socket.getOutputStream () sendet; // Dekorieren Sie den Standardeingangsstrom, der zum Eingang vom Konsolenscanner -Scanner = neuer Scanner (System.in) verwendet wird. while (true) {string send = scanner.nextline (); System.out.println ("Client:" + send); byte [] von = send.getBytes ("utf-8"); Header Header = neuer Header ((Byte) 1, (Byte) 1, (Byte) 1, (Byte) 1, (Byte) 1, (Byte) 1, "713F17CA614361FB257DC6741332CAF2", durch. Länge, 1); Message Message = New Message (Header, Senden); out.write (message.tobyte ()); out.flush (); // Senden Sie die von der Konsole erhaltenen Informationen an den Server // out.writeUtf ("Client:" + send); // Lesen Sie die Informationen vom Server}} endlich {Socket.CLOSE (); }} catch (ioException e) {e.printstacktrace (); }}}Testergebnisse, OK
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.