內(nèi)置消息簡析
在 MTProtoKit
中冻记,有很多內(nèi)置的消息,除了這些消息之外来惧,還有和解析這些消息相關(guān)的類檩赢,比如 MTBufferReader 和 MTInternalMessageParser,這些都是用來對這些內(nèi)置消息進(jìn)行解析用的违寞;除了內(nèi)置消息贞瞒,還應(yīng)該有很多業(yè)務(wù)相關(guān)性的消息,而這些消息都不在 MTProtoKit
考慮之中趁曼,MTProtoKit
將其它消息的序列化由 MTSerialization 協(xié)議委托給了使用者(Serialization)去實(shí)現(xiàn)军浆,這樣做還是很合理的,因?yàn)?MTProto
是一個非常動態(tài)的協(xié)議挡闰,擴(kuò)展性非常強(qiáng)乒融。
內(nèi)置消息其實(shí)就像編程語言給我們提供的標(biāo)準(zhǔn)庫一樣,它是框架摄悯,也是基礎(chǔ)赞季,下面簡單選取一些消息做個介紹:
- MTBadMsgNotificationMessage:服務(wù)端未能正確解析客戶端消息時,會返回該消息奢驯,并附帶錯誤碼申钩。
-
MTBadServerSaltNotificationMessage:服務(wù)端對
Salt
驗(yàn)證失敗時,會返回該消息瘪阁。 - MTDestroySessionResponseOkMessage:客戶端請求銷毀當(dāng)前會話撒遣,服務(wù)端返回銷毀成功的響應(yīng)邮偎。
- MTDestroySessionResponseNoneMessage:客戶端請求銷毀當(dāng)前會話,服務(wù)端返回未找到該會話的響應(yīng)义黎。
-
MTDropRpcResultUnknownMessage:客戶端請求服務(wù)端取消某次
RPC
請求禾进,服務(wù)端返回未知狀態(tài)的響應(yīng)。 -
MTDropRpcResultDroppedRunningMessage:客戶端請求服務(wù)端取消某次
RPC
請求廉涕,服務(wù)端返回正在處理的響應(yīng)泻云。 -
MTDropRpcResultDroppedMessage:客戶端請求服務(wù)端取消某次
RPC
請求,服務(wù)端返回處理完成的響應(yīng)狐蜕。 -
MTFutureSaltsMessage:客戶端請求服務(wù)端
Salt
壶愤,服務(wù)端的響應(yīng)消息。 - MTMsgsStateReqMessage:當(dāng)消息處理的任何一方(服務(wù)器或客戶端)長時間未收到發(fā)出消息的響應(yīng)馏鹤,則可以通過該請求來查詢消息處理狀態(tài)。
- MTMsgsStateInfoMessage:消息處理狀態(tài)響應(yīng)娇哆。
- MTMsgAllInfoMessage:自發(fā)性的通知另一方湃累,消息處理的狀態(tài)。
- MTMsgContainerMessage:消息容器碍讨,用于多個消息同時打包發(fā)送治力。
- MTMsgDetailedResponseInfoMessage:當(dāng)收到重復(fù)的消息請求時,且響應(yīng)內(nèi)容過大勃黍,服務(wù)端會返回該條響應(yīng)宵统,用于描述響應(yīng)內(nèi)容。
- MTMsgResendReqMessage:客戶端請求消息響應(yīng)重傳覆获,旨在處理重復(fù)請求時马澈,明確的讓服務(wù)端返回響應(yīng)內(nèi)容。
- MTMsgsAckMessage:消息回執(zhí)弄息。
- MTNewSessionCreatedMessage:服務(wù)端通知客戶端痊班,一個新的會話被創(chuàng)建。
-
MTRpcResultMessage:
RPC
請求響應(yīng)消息摹量。 -
MTRpcError:
RPC
請求錯誤響應(yīng)涤伐。
全局上下文
什么是上下文呢?
上下文就是在某個特定的場景里缨称,用于記錄該場景特定狀態(tài)的一種抽象凝果。
所謂的全局上下文,也就是 MTContext 類睦尽,這是一個使用相當(dāng)頻繁的類器净,它的主要意圖是用來給 MTProtoKit
中,其它類提供一個公共的運(yùn)行上下文当凡,也相當(dāng)于是整個 MTProtoKit
的入口點(diǎn)。所以,一些公共的狀態(tài)和方法都會被提升到這個類中铜涉,它大體記錄了以下信息:
1.客戶端運(yùn)行環(huán)境乘陪,即 MTApiEnvironment。
2.非內(nèi)置消息的序列化器實(shí)現(xiàn)魄缚,即 MTSerialization 協(xié)議(Serialization)的實(shí)現(xiàn)。
3.客戶端時間,并允許設(shè)定偏差來校準(zhǔn)匙赞。
4.當(dāng)前用戶的授權(quán)相關(guān)信息和操作。
5.數(shù)據(jù)中心的相關(guān)信息和操作妖碉。
6.傳輸格式(MTTransportScheme)的相關(guān)信息和操作涌庭。
其他細(xì)節(jié)
現(xiàn)在,我們再來看看一些比較有意思的細(xì)節(jié)處理欧宜;首先是 MTBuffer 類中坐榆,字節(jié)對齊的算法實(shí)現(xiàn):
static inline int roundUp(int numToRound, int multiple)
{
return multiple == 0 ? numToRound : ((numToRound % multiple) == 0 ? numToRound : (numToRound + multiple - (numToRound % multiple)));
}
很簡單的算法,但很有意思冗茸,使用 roundUp(17, 4)
席镀,則會得到 17 按照 4 向上對齊的結(jié)果,也就是 20夏漱。
MTBuffer 中豪诲,還有一個方法,也就是追加 TL 字節(jié)挂绰,我們來看一下:
- (void)appendTLBytes:(NSData *)bytes
{
int32_t length = (int32_t)bytes.length;
if (bytes == nil || length == 0)
{
[self appendInt32:0];
return;
}
int paddingBytes = 0;
if (length >= 254)
{
uint8_t tmp = 254;
[self appendBytes:&tmp length:1];
[self appendBytes:(const uint8_t *)&length length:3];
paddingBytes = roundUp(length, 4) - length;
}
else
{
[self appendBytes:(const uint8_t *)&length length:1];
paddingBytes = roundUp(length + 1, 4) - (length + 1);
}
[self appendBytes:bytes.bytes length:length];
uint8_t tmp = 0;
for (int i = 0; i < paddingBytes; i++)
[self appendBytes:&tmp length:1];
}
當(dāng)這個塊的長度小于 254
時屎篱,第一個字節(jié)就是用來標(biāo)識內(nèi)容的長度;而當(dāng)這個塊的長度大于或等于 254
時葵蒂,第一個字節(jié)只是一個標(biāo)志交播,后面 3
個字節(jié)才是真正的長度,所以践付,每個塊的最大長度是 24
位值堪侯,而不是 32
位;這樣做荔仁,長度值所占用的字節(jié)就可以被壓縮了伍宦。
再來看一個 MTInternalMessageParser 中的 decompressGZip 方法,因?yàn)橄⑹强梢苑胖迷?gzip
容器中進(jìn)行傳輸?shù)姆α海钥蛻舳诵枰鈮鹤止?jié)流:
+ (NSData *)decompressGZip:(NSData *)data
{
const int kMemoryChunkSize = 1024;
NSUInteger length = [data length];
int windowBits = 15 + 32; //Default + gzip header instead of zlib header
int retCode;
unsigned char output[kMemoryChunkSize];
uInt gotBack;
NSMutableData *result;
z_stream stream;
if ((length == 0) || (length > UINT_MAX)) //FIXME: Support 64 bit inputs
return nil;
bzero(&stream, sizeof(z_stream));
stream.avail_in = (uInt)length;
stream.next_in = (unsigned char*)[data bytes];
retCode = inflateInit2(&stream, windowBits);
if(retCode != Z_OK)
{
NSLog(@"%s: inflateInit2() failed with error %i", __PRETTY_FUNCTION__, retCode);
return nil;
}
result = [NSMutableData dataWithCapacity:(length * 4)];
do
{
stream.avail_out = kMemoryChunkSize;
stream.next_out = output;
retCode = inflate(&stream, Z_NO_FLUSH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END))
{
NSLog(@"%s: inflate() failed with error %i", __PRETTY_FUNCTION__, retCode);
inflateEnd(&stream);
return nil;
}
gotBack = kMemoryChunkSize - stream.avail_out;
if (gotBack > 0)
[result appendBytes:output length:gotBack];
} while( retCode == Z_OK);
inflateEnd(&stream);
return (retCode == Z_STREAM_END ? result : nil);
}