[iOS]從零開始開發(fā)一個即時通訊APP

前言
剛開始確定這個課題的時候是因為以前有稍微研究過一些XMPP協(xié)議,在這個基礎(chǔ)上做起來應(yīng)該不難。然后開始選技術(shù)的時候還有半年,我想為什么不從更底層做起呢!那就不用XMPP,當(dāng)時接觸過相關(guān)的即時通訊技術(shù)還有WebSocket,那為什么直接從更底層的Socket開始封裝呢服務(wù)端就用Go語言吧,用來做IM服務(wù)器和HTTP服務(wù)器都很好。

技術(shù)選型
既然是基于Socket,iOS端我并不準(zhǔn)備中C語言的Socket開發(fā)封裝起,而是使用一個第三方庫CocoaAsyncSocket。XMPP的iOS framework也是從這個庫開始封裝。而Go語言的IM服務(wù)端則直接使用原生開發(fā)即可,無論是UDP還是TCP都已經(jīng)封裝的很好。

HTTP服務(wù)器使用的框架是Gin,已經(jīng)相當(dāng)成熟,可以用于大型服務(wù)端的開發(fā)了。

關(guān)于傳輸?shù)臄?shù)據(jù)格式,XMPP使用的是XML,但是體積太大,冗余過多不必要的數(shù)據(jù),考慮了很久好像也沒必要自己封裝二進(jìn)制的數(shù)據(jù)格式,我用的是Google的protocol buffer。HTTP服務(wù)器還是使用JSON。

我還需要存儲客戶端的IP地址,由于需要快速讀寫,我使用的是Redis。
AccessToken驗證方式使用的是JSON Web Token(JWT)

實現(xiàn)思路
我的想法是使用UDP Socket來傳輸數(shù)據(jù),至于為什么使用UDP呢,一開始的想法是UDP比TCP快,雖然可能會丟包但是可以試著優(yōu)化。關(guān)于使用UDP來做IM這個想法也被一些大神噴過,但是這都是我自己的想法,就這樣做著先。
使用UDP會丟包,所以我想需要一個回執(zhí)機(jī)制,接收端收到了消息后就給發(fā)送端發(fā)送一個回執(zhí),這個回執(zhí)包括這條消息的ID,如果發(fā)送方過一段時間還沒有接受到回執(zhí)的時候則重新發(fā)送。而且這個回執(zhí)還不能丟,所以我使用TCP來發(fā)送回執(zhí)。
UDP是無連接性的,還是要使用TCP來連接服務(wù)端,表明登錄狀態(tài)。所以TCP的作用是連接和發(fā)送回執(zhí)。
具體思路是當(dāng)客戶端登錄和重新連接的時候,客戶端使用UDP Socket綁定端口,然后使用TCP Socket來發(fā)送UDP 地址給服務(wù)端,服務(wù)端把用戶的ID和UDP地址存進(jìn)Redis,等發(fā)送方發(fā)送的消息包含接收端的用戶ID,服務(wù)端再從Redis取出接收方的UDP地址進(jìn)行轉(zhuǎn)發(fā)。
發(fā)送圖片我是這樣實現(xiàn)的,我會把圖片上傳到七牛云,發(fā)圖片的URL來發(fā)送,接收端只需要使用URL來加載圖片即可
簡單封裝一個通訊協(xié)議
就叫簡單的即時通訊協(xié)議,Simple Instant Messaging Protocol,簡稱SIMP
我想是基于連接的,所以一個用戶對應(yīng)一個 SIMPConnection,每一個SIMPConnection是一個單例,使用代理進(jìn)行回調(diào)

- (BOOL)connectionToRemoteHost:(NSString *)host port:(NSInteger)port forUser:(NSString *)userID;```

連接需要用戶ID和服務(wù)器的地址和端口
在連接的時候就創(chuàng)建TCP和UDP Socket 進(jìn)行連接,TCP Socket要發(fā)送連接的數(shù)據(jù),包括UDP Socket的地址
  • (BOOL)connectionToRemoteHost:(NSString *)host port:(NSInteger)port forUser:(NSString *)userID {
    self.host = host;
    self.port = port;
    self.userID = userID;
    self.tcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)];
    self.udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    return [self connect];
    }
  • (BOOL)connect {
    NSError *error;
    BOOL tcpSuccess = [self.tcpSocket connectToHost:self.host onPort:self.port error:&error];
    CheckError(@"TCPSocketConnectToHost", &error);

BOOL udpSuccess = [self.udpSocket connectToHost:self.host onPort:self.port + 1 error:&error];

CheckError(@"UDPSocketConnectToHost", &error);
[self.udpSocket beginReceiving:&error];
CheckError(@"beginReceiving", &error);

[self sendConnectData];
return tcpSuccess && udpSuccess;
}```

還有封裝一個 SIMPMessage里面包含protobuf的數(shù)據(jù)

我的protobuf數(shù)據(jù)是這樣的,版本,消息的ID,時間,文字內(nèi)容,圖片URL,發(fā)送方的ID和接收方的ID,消息類型,圖片的比例

syntax = "proto3";
message Message { float version = 1; 
uint64 messageId = 2; 
uint64 time = 3; 
string content = 4; 
string imageURL = 5; 
string fromUser = 6; 
string toUser = 7; 
MessageType type = 8; 
float imageScale = 9; 
enum MessageType { 
TEXT = 0;
 IMAGE = 1; 
AUDIO = 2; 
CONNECT = 3; 
RECEIPT = 4; 
  }
}```

還有消息隊列,群聊等一些我已經(jīng)有想法但是還沒實現(xiàn)的功能
架構(gòu)
關(guān)于整個APP的流程如下
![](http://upload-images.jianshu.io/upload_images/2023270-bb29da39932190db.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
關(guān)于iOS端,使用了MVVM設(shè)計模式結(jié)合RAC,在Controller里面只需要組合一下視圖和布局,綁定數(shù)據(jù)即可,把處理數(shù)據(jù)和大部分邏輯都放在了ViewModel里面,結(jié)構(gòu)還算清晰。

關(guān)于數(shù)據(jù)管理,我使用了一個Redux思想的全局?jǐn)?shù)據(jù)調(diào)度中心,實現(xiàn)了單向數(shù)據(jù)流,數(shù)據(jù)的持久化等。數(shù)據(jù)持久化用到了FMDB。但是大部分代碼是一個大神寫的,很屌。

效果和下一步
目前實現(xiàn)傳輸文字和圖片,好友添加還是在后臺添加(前端還沒做),動態(tài)模塊等。
![](http://upload-images.jianshu.io/upload_images/2023270-5d070c97849b458e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
通訊錄![](http://upload-images.jianshu.io/upload_images/2023270-5fb0a4e03fb932f4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
詳細(xì)資料![](http://upload-images.jianshu.io/upload_images/2023270-8cf91145c519f8eb.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
個人資料![](http://upload-images.jianshu.io/upload_images/2023270-f3028b692041394e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
聊天界面![](http://upload-images.jianshu.io/upload_images/2023270-02a3a525d49bc68e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Demo
先上傳到了github,目前功能還不完善,還會持續(xù)開發(fā)[https://github.com/AscenZ/Hey](https://github.com/AscenZ/Hey)
 轉(zhuǎn)于http://www.cnblogs.com/zyb428/p/7070330.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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