亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Mina、Netty、Twisted一起學(五):整合protobuf

2019-11-14 21:01:54
字體:
來源:轉載
供稿:網友
Mina、Netty、Twisted一起學(五):整合PRotobuf

protobuf是谷歌的Protocol Buffers的簡稱,用于結構化數據和字節碼之間互相轉換(序列化、反序列化),一般應用于網絡傳輸,可支持多種編程語言。

protobuf如何使用這里不再介紹,本文主要介紹在MINA、Netty、Twisted中如何使用protobuf,不了解protobuf的同學可以去參考我的另一篇博文。

在前面的一篇博文中,有介紹到一種用一個固定為4字節的前綴Header來指定Body的字節數的一種消息分割方式,在這里同樣要使用到。只是其中Body的內容不再是字符串,而是protobuf字節碼。

在處理業務邏輯時,肯定不希望還要對數據進行序列化和反序列化,而是希望直接操作一個對象,那么就需要有相應的編碼器和解碼器,將序列化和反序列化的邏輯寫在編碼器和解碼器中。有關編碼器和解碼器的實現,上一篇博文中有介紹。

Netty包中已經自帶針對protobuf的編碼器和解碼器,那么就不用再自己去實現了。而MINA、Twisted還需要自己去實現protobuf的編碼器和解碼器。

這里定義一個protobuf數據結構,用于描述一個學生的信息,保存為StudentMsg.proto文件:

