게임에는 일반적으로 긴 연결과 사용자 정의 프로토콜이 있으며 HTTP 프로토콜을 사용하지 않습니다. 나는 Bio, Nio, Aio 등에 대해 이야기하지 않을 것입니다. 직접 정보를 확인하십시오.
Spring+Netty를 사용하여 간단한 게임 서버를 설정하고 있습니다.
아이디어 : 1. 사용자 정의 프로토콜 및 프로토콜 패키지; 2. 스프링+네티 통합; 3. HELF-PACKET 고정 처리, 심장 박동 메커니즘 등; 4. 요청 분포 (현재, 나는 싱글 톤 모드를하고 있습니다)
다음은 테스트를위한 것입니다. 구조는 다음과 같습니다
먼저 패키지 헤더를 사용자 정의하십시오
header.java
패키지 com.test.netty.message; / ** * header.java * 사용자 정의 프로토콜 헤더 * @Author janehuang * @version 1.0 */ public class header {private byte tag; /* 인코딩*/ 개인 바이트 인코딩; /* 암호화*/ 개인 바이트 암호화; /*기타 필드*/ 개인 바이트 확장 1; /*기타 2*/ 개인 바이트 확장 2; /*sessionId*/ private String sessionId; /*패키지 길이*/ 개인 int 길이 = 1024; /*명령*/ 개인 int cammand; public Header () {} public Header (String SessionId) {this.encode = 0; this.encrypt = 0; this.sessionId = sessionId; } public 헤더 (바이트 태그, 바이트 인코딩, 바이트 암호화, 바이트 암호, 바이트 확장 1, 바이트 확장 2, 문자열 sessionID, int bullet) {this.tag = tag; this.encode = encode; this.encrypt = 암호화; this.extend1 = extend1; this.extend2 = extend2; this.sessionId = sessionId; 길이 = 길이; this.cammand = cammand; } @override public String toString () {return "return"return "return"return "agg =" + tag + "encode =" + encode + ", Encrypt =" + 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 alcrypt; } 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 () {리턴 길이; } public void setLength (int length) {this.length = 길이; } public int getCammand () {return campmand; } public void setcammand (int campmand) {this.cammand = campmand; }} 나는 단순히 문자열 사용을 바이트 코드로 처리합니다. 일반적으로 많은 게임은 Probuf 시리즈를 사용하여 이진으로 변환합니다.
Message.java
패키지 com.test.netty.message; import io.netty.buffer.bytebuf; import io.netty.buffer.unpooled; import java.io.BytearRayoutputStream; import java.io.ioexception; java.io.unsupportedencodingException 가져 오기; com.test.netty.decoder.messagedeCoder 가져 오기; / ** * message.java * * @author janehuang * @version 1.0 */ public 클래스 메시지 {개인 헤더 헤더; 개인 문자열 데이터; Public Header Getheader () {리턴 헤더; } public void setheader (헤더 헤더) {this.header = 헤더; } public String getData () {return data; } public void setData (문자열 데이터) {this.data = data; } 공개 메시지 (헤더 헤더) {this.header = 헤더; } 공개 메시지 (헤더 헤더, 문자열 데이터) {this.header = 헤더; this.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 ()); 바이트 [] bb = 새로운 바이트 [32]; 바이트 [] bb2 = header.getSessionId (). getBytes (); for (int i = 0; i <bb2.length; i ++) {bb [i] = bb2 [i]; } try {out.write (bb); 바이트 [] 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 자동 생성 캐치 블록 e.printstacktrace (); } catch (ioexception e) {// todo 자동 생성 캐치 블록 e.printstacktrace (); } return.tobytearRay (); } public static bd intbyte [3] = (byte) ((newint >> 24) & 0xff); Intbyte [2] = (byte) ((newint >> 16) & 0xff); intbyte [1] = (byte) ((newint >> 8) & 0xff); Intbyte [0] = (바이트) (NewInt & 0xff); 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); 반환 값; } public static byte [] inttobytes2 (int value) {byte [] src = new Byte [4]; src [0] = (byte) ((값 >> 24) & 0xff); src [1] = (byte) ((값 >> 16) & 0xff); src [2] = (byte) ((값 >> 8) & 0xff); src [3] = (바이트) (값 & 0xff); 반환 SRC; } public static void main (string [] args) {bytebuf heapbuffer = Unpooled.buffer (8); System.out.println (Heapbuffer); bytearrayoutputStream out = 새로운 BytearRayoutputStream (); try {out.write (inttobytes2 (1)); } catch (ioexception e) {// todo 자동 생성 캐치 블록 e.printstacktrace (); } byte [] data = out.tobytearRay (); heapbuffer.writeBytes (데이터); System.out.println (Heapbuffer); int a = heapbuffer.readint (); System.out.println (a); }} 디코더
MessageDecoder.java
package com.test.netty.decoder; import io.netty.buffer.bytebuf; import io.netty.channel.channel HandhandlerContext; import io.netty.handler.codec.ByTeTomessagedEcoder; import io.netty.handler.codec.corruptedframeException; Java.util.list 가져 오기; com.test.netty.message.header import; com.test.netty.message.message 가져 오기; / *** headerdecoder.java** @author janehuang* @version 1.0*/ public class messageDecoder 확장 BYTETOMESSEDAGEDECODER {/ ** 패키지 길이 헤더 **/ public static final head_lenght = 45; / ** 플래그 헤드러 **/ 공개 정적 최종 바이트 패키지 _tag = 0x01; @override protected void decode (channelHandlerContext ctx, bytebuf buffer, list <bood> out) 예외 {buffer.markreaderIndex (); if (buffer.readableBytes () <head_lenght) {새로 새로부터 손상된 frameException ( "패키지 길이 문제"); } byte tag = buffer.readByte (); if (tag! = package_tag) {throw new nehruptedframeException ( "플래그 오류"); } byte encode = buffer.readByte (); 바이트 암호화 = buffer.readByte (); 바이트 Extend1 = buffer.readByte (); 바이트 Extend2 = buffer.readByte (); 바이트 세션 비트 [] = 새로운 바이트 [32]; Buffer.readBytes (SessionByte); String SessionId = new String (SessionByte, "UTF-8"); int length = buffer.readint (); int cammand = buffer.readint (); 헤더 헤더 = 새 헤더 (Tag, Encode, Encrypt, Extend1, Extend2, SessionID, Length, Cammand); 바이트 [] data = 새로운 바이트 [길이]; Buffer.readBytes (데이터); 메시지 메시지 = 새 메시지 (헤더, 새 문자열 (데이터, "UTF-8")); out.add (메시지); }} 인코더
MessageEncoder.java
패키지 com.test.netty.encoder; com.test.netty.decoder.messagedeCoder 가져 오기; com.test.netty.message.header import; com.test.netty.message.message 가져 오기; import io.netty.buffer.bytebuf; import io.netty.channel.channel HandhandlerContext; import io.netty.handler.codec.messagetobyteencoder; / ** * MessageEncoder.java * * @author janehuang * @version 1.0 */ public class messageencoder는 messagetobyteencoder <message> {@override protected void encode (channelHandlerContext ctx, messg, bytebuf out) 예외 {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
패키지 com.test.netty.server; org.springframework.stereotyp.component import; 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; 가져 오기 io.netty.channel.channelOption; import io.netty.channel.eventLoopGroup; import io.netty.channel.nio.nioeventLoopGroup; 가져 오기 io.netty.channel.socket.socketchannel; 가져 오기 io.netty.channel.socket.nio.nioserversocketchannel; import io.netty.handler.codec.linebasedframedecoder; com.test.netty.decoder.messagedeCoder 가져 오기; com.test.netty.encoder.messageEncoder 가져 오기; com.test.netty.handler.serverhandler import; / ** * chatserver.java * * @author janehuang * @version 1.0 */ @component public class timeserver {private int port = 88888; public void run ()은 InterruptedException {eventLoopgroup bossgroup = new nioeventLoopgroup (); EventLoopGroup WorkerGroup = NEW NIOEVENTLOOPGROUP (); Bytebuf heapbuffer = Unp -설치 .buffer (8); heapbuffer.writeBytes ( "/r".getBytes ()); {serverbootstrap b = new ServerBootstrap (); // (2) B.group (BossGroup, WorkerGroup) .Channel (nioserversocketchanchann.class) // (3) .ChildHandler (New ChannelInitializer <socketchannel> () {// (4) @override public void initchannel (Socketchannel Ch) 예외 {ch.pipeline (). MessageOder ()). addlast ( "Decoder", New MessageDecoder ()). AddFirst (New LineBasedFramedeCoder (65535)) .AddLast (New ServerHandler ()). // (6) channelfuture f = b.bind (port) .sync (); // (7) F.Channel (). CloseFuture (). sync (); } 마침내 {workerGroup.shutdownGracely (); bossgroup.shutdowngracely (); }} public void start (int port) 던지기 InterruptedException {this.port = port; this.run (); }} 프로세서 및 배포
ServerHandler.java
패키지 com.test.netty.handler; IO.NETTY.CHANNEL.CHANNELHANDLERADAPTER 가져 오기; import io.netty.channel.channel HandhandlerContext; import com.test.netty.invote.actionMaputil; com.test.netty.message.header import; com.test.netty.message.message 가져 오기; / ** * * @author janehuang * */ public class serverhandler는 channelHandlerAdapter {@override public void channelActive (channelHandlerContext ctx) 예외 {string content = "연결을 받았다"; 헤더 헤더 = 새 헤더 ((byte) 0, (byte) 1, (바이트) 1, (바이트) 1, (바이트) 0, "713F17CA614361FB257DC6741332CAF2", content.getBytes ( "UTF-8"). 길이, 1); 메시지 메시지 = 새 메시지 (헤더, 내용); ctx.writeAndflush (메시지); } @override public void ExceptionCaught (Chann ctx.close (); } @override public void Channelread (Chann // (1)/* 요청 분배*/ ActionMaputil.invote (header.getCammand (), Ctx, M); }} 배포 도구 카테고리
ActionMaputil.java
패키지 com.test.netty.invote; import java.lang.reflect.method; java.util.hashmap import; java.util.map import; public class actionmaputil {private static map <integer, action> map = new Hashmap <integer, action> (); public static object invote (정수 키, 객체 ... args)는 예외를 던집니다 {action action = map.get (키); if (action! = null) {메소드 메소드 = action.getMethod (); try {return method.invoke (action.getObject (), args); } catch (예외 e) {Throw e; }} return null; } public static void put (정수 키, 액션 조치) {map.put (키, 동작); }} 분포를 위해 생성 된 개체
action.java
패키지 com.test.netty.invote; import java.lang.reflect.method; 공개 집단 행동 {개인 방법 방법; 개인 객체 객체; 공개 메소드 getMethod () {return method; } public void setMethod (메소드 메소드) {this.method = 메소드; } public Object getObject () {return 객체; } public void setObject (Object Object) {this.Object = Object; }}SpringMVC의 @Controller와 유사한 사용자 정의 주석
NettyController.java
패키지 com.test.netty.core; import java.lang.annotation.documented; import java.lang.annotation.elementtype; java.lang.annotation.trention import; java.lang.annotation.retentionpolicy import; Java.lang.annotation.target import; org.springframework.stereotyp.component import; @retention (rendentionpolicy.runtime) @target (elementtype.type) @documented @component public @interface nettycontroller {} 유형 스프링 MVC의 @reqestmapping
ActionMap.java
패키지 com.test.netty.core; import java.lang.annotation.documented; import java.lang.annotation.elementtype; java.lang.annotation.trention import; java.lang.annotation.retentionpolicy import; Java.lang.annotation.target import; @retention (rendentionpolicy.runtime) @target (elementtype.method) @documented public @interface actionmap {int key (); } 이 주석은 스프링 초기화 Beans 이후 컨테이너에 이러한 물체를 저장하기 위해 추가됩니다. 이 콩은 봄에 구성되어야합니다. 스프링 콩은 인스턴스화 후에 호출됩니다.
ActionBeanPostProcessor.java
패키지 com.test.netty.core; import java.lang.reflect.method; org.springframework.beans.beansexception import; org.springframework.beans.factory.config.beanpostprocessor; import com.test.netty.invote.action; import com.test.netty.invote.actionMaputil; 공개 집단 ActionBeanPostProcessor는 BeanPostProcessor를 구현합니다. {public object postprocessbeforeinitialization (Object Bean, String Beaname)은 beansexception {return bean; } public Object PostProcessAfterinitialization (Object Bean, String Beanname)은 Beansexception을 던졌습니다. for (method method : methods) {ActionMap ActionMap = Method.getAntation (ActionMap.class); if (actionMap! = null) {action action = new Action (); action.setMethod (메소드); action.setObject (bean); ActionMaputil.put (ActionMap.key (), action); }} 반환 Bean; }} 컨트롤러 인스턴스
Usercontroller.java
패키지 com.test.netty.controller; import io.netty.channel.channel HandhandlerContext; org.springframework.beans.factory.annotation.autowired; com.test.model.usermodel import; import com.test.netty.core.actionMap; com.test.netty.core.nettycontroller 가져 오기; com.test.netty.message.message 가져 오기; import com.test.service.userservice; @nettyController () public class userAction {@autowired private userervice userervice; @ActionMap (key = 1) 공개 문자열 로그인 (Chann System.out.println (String.format ( "닉네임 사용 : %s; 암호 %d; 송신기 내용 %s", usermodel.getnickname (), usermodel.getid (), message.getData ())); usermodel.getNickName ()을 반환합니다. }} ApplicationContext.xml 구성 파일에 추가하십시오
<bean/>
테스트 코드
패키지 테스트; import org.springframework.context.applicationcontext; import org.springframework.context.support.classPathXmlApplicationContext; com.test.netty.server.timeserver 가져 오기; 공개 클래스 테스트 {public static void main (String [] args) {ApplicationContext ac = new ClassPathXmlApplicationContext ( "ApplicationContext.xml"); Timeserver Timeserver = ac.getBean (TimesServer.class); try {timeserver.start (8888); } catch (InterruptedException e) {// todo 자동 생성 캐치 블록 e.printstacktrace (); }}} 테스트 스위치 끝
패키지 테스트; import java.io.ioexception; import java.io.outputStream; import java.net.socket; 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 {// 서버 소켓에 연결 = 새 소켓 ( "127.0.0.1", 8888); 시도 {// outputStream out = socket.getOutputStream ()에 정보를 보내는 dataOutputStream을 시도합니다. // 콘솔 스캐너 스캐너에서 입력하는 데 사용되는 표준 입력 스트림을 장식합니다. = 새 스캐너 (System.In); while (true) {string send = scanner.nextline (); System.out.println ( "클라이언트 :" + send); 바이트 [] by = send.getBytes ( "UTF-8"); 헤더 헤더 = 새 헤더 ((바이트) 1, (바이트) 1, (바이트) 1, (바이트) 1, (바이트) 1, (바이트) 1, "713f17ca614361fb257dc6741332caf2", by.length, 1); Message Message = 새 메시지 (Header, Send); out.write (message.tobyte ()); out.flush (); // 콘솔에서 얻은 정보를 서버로 보내 // out.writeUtf ( "client :" + send); // 서버에서 정보를 읽습니다}} 마침내 {socket.close (); }} catch (ioexception e) {e.printstacktrace (); }}}테스트 결과, OK
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.