基于Telegram二次開發(fā) --- MTProtoKit 消息解析

內(nèi)置消息簡析

MTProtoKit 中,有很多內(nèi)置的消息,除了這些消息之外,還有和解析這些消息相關的類,比如 MTBufferReaderMTInternalMessageParser,這些都是用來對這些內(nèi)置消息進行解析用的;除了內(nèi)置消息,還應該有很多業(yè)務相關性的消息,而這些消息都不在 MTProtoKit 考慮之中,MTProtoKit 將其它消息的序列化由 MTSerialization 協(xié)議委托給了使用者(Serialization)去實現(xiàn),這樣做還是很合理的,因為 MTProto 是一個非常動態(tài)的協(xié)議,擴展性非常強。

內(nèi)置消息其實就像編程語言給我們提供的標準庫一樣,它是框架,也是基礎,下面簡單選取一些消息做個介紹:

全局上下文

什么是上下文呢?
上下文就是在某個特定的場景里,用于記錄該場景特定狀態(tài)的一種抽象。

所謂的全局上下文,也就是 MTContext 類,這是一個使用相當頻繁的類,它的主要意圖是用來給 MTProtoKit 中,其它類提供一個公共的運行上下文,也相當于是整個 MTProtoKit 的入口點。所以,一些公共的狀態(tài)和方法都會被提升到這個類中,它大體記錄了以下信息:

1.客戶端運行環(huán)境,即 MTApiEnvironment。
2.非內(nèi)置消息的序列化器實現(xiàn),即 MTSerialization 協(xié)議(Serialization)的實現(xiàn)。
3.客戶端時間,并允許設定偏差來校準。
4.當前用戶的授權(quán)相關信息和操作。
5.數(shù)據(jù)中心的相關信息和操作。
6.傳輸格式(MTTransportScheme)的相關信息和操作。

其他細節(jié)

現(xiàn)在,我們再來看看一些比較有意思的細節(jié)處理;首先是 MTBuffer 類中,字節(jié)對齊的算法實現(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];
}

當這個塊的長度小于 254 時,第一個字節(jié)就是用來標識內(nèi)容的長度;而當這個塊的長度大于或等于 254 時,第一個字節(jié)只是一個標志,后面 3 個字節(jié)才是真正的長度,所以,每個塊的最大長度是 24 位值,而不是 32 位;這樣做,長度值所占用的字節(jié)就可以被壓縮了。

再來看一個 MTInternalMessageParser 中的 decompressGZip 方法,因為消息是可以放置在 gzip 容器中進行傳輸?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);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容