您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)怎么搭建spring+netty服務(wù)器,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
我現(xiàn)在用spring+netty搭起簡(jiǎn)單的游戲服
思路:1自定義協(xié)議和協(xié)議包;2spring+netty整合;3半包粘包處理,心跳機(jī)制等;4請(qǐng)求分發(fā)(目前自己搞的都是單例模式)
下個(gè)是測(cè)試用的,結(jié)構(gòu)如下
首先自定義包頭
Header.java
package com.test.netty.message; /** * Header.java * 自定義協(xié)議包頭 * @author janehuang * @version 1.0 */ public class Header { private byte tag; /* 編碼*/ private byte encode; /*加密*/ private byte encrypt; /*其他字段*/ private byte extend1; /*其他2*/ private byte extend2; /*會(huì)話id*/ private String sessionid; /*包的長(zhǎng)度*/ 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; } @Override public 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; } }
包體,我簡(jiǎn)單處理用字符串轉(zhuǎn)字節(jié)碼,一般好多游戲用probuf系列化成二進(jìn)制
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 block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated 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); 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 block e.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 { /**包長(zhǎng)度志頭**/ public static final int HEAD_LENGHT = 45; /**標(biāo)志頭**/ public static final byte PACKAGE_TAG = 0x01; @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception { buffer.markReaderIndex(); if (buffer.readableBytes() < HEAD_LENGHT) { throw new CorruptedFrameException("包長(zhǎng)度問(wèn)題"); } byte tag = buffer.readByte(); if (tag != PACKAGE_TAG) { throw new CorruptedFrameException("標(biāo)志錯(cuò)誤"); } 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> { @Override protected 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")); } }
服務(wù)器
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 */ @Component public 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) @Override public 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(); } }
處理器并分發(fā)
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 { @Override public 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(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Message m = (Message) msg; // (1) /* 請(qǐng)求分發(fā)*/ ActionMapUtil.invote(header.getCammand(),ctx, m); } }
分發(fā)工具類
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); } }
為分發(fā)創(chuàng)建的對(duì)象
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 @Component public @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) @Documented public @interface ActionMap { int key(); }
加了這些注解是為了spring初始化bean后把這些對(duì)象存到容器,此bean需要在spring配置,spring bean 實(shí)例化后會(huì)調(diào)用
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實(shí)例
UserController.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 { @Autowired private 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;傳人內(nèi)容%s", userModel.getNickname(),userModel.getId(),message.getData())); return userModel.getNickname(); } }
applicationContext.xml配置文件記得加入這個(gè)
<bean class="com.test.netty.core.ActionBeanPostProcessor"/>
測(cè)試代碼
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 block e.printStackTrace(); } } }
測(cè)試開(kāi)關(guān)端
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 { // 連接到服務(wù)器 Socket socket = new Socket("127.0.0.1", 8888); try { // 向服務(wù)器端發(fā)送信息的DataOutputStream OutputStream out = socket.getOutputStream(); // 裝飾標(biāo)準(zhǔn)輸入流,用于從控制臺(tái)輸入 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(); // 把從控制臺(tái)得到的信息傳送給服務(wù)器 // out.writeUTF("客戶端:" + send); // 讀取來(lái)自服務(wù)器的信息 } } finally { socket.close(); } } catch (IOException e) { e.printStackTrace(); } } }
測(cè)試結(jié)果,ok了
關(guān)于“怎么搭建spring+netty服務(wù)器”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。