摘要:而實際兩者之間的通信使用的是基于的自定義二進制數(shù)據(jù)幀,對象與數(shù)據(jù)幀之間需進行轉(zhuǎn)換。該類實現(xiàn)了編碼解碼方法,故可對消息對象進行編碼或?qū)?shù)據(jù)幀進行解碼。該類的靜態(tài)方法可通過指定功能消息對象生成相應(yīng)的回復(fù)對象。
本文為該系列的第二篇文章,設(shè)計需求為:服務(wù)端程序和眾多客戶端程序通過 TCP 協(xié)議進行通信,通信雙方需通信的消息種類眾多。上一篇文章詳細描述了該通信協(xié)議的二進制數(shù)據(jù)幀格式以及基本 Java 消息類,假設(shè)通信雙方「服務(wù)端、客戶端」均由 Netty 框架構(gòu)建而成,雙方在程序內(nèi)部使用 Java 消息對象,通信雙方信息交互采用的是自定義二進制幀格式,本文通過一個具體實例,探討指定的 Java 消息對象與其相應(yīng)的二進制數(shù)據(jù)幀相互轉(zhuǎn)換的方法。
1 特定 Java 消息對象通信舉例本小節(jié)以一個具體的需求為例,講述該自定義通信協(xié)議的工作流程。該需求為:對某一個特定的客戶端進行命名。該需求的具體工作流程描述如下:
服務(wù)端需主動向指定的客戶端發(fā)送消息,對客戶端設(shè)置指定的名稱,客戶端接到指定的消息并驗證合法后,需向服務(wù)端反饋消息接受成功的確認回復(fù),服務(wù)器接收到該回復(fù)后,即可認為對客戶端進行命名的消息發(fā)送成功并且名字設(shè)置成功,若服務(wù)端在指定的時間內(nèi)未收到回復(fù),需進行重發(fā)或者向上層「如管理員或數(shù)據(jù)庫」反饋該客戶端的異常。
上述過程使用 UML 序列圖演示如下:
由上圖可以直觀地看出:管理員對服務(wù)器的操作以及服務(wù)器對管理員的反饋均為動作,Server 與 Client 之間的通信以 Java 的視角均通過 Java 消息對象,共需兩個對象:客戶端別名設(shè)置對象、客戶端別名設(shè)置回復(fù)對象。而實際兩者之間的通信使用的是基于 TCP 的自定義二進制數(shù)據(jù)幀,對象與數(shù)據(jù)幀之間需進行轉(zhuǎn)換。
2 該任務(wù)所需 Java 消息類的設(shè)計上小節(jié)所述過程需要兩個 Java 消息類,如下所示:
客戶端別名設(shè)置類
/** * 「消息對象」客戶端別名設(shè)置 */ public class MsgDeviceName extends BaseMsg { private final String name; public MsgDeviceName(BaseMsgCodec msgCodec, int groupId, int deviceId, String name) { super(msgCodec, groupId, deviceId); this.name = name; } public String getName() { return name; } @Override public String msgDetailToString() { return super.msgDetailToString() + "別名:" + name; } }
客戶端別名設(shè)置回復(fù)類「直接使用通用回復(fù)類」
/** * 「消息對象」通用消息回復(fù) */ public class MsgReplyNormal extends BaseMsg { public MsgReplyNormal(BaseMsgCodec msgCodec, int groupId, int deviceId) { super(msgCodec, groupId, deviceId); } @Override public String msgDetailToString() { return super.msgDetailToString(); } }
客戶端別名設(shè)置類相比于基礎(chǔ)消息類,覆寫了消息細節(jié)描述方法,優(yōu)化調(diào)試日志的使用體驗。主要改變是,僅僅增加了客戶端別名的引用及其 Get 方法;而對于客戶端別名設(shè)置回復(fù),直接使用了通用回復(fù)類,減小了設(shè)計的復(fù)雜度。
該自定義幀協(xié)議有一個設(shè)計要點:每一個功能性消息類均有相對應(yīng)的特定回復(fù)類。從功能位的角度來看,該兩種類的主幀功能位之間存在如下關(guān)系:
消息回復(fù)類功能位 - 消息類功能位 = 0x10
即兩類的功能位數(shù)值之差以十六進制表示為 0x10。據(jù)此設(shè)計功能性 Java 消息類后,不需要專門設(shè)計對應(yīng)的回復(fù)類,系統(tǒng)會自行使用該通用回復(fù)類進行工作。
3 該任務(wù)所需消息類編解碼器的設(shè)計編碼器可將 Java 消息對象編碼為數(shù)據(jù)幀,解碼器可講數(shù)據(jù)幀解碼為指定的 Java 消息對象,上節(jié)所述的兩種消息類均需要相對應(yīng)的編解碼器,如下所示:
3.1 客戶端別名設(shè)置編解碼器類該類相比于基礎(chǔ)類,新增了編解碼器的靜態(tài)工廠方法,手動傳入功能位及功能文字描述,進而生成包含這些參數(shù)的編解碼器。如此設(shè)計,使得所有消息的功能位和文字描述均能夠統(tǒng)一管理,降低維護成本。
該類實現(xiàn)了編碼、解碼方法,故可對消息對象進行編碼或?qū)?shù)據(jù)幀進行解碼。該類的實現(xiàn)如下所示:
/** * 「消息對象編解碼器」客戶端別名設(shè)置 */ public class MsgCodecDeviceName extends BaseMsgCodec { private static MsgCodecDeviceName msgCodec = null; public MsgCodecDeviceName (int majorMsgId, int subMsgId, String detail) { super(majorMsgId, subMsgId, detail); msgCodec = this; } public static MsgDeviceName create(int groupId, int deviceId, String name) { return new MsgDeviceName(msgCodec, groupId, deviceId, name); } @Override public ByteBuf code(BaseMsg msg, ByteBuf buffer) { MsgDeviceName message = (MsgDeviceName) msg; buffer.writeByte(message.getSubMsgId()); byte[] data = KyToArrayUtil.stringToArray(message.getName()); buffer.writeShort(data.length); buffer.writeBytes(data); return buffer; } @Override public MsgDeviceName decode(int groupId, int deviceId, byte[] data) { String name = KyToArrayUtil.arrayToString(data); return create(groupId, deviceId, name); } }3.2 通用回復(fù)編解碼器類
該類相比于基礎(chǔ)類,新增了編解碼器的靜態(tài)工廠方法,實現(xiàn)了編解碼器,理由與上小節(jié)相同。該類的 createByBaseMsg(BaseMsg) 靜態(tài)方法可通過指定功能消息對象生成相應(yīng)的回復(fù)對象。該類的實現(xiàn)如下所示:
/** * 「消息對象編解碼器」通用消息回復(fù) */ public class MsgCodecReplyNormal extends BaseMsgCodec { private static MsgCodecReplyNormal msgCodec = null; public MsgCodecReplyNormal(int majorMsgId, int subMsgId, String detail) { super(majorMsgId, subMsgId, detail); msgCodec = this; } /** * 根據(jù)收到的消息對象,創(chuàng)建新的通用消息回復(fù)對象, * * @param msg 收到的消息對象 * @return 新的通用消息回復(fù)對象 */ public static MsgReplyNormal createByBaseMsg(BaseMsg msg) { BaseMsgCodec msgCodec = MsgCodecToolkit.getMsgCodec(msg.getMajorMsgId() + 0x10, msg.getSubMsgId()); if (msgCodec == null) { return null; } return new MsgReplyNormal(msgCodec, msg.getGroupId(), msg.getDeviceId()); } /** * 創(chuàng)建新的通用消息回復(fù)對象 * * @param groupId 組號 * @param deviceId 設(shè)備號 * @return 生成的通用消息回復(fù)對象 */ private MsgReplyNormal create(int groupId, int deviceId) { return new MsgReplyNormal(this, groupId, deviceId); } @Override public ByteBuf code(BaseMsg msg, ByteBuf buffer) { MsgReplyNormal message = (MsgReplyNormal) msg; buffer.writeByte(message.getSubMsgId()); buffer.writeShort(0); return buffer; } @Override public MsgReplyNormal decode(int groupId, int deviceId, byte[] data) { return create(groupId, deviceId); } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71070.html
摘要:基本消息對象的設(shè)計消息對象的設(shè)計主要由兩部分組成特定數(shù)據(jù)幀對應(yīng)的特定消息對象。該類包含上節(jié)數(shù)據(jù)幀主幀及子幀的所有公共信息,僅僅未包含子幀中的數(shù)據(jù)體信息,該需求由基本消息對象的子類實現(xiàn)。 開發(fā)工程中,有一個常見的需求:服務(wù)端程序和多個客戶端程序通過 TCP 協(xié)議進行通信,通信雙方需通信的消息種類眾多,并且客戶端的數(shù)量可能有數(shù)萬個。為此,雙方需要約定盡可能豐富、靈活的數(shù)據(jù)幀「數(shù)據(jù)包」協(xié)議,...
摘要:本文仍以該實例為例,探討該自定義通信協(xié)議的具體工作流程,以及如何以注冊的形式靈活插拔通信消息對象。進行二進制數(shù)據(jù)幀的解碼操作時,數(shù)據(jù)幀中已包含了消息的功能位,據(jù)此可獲取相應(yīng)的編解碼器,而后可以對該數(shù)據(jù)幀進行解析,生成相應(yīng)的消息對象。 本文為該系列的第三篇文章,設(shè)計需求為:服務(wù)端程序和眾多客戶端程序通過 TCP 協(xié)議進行通信,通信雙方需通信的消息種類眾多。上一篇文章以一個具體的需求為例,...
項目地址 showImg(https://segmentfault.com/img/remote/1460000019380071); 什么是 Puzzle Puzzle 是基于 Vue 和 Webpack4 實現(xiàn)的一種項目結(jié)構(gòu);業(yè)務(wù)模塊可以像拼圖一樣與架構(gòu)模塊組合,形成不同的系統(tǒng),而這一切都是可以在生產(chǎn)環(huán)境熱插拔的;這意味著你可以隨時向你的系統(tǒng)添加新的功能模塊,甚至改版整個系統(tǒng),而不需要全量替換...
摘要:比特幣和以太幣屬于一類區(qū)塊鏈,我們將其歸類為公共無許可的區(qū)塊鏈技術(shù)。例如,在單個企業(yè)中部署時,或由受信任的權(quán)威機構(gòu)運作,完全拜占庭容錯的共識可能被認為是不必要的,并且對性能和吞吐量造成過度的拖累。 介紹 一般而言,區(qū)塊鏈是一個不可變的交易分類賬,維護在一個分布式對等節(jié)點網(wǎng)絡(luò)中。這些節(jié)點通過應(yīng)用已經(jīng)由共識協(xié)議驗證的交易來維護分類帳的副本,該交易被分組為包括將每個塊綁定到前一個塊的散列的塊...
閱讀 3475·2023-04-25 18:52
閱讀 2488·2021-11-22 15:31
閱讀 1228·2021-10-22 09:54
閱讀 3016·2021-09-29 09:42
閱讀 612·2021-09-26 09:55
閱讀 915·2021-09-13 10:28
閱讀 1111·2019-08-30 15:56
閱讀 2111·2019-08-30 15:55