基于netty游戏服后台搭建
来源:互联网 发布:淘宝海外买手 编辑:程序博客网 时间:2024/05/02 04:42
项目要转游戏开发了,所以搭个游戏服,游戏一般是长连接,自定义协议,不用http协议,BIO,NIO,AIO这些我就不说了,自己查资料
我现在用spring+netty搭起简单的游戏服
思路:1自定义协议和协议包;2spring+netty整合;3半包粘包处理,心跳机制等;4请求分发(目前自己搞的都是单例模式)
下个是测试用的,结构如下
首先自定义包头
Header.java
package com.test.netty.message;/** * Header.java * 自定义协议包头 * @author janehuang * @version 1.0 */public class Header {private byte tag; /* 编码*/private byte encode;/*加密*/private byte encrypt; /*其他字段*/private byte extend1;/*其他2*/private byte extend2;/*会话id*/private String sessionid;/*包的长度*/private int length = 1024;/*命令*/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;}@Overridepublic 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;}}
包体,我简单处理用字符串转字节码,一般好多游戏用probuf系列化成二进制
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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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 blocke.printStackTrace();}byte[] data = out.toByteArray();heapBuffer.writeBytes(data);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.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 MessageDecoder extends ByteToMessageDecoder {/**包长度志头**/public static final int HEAD_LENGHT = 45;/**标志头**/public static final byte PACKAGE_TAG = 0x01;@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {buffer.markReaderIndex();if (buffer.readableBytes() < HEAD_LENGHT) {throw new CorruptedFrameException("包长度问题");}byte tag = buffer.readByte();if (tag != PACKAGE_TAG) {throw new CorruptedFrameException("标志错误");}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);}}
编码器
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> {@Overrideprotected 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"));}}服务器
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 */@Componentpublic 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)@Overridepublic 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();}}
处理器并分发
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 { @Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception { String content="我收到连接";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(); }@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Message m = (Message) msg; // (1) /* 请求分发*/ ActionMapUtil.invote(header.getCammand(),ctx, m);} }
分发工具类
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);}}
为分发创建的对象
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;}}自定义注解,类似springmvc 里面的@Controller
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@Componentpublic @interface NettyController { }
类型spring mvc里面的@ReqestMapping
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)@Documentedpublic @interface ActionMap { int key(); }加了这些注解是为了spring初始化bean后把这些对象存到容器,此bean需要在spring配置,spring bean 实例化后会调用
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实例
UserController.java
</pre><pre name="code" class="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 {@Autowiredprivate UserService userService;@ActionMap(key=1)public String login(ChannelHandlerContext ct,Message message){UserModel userModel=this.userService.findByMasterUserId(1000001);System.out.println(String.format("用户昵称:%s;密码%d;传人内容%s", userModel.getNickname(),userModel.getId(),message.getData()));return userModel.getNickname();}}
applicationContext.xml配置文件记得加入这个
<bean class="com.test.netty.core.ActionBeanPostProcessor"/>
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 blocke.printStackTrace();} }}
测试开关端
package test;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 {// 连接到服务器Socket socket = new Socket("127.0.0.1", 8888);try {// 向服务器端发送信息的DataOutputStreamOutputStream out = socket.getOutputStream();// 装饰标准输入流,用于从控制台输入Scanner scanner = new Scanner(System.in);while (true) {String send = scanner.nextLine();System.out.println("客户端:" + send);byte[] by = send.getBytes("UTF-8");Header header = new Header((byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, "713f17ca614361fb257dc6741332caf2", by.length, 1);Message message = new Message(header, send);out.write(message.toByte());out.flush();// 把从控制台得到的信息传送给服务器// out.writeUTF("客户端:" + send);// 读取来自服务器的信息}} finally {socket.close();}} catch (IOException e) {e.printStackTrace();}}}
测试结果,ok了
0 0
- 基于netty游戏服后台搭建
- 基于netty的rts游戏服务器搭建
- 今天开始搭建netty+spring+ibats 用来实现游戏后台服务
- netty游戏服务器搭建之服务端
- netty游戏服务器搭建之客户端
- 《从零开始搭建游戏服务器》Netty导入创建一个Socket服务器
- 《从零开始搭建游戏服务器》Netty导入创建一个Socket服务器
- netty搭建
- 基于Cassandra搭建简单Blog程序后台
- 基于gulp搭建后台(json-server)
- 基于SAE搭建python微信公众后台
- 基于EasyUI的通用后台管理系统框架搭建
- 85-002-12 基于frameSet搭建前端页面的后台
- 85-002-13 基于EasyUI搭建前端页面的后台
- Parse后台搭建与运行基于Windows平台
- netty websocket 后台消息推送
- netty websocket 后台消息推送
- netty服务器搭建-http
- 服务器负载均衡的基本功能和实现原理
- 微信现金红包高级红包接口开发注意事项
- mono+jexus安装
- QT中qstring和char *的互相转换
- 接口和实现类
- 基于netty游戏服后台搭建
- Ubuntu16.04搭建bugzilla缺陷管理系统
- 使TableviewCell排序手势占满cell条
- 浅谈jquery.fn.extend与jquery.extend区别
- Centos 6.5 Zabiix 配置Mysql服务
- shuqian
- Integer.valueof(string)字符串转Integer 异常
- ArcGIS制图表达Representation-制图表达原理
- 混合编程系列