message Student {      // ID      required int32 id = 1;          // 姓名      required string name = 2;        // email      optional string email = 3;        // 朋友      repeated string friends = 4;  }  

用StudentMsg.proto分別生成java和Python代碼,將代碼加入到相應的項目中。生成的代碼就不再貼上來了。

下面分別介紹在Netty、MINA、Twisted如何使用protobuf來傳輸Student信息。

Netty:

Netty自帶protobuf的編碼器和解碼器,分別是ProtobufEncoder和ProtobufDecoder。需要注意的是,ProtobufEncoder和ProtobufDecoder只負責protobuf的序列化和反序列化,而處理消息Header前綴和消息分割的還需要LengthFieldBasedFrameDecoder和LengthFieldPrepender。LengthFieldBasedFrameDecoder即用于解析消息Header前綴,根據Header中指定的Body字節數截取Body,LengthFieldPrepender用于在wirte消息時在消息前面添加一個Header前綴來指定Body字節數。

public class TcpServer {        public static void main(String[] args) throws InterruptedException {          EventLoopGroup bossGroup = new NioEventLoopGroup();          EventLoopGroup workerGroup = new NioEventLoopGroup();          try {              ServerBootstrap b = new ServerBootstrap();              b.group(bossGroup, workerGroup)                      .channel(NioServerSocketChannel.class)                      .childHandler(new ChannelInitializer<SocketChannel>() {                          @Override                          public void initChannel(SocketChannel ch)                                  throws Exception {                              ChannelPipeline pipeline = ch.pipeline();                                  // 負責通過4字節Header指定的Body長度將消息切割                              pipeline.addLast("frameDecoder",                                       new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));                                                            // 負責將frameDecoder處理后的完整的一條消息的protobuf字節碼轉成Student對象                              pipeline.addLast("protobufDecoder",                                      new ProtobufDecoder(StudentMsg.Student.getDefaultInstance()));                                // 負責將寫入的字節碼加上4字節Header前綴來指定Body長度                              pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));                                                            // 負責將Student對象轉成protobuf字節碼                              pipeline.addLast("protobufEncoder", new ProtobufEncoder());                                pipeline.addLast(new TcpServerHandler());                          }                      });              ChannelFuture f = b.bind(8080).sync();              f.channel().closeFuture().sync();          } finally {              workerGroup.shutdownGracefully();              bossGroup.shutdownGracefully();          }      }  } 

處理事件時,接收和發送的參數直接就是Student對象:

public class TcpServerHandler extends ChannelInboundHandlerAdapter {        @Override      public void channelRead(ChannelHandlerContext ctx, Object msg) {                    // 讀取客戶端傳過來的Student對象          StudentMsg.Student student = (StudentMsg.Student) msg;          System.out.println("ID:" + student.getId());          System.out.println("Name:" + student.getName());          System.out.println("Email:" + student.getEmail());          System.out.println("Friends:");          List<String> friends = student.getFriendsList();          for(String friend : friends) {              System.out.println(friend);          }            // 新建一個Student對象傳到客戶端          StudentMsg.Student.Builder builder = StudentMsg.Student.newBuilder();          builder.setId(9);          builder.setName("服務器");          builder.setEmail("123@abc.com");          builder.addFriends("X");          builder.addFriends("Y");          StudentMsg.Student student2 = builder.build();          ctx.writeAndFlush(student2);      }        @Override      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {          cause.printStackTrace();          ctx.close();      }  }  

MINA:

在MINA中沒有針對protobuf的編碼器和解碼器,但是可以自己實現一個功能和Netty一樣的編碼器和解碼器。

編碼器:

public class MinaProtobufEncoder extends ProtocolEncoderAdapter {        @Override      public void encode(Iosession session, Object message,              ProtocolEncoderOutput out) throws Exception {            StudentMsg.Student student = (StudentMsg.Student) message;          byte[] bytes = student.toByteArray(); // Student對象轉為protobuf字節碼          int length = bytes.length;                    IoBuffer buffer = IoBuffer.allocate(length + 4);          buffer.putInt(length); // write header          buffer.put(bytes); // write body          buffer.flip();          out.write(buffer);      }  }  

解碼器:

public class MinaProtobufDecoder extends CumulativeProtocolDecoder {        @Override      protected boolean doDecode(IoSession session, IoBuffer in,              ProtocolDecoderOutput out) throws Exception {            // 如果沒有接收完Header部分(4字節),直接返回false          if (in.remaining() < 4) {              return false;          } else {                // 標記開始位置,如果一條消息沒傳輸完成則返回到這個位置              in.mark();                // 讀取header部分,獲取body長度              int bodyLength = in.getInt();                // 如果body沒有接收完整,直接返回false              if (in.remaining() < bodyLength) {                  in.reset(); // IoBuffer position回到原來標記的地方                  return false;              } else {                  byte[] bodyBytes = new byte[bodyLength];                  in.get(bodyBytes); // 讀取body部分                  StudentMsg.Student student = StudentMsg.Student.parseFrom(bodyBytes); // 將body中protobuf字節碼轉成Student對象                  out.write(student); // 解析出一條消息                  return true;              }          }      }  }  

MINA服務器加入protobuf的編碼器和解碼器:

public class TcpServer {        public static void main(String[] args) throws IOException {          IoAcceptor acceptor = new NioSocketAcceptor();            // 指定protobuf的編碼器和解碼器          acceptor.getFilterChain().addLast("codec",                  new ProtocolCodecFilter(new MinaProtobufEncoder(), new MinaProtobufDecoder()));            acceptor.setHandler(new TcpServerHandle());          acceptor.bind(new InetSocketAddress(8080));      }  }  

這樣,在處理業務邏輯時,就和Netty一樣了:

public class TcpServerHandle extends IoHandlerAdapter {        @Override      public void exceptionCaught(IoSession session, Throwable cause)              throws Exception {          cause.printStackTrace();      }            @Override      public void messageReceived(IoSession session, Object message)              throws Exception {            // 讀取客戶端傳過來的Student對象          StudentMsg.Student student = (StudentMsg.Student) message;          System.out.println("ID:" + student.getId());          System.out.println("Name:" + student.getName());          System.out.println("Email:" + student.getEmail());          System.out.println("Friends:");          List<String> friends = student.getFriendsList();          for(String friend : friends) {              System.out.println(friend);          }            // 新建一個Student對象傳到客戶端          StudentMsg.Student.Builder builder = StudentMsg.Student.newBuilder();          builder.setId(9);          builder.setName("服務器");          builder.setEmail("123@abc.com");          builder.addFriends("X");          builder.addFriends("Y");          StudentMsg.Student student2 = builder.build();          session.write(student2);      }  }  

Twisted:

在Twisted中,首先定義一個ProtobufProtocol類,繼承Protocol類,充當編碼器和解碼器。處理業務邏輯的TcpServerHandle類再繼承ProtobufProtocol類,調用或重寫ProtobufProtocol提供的方法。

# -*- coding:utf-8 –*-    from struct import pack, unpack  from twisted.internet.protocol import Factory  from twisted.internet.protocol import Protocol  from twisted.internet import reactor  import StudentMsg_pb2    # protobuf編碼、解碼器  class ProtobufProtocol(Protocol):        # 用于暫時存放接收到的數據      _buffer = b""        def dataReceived(self, data):          # 上次未處理的數據加上本次接收到的數據          self._buffer = self._buffer + data          # 一直循環直到新的消息沒有接收完整          while True:              # 如果header接收完整              if len(self._buffer) >= 4:                  # header部分,按大字節序轉int,獲取body長度                  length, = unpack(">I", self._buffer[0:4])                  # 如果body接收完整                  if len(self._buffer) >= 4 + length:                      # body部分,protobuf字節碼                      packet = self._buffer[4:4 + length]                                            # protobuf字節碼轉成Student對象                      student = StudentMsg_pb2.Student()                      student.ParseFromString(packet)                                            # 調用protobufReceived傳入Student對象                      self.protobufReceived(student)                                            # 去掉_buffer中已經處理的消息部分                      self._buffer = self._buffer[4 + length:]                  else:                      break;              else:                  break;        def protobufReceived(self, student):          raise NotImplementedError        def sendProtobuf(self, student):          # Student對象轉為protobuf字節碼          data = student.SerializeToString()          # 添加Header前綴指定protobuf字節碼長度          self.transport.write(pack(">I", len(data)) + data)    # 邏輯代碼  class TcpServerHandle(ProtobufProtocol):        # 實現ProtobufProtocol提供的protobufReceived      def protobufReceived(self, student):            # 將接收到的Student輸出          print 'ID:' + str(student.id)          print 'Name:' + student.name          print 'Email:' + student.email          print 'Friends:'          for friend in student.friends:              print friend            # 創建一個Student并發送給客戶端          student2 = StudentMsg_pb2.Student()          student2.id = 9          student2.name = '服務器'.decode('UTF-8') # 中文需要轉成UTF-8字符串          student2.email = '123@abc.com'          student2.friends.append('X')          student2.friends.append('Y')          self.sendProtobuf(student2)    factory = Factory()  factory.protocol = TcpServerHandle  reactor.listenTCP(8080, factory)  reactor.run()  

下面是Java編寫的一個客戶端測試程序:

public class TcpClient {        public static void main(String[] args) throws IOException {            Socket socket = null;          DataOutputStream out = null;          DataInputStream in = null;                    try {                socket = new Socket("localhost", 8080);              out = new DataOutputStream(socket.getOutputStream());              in = new DataInputStream(socket.getInputStream());                            // 創建一個Student傳給服務器              StudentMsg.Student.Builder builder = StudentMsg.Student.newBuilder();              builder.setId(1);              builder.setName("客戶端");              builder.setEmail("xxg@163.com");              builder.addFriends("A");              builder.addFriends("B");              StudentMsg.Student student = builder.build();              byte[] outputBytes = student.toByteArray(); // Student轉成字節碼              out.writeInt(outputBytes.length); // write header              out.write(outputBytes); // write body              out.flush();                            // 獲取服務器傳過來的Student              int bodyLength = in.readInt();  // read header              byte[] bodyBytes = new byte[bodyLength];              in.readFully(bodyBytes);  // read body              StudentMsg.Student student2 = StudentMsg.Student.parseFrom(bodyBytes); // body字節碼解析成Student              System.out.println("Header:" + bodyLength);              System.out.println("Body:");              System.out.println("ID:" + student2.getId());              System.out.println("Name:" + student2.getName());              System.out.println("Email:" + student2.getEmail());              System.out.println("Friends:");              List<String> friends = student2.getFriendsList();              for(String friend : friends) {                  System.out.println(friend);              }            } finally {              // 關閉連接              in.close();              out.close();              socket.close();          }      }  }  

用客戶端分別測試上面三個TCP服務器:

服務器輸出:

ID:1Name:客戶端Email:xxg@163.comFriends:AB

客戶端輸出:

Header:32Body:ID:9Name:服務器Email:123@abc.comFriends:XY


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品福利久久久| 日韩精品久久久久久久玫瑰园| 国产视频在线观看一区二区| 国产在线拍偷自揄拍精品| 不卡av在线播放| 日韩在线中文视频| 日韩国产精品亚洲а∨天堂免| 主播福利视频一区| 爽爽爽爽爽爽爽成人免费观看| 久久九九精品99国产精品| 欧美大成色www永久网站婷| 中文字幕自拍vr一区二区三区| 国产精品视频yy9099| 国产日韩欧美在线观看| 日韩av在线免费观看| 中文欧美日本在线资源| 伊人激情综合网| 亚洲午夜久久久影院| 日韩视频精品在线| 欧美在线视频导航| 久久久久久国产免费| 91精品国产综合久久久久久久久| 国产精品自产拍高潮在线观看| 亚洲无限乱码一二三四麻| 亚洲人成电影网站色xx| 国语自产在线不卡| 91亚洲永久免费精品| 色综合久久中文字幕综合网小说| 韩国v欧美v日本v亚洲| 中文字幕亚洲在线| 亚洲xxx大片| 中文字幕日本精品| 91嫩草在线视频| 亚洲欧美日韩国产中文专区| 精品国产福利视频| 美日韩精品免费视频| 久久中文精品视频| 日韩av在线网址| 日韩中文字幕网址| 久久久女女女女999久久| 日韩高清人体午夜| 欧美激情综合亚洲一二区| 国产精品久久久久久久久| 国产精品极品美女在线观看免费| 日韩国产在线播放| 97人人模人人爽人人喊中文字| 久久久久久伊人| 青青久久av北条麻妃海外网| 精品一区二区三区四区在线| 亚洲第一天堂无码专区| 久久精品国产欧美激情| 欧美日韩国产成人高清视频| 日本久久亚洲电影| 日韩av在线网| 久久精视频免费在线久久完整在线看| 亚洲黄色www网站| 成人妇女免费播放久久久| 国产精品视频一区国模私拍| 中文字幕亚洲欧美日韩2019| 亚洲综合在线小说| 欧美午夜片欧美片在线观看| 日韩av电影手机在线观看| 97国产suv精品一区二区62| 2018中文字幕一区二区三区| 在线观看欧美日韩| 日韩av在线网址| 精品久久久久久电影| 日韩精品欧美国产精品忘忧草| 九九精品在线播放| 午夜精品久久久久久久99黑人| 久久精品久久久久久国产 免费| 91高清视频免费| 精品久久久久久中文字幕| 久久99热精品这里久久精品| 日韩久久精品成人| 国产精品18久久久久久首页狼| 日韩大胆人体377p| 亚洲黄色在线看| 91牛牛免费视频| 日韩电影中文 亚洲精品乱码| 国产精品久久久久久久天堂| 久久国产精品久久久| 亚洲精品国产福利| 欧美在线一级视频| 亚洲成av人影院在线观看| 国产一区二区三区视频在线观看| 人人澡人人澡人人看欧美| 久久99亚洲精品| 亚洲成av人影院在线观看| 欧美黑人性视频| 国产91ⅴ在线精品免费观看| 91亚洲精品视频| 韩剧1988在线观看免费完整版| 亚洲精品小视频| 欧美专区中文字幕| 色妞色视频一区二区三区四区| 日韩欧美中文在线| 亚洲日韩欧美视频一区| 国产999在线| 91久久久久久久久| 欧美人成在线视频| 欧美综合一区第一页| 久久亚洲精品网站| 日本欧美中文字幕| 日韩免费视频在线观看| 国产精品一区二区三区成人| 久久综合伊人77777| 久久亚洲国产精品| 永久免费看mv网站入口亚洲| 亚洲精品91美女久久久久久久| 欧美黑人又粗大| 日韩小视频在线| 上原亚衣av一区二区三区| 性视频1819p久久| www.xxxx欧美| 欧美日韩国产色| 蜜月aⅴ免费一区二区三区| 成人久久18免费网站图片| 久久久久国产精品免费| 4388成人网| 亚洲a级在线播放观看| 国产成人涩涩涩视频在线观看| 97色在线视频观看| 亚洲成人av片在线观看| 一区二区欧美亚洲| 青青久久aⅴ北条麻妃| 中文字幕日韩视频| 最新国产精品亚洲| 欧美激情视频在线观看| 国产精品久久久久久久久久久久久久| 亚洲精品日韩av| 亚洲电影在线观看| 538国产精品视频一区二区| 在线视频精品一| 欧美性理论片在线观看片免费| 91成品人片a无限观看| 亚洲精品白浆高清久久久久久| 国内精久久久久久久久久人| 欧美情侣性视频| 日韩av中文字幕在线免费观看| www日韩中文字幕在线看| 国产精品嫩草影院久久久| 国产一区二区久久精品| 91久久久久久久久久| 日韩av在线免费播放| 91精品久久久久久久久久久| 亚洲社区在线观看| 亚洲直播在线一区| 日韩一区二区三区国产| 亚洲成人激情在线| 在线视频欧美日韩| 中文字幕成人精品久久不卡| 亚洲性日韩精品一区二区| 国产精品高潮呻吟久久av野狼| 欧美激情手机在线视频| 亚洲成人三级在线| 国产91免费观看| 国产精品日韩精品| 欧美一级淫片videoshd| 欧美激情精品久久久久久蜜臀| 国产精品一区二区三区毛片淫片| 色悠悠久久88| 亚洲乱码av中文一区二区| 国产精品久久久久久久久借妻|