[iOS]从零开始开发一个即时通讯APP

前言
刚开始确定这个课题的时候是因为以前有稍微研究过一些XMPP协议,在这个基础上做起来应该不难。然后开始选技术的时候还有半年,我想为什么不从更底层做起呢!那就不用XMPP,当时接触过相关的即时通讯技术还有WebSocket,那为什么直接从更底层的Socket开始封装呢服务端就用Go语言吧,用来做IM服务器和HTTP服务器都很好。

技术选型
既然是基于Socket,iOS端我并不准备中C语言的Socket开发封装起,而是使用一个第三方库CocoaAsyncSocket。XMPP的iOS framework也是从这个库开始封装。而Go语言的IM服务端则直接使用原生开发即可,无论是UDP还是TCP都已经封装的很好。

HTTP服务器使用的框架是Gin,已经相当成熟,可以用于大型服务端的开发了。

关于传输的数据格式,XMPP使用的是XML,但是体积太大,冗余过多不必要的数据,考虑了很久好像也没必要自己封装二进制的数据格式,我用的是Google的protocol buffer。HTTP服务器还是使用JSON。

我还需要存储客户端的IP地址,由于需要快速读写,我使用的是Redis
AccessToken验证方式使用的是JSON Web Token(JWT)

实现思路
我的想法是使用UDP Socket来传输数据,至于为什么使用UDP呢,一开始的想法是UDP比TCP快,虽然可能会丢包但是可以试着优化。关于使用UDP来做IM这个想法也被一些大神喷过,但是这都是我自己的想法,就这样做着先。
使用UDP会丢包,所以我想需要一个回执机制,接收端收到了消息后就给发送端发送一个回执,这个回执包括这条消息的ID,如果发送方过一段时间还没有接受到回执的时候则重新发送。而且这个回执还不能丢,所以我使用TCP来发送回执。
UDP是无连接性的,还是要使用TCP来连接服务端,表明登录状态。所以TCP的作用是连接和发送回执。
具体思路是当客户端登录和重新连接的时候,客户端使用UDP Socket绑定端口,然后使用TCP Socket来发送UDP 地址给服务端,服务端把用户的ID和UDP地址存进Redis,等发送方发送的消息包含接收端的用户ID,服务端再从Redis取出接收方的UDP地址进行转发。
发送图片我是这样实现的,我会把图片上传到七牛云,发图片的URL来发送,接收端只需要使用URL来加载图片即可
简单封装一个通讯协议
就叫简单的即时通讯协议,Simple Instant Messaging Protocol,简称SIMP
我想是基于连接的,所以一个用户对应一个 SIMPConnection,每一个SIMPConnection是一个单例,使用代理进行回调

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

连接需要用户ID和服务器的地址和端口
在连接的时候就创建TCP和UDP Socket 进行连接,TCP Socket要发送连接的数据,包括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的数据

我的protobuf数据是这样的,版本,消息的ID,时间,文字内容,图片URL,发送方的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; 
  }
}```

还有消息队列,群聊等一些我已经有想法但是还没实现的功能
架构
关于整个APP的流程如下
![](http://upload-images.jianshu.io/upload_images/2023270-bb29da39932190db.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
关于iOS端,使用了MVVM设计模式结合RAC,在Controller里面只需要组合一下视图和布局,绑定数据即可,把处理数据和大部分逻辑都放在了ViewModel里面,结构还算清晰。

关于数据管理,我使用了一个Redux思想的全局数据调度中心,实现了单向数据流,数据的持久化等。数据持久化用到了FMDB。但是大部分代码是一个大神写的,很屌。

效果和下一步
目前实现传输文字和图片,好友添加还是在后台添加(前端还没做),动态模块等。
![](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)
详细资料![](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,目前功能还不完善,还会持续开发[https://github.com/AscenZ/Hey](https://github.com/AscenZ/Hey)
 转于http://www.cnblogs.com/zyb428/p/7070330.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容