demo下载地址 : https://github.com/coderMyy/CocoaAsyncSocket_Demo , 如果觉得有那么一丁点用 , 麻烦点一颗star ,谢谢.
想要搭建一个完善 ,且自定义程度较高 , 又便于后期维修和修改的IM体系 , 采用环信或者融云等实际上是非常消耗精力和时间的 ,因为他们做得也不完善 . 目前咱们已知的通信协议大概有MQTT , XMPP等 .. 然而 , 通信协议是别人制定好的规则 , 想要不受约束 , 就需要自定义一套属于自己的通信协议 . 其实通信协议并不难 , 无非就是iOS端,安卓端,web端和服务器共同协商一套,大家的交流方式 . 话不多说 , 目前项目中功能除了视频通话和语音通话 ,其他IM的功能基本上已经实现, 跟微信对比 . 当然还有很多的细节需要改进和完善 . iOS客户端的TCP是基于CocoaAsyncSocket开源框架 , 服务器采用Netty框架 , 安卓采用Netty框架.
Appdelegate
#import "AppDelegate.h"
#import "RealReachability.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//开启网络监听
[GLobalRealReachability startNotifier];
return YES;
}
@end
登录用户信息单例
#import <Foundation/Foundation.h>
@interface Account : NSObject<NSCoding>
@property (nonatomic ,copy) NSString *myUserID; //当前用户ID
@property (nonatomic ,strong) NSNumber *sex; //性别
@property (nonatomic ,strong) NSNumber *age; //年龄
@property (nonatomic ,copy) NSString *birthDay; //生日
@property (nonatomic ,strong,getter=isVip) NSNumber *vip; //是否会员
@property (nonatomic ,strong,getter=isOnline) NSNumber *online;//是否在线
@property (nonatomic ,copy) NSString *lastLoginTime; //最后登录时间
/*
这里仅仅是一个模拟 , 真正的关于当前用户的资料可能还会有很多
*/
+ (instancetype)shareInstance;
@end
.m文件
#import "Account.h"
@implementation Account
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}
//单例
+ (instancetype)shareInstance
{
static Account *account = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
account = [[Account alloc]init];
});
return account;
}
//runtime快速解档
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivar = class_copyIvarList([Account class], &count);
for (NSInteger index = 0; index<count; index++) {
Ivar iva = ivar[index];
const char *name = ivar_getName(iva);
NSString *strName = [NSString stringWithUTF8String:name];
id value = [decoder decodeObjectForKey:strName];
[self setValue:value forKey:strName];
}
free(ivar);
}
return self;
}
//runtime快速归档
- (void)encodeWithCoder:(NSCoder *)encoder
{
unsigned int count;
Ivar *ivar = class_copyIvarList([Account class], &count);
for (NSInteger index = 0; index <count; index++) {
Ivar iv = ivar[index];
const char *name = ivar_getName(iv);
NSString *strName = [NSString stringWithUTF8String:name];
id value = [self valueForKey:strName];
[encoder encodeObject:value forKey:strName];
}
free(ivar);
}
当前登录用户信息操作工具类
#import <Foundation/Foundation.h>
#import "Account.h"
@interface AccountTool : NSObject
//保存个人信息
+ (void)save:(Account *)account;
//获取个人信息
+ (Account *)account;
@end
.m 文件
#import "AccountTool.h"
@implementation AccountTool
//当存当前用户信息
+ (void)save:(Account *)account
{
NSString *cachePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.arch"];
[NSKeyedArchiver archiveRootObject:account toFile:cachePath];
}
//获取当前登录用户信息
+ (Account *)account
{
NSString *cachePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.arch"];
return [NSKeyedUnarchiver unarchiveObjectWithFile:cachePath];
}
@end
消息模型
#import <Foundation/Foundation.h>
//TCP连接状态
typedef NS_ENUM(NSInteger) {
SocketConnectStatus_UnConnected = 0<<0,//未连接状态
SocketConnectStatus_Connected = 1<<0,//连接状态
SocketConnectStatus_DisconnectByUser = 2<<0,//主动断开连接
SocketConnectStatus_Unknow = 3<<0 //未知
}SocketConnectStatus;
//消息类型
typedef NS_ENUM(NSInteger){
ChatMessageType_Login = 0<<0,
ChatMessageType_Normal = 1<<0, //正常消息,文字,图片,语音,文件,撤回,提示语等..
ChatMessageType_Validate = 2<<0, //验证消息,添加好友,申请入群等..
ChatMessageType_System = 3<<0, //系统消息 ,xxx退出群,xxx加入群等..
ChatMessageType_NormalReceipt = 4<<0, //发送消息回执
ChatMessageType_LoginReceipt = 5<<0, //登录回执
ChatMessageType_InvalidReceipt = 6<<0, //消息发送失败回执
ChatMessageType_RepealReceipt = 7<<0, //撤回消息回执
ChatMessageContentType_Unknow = 8<<0 // 未知消息类型
}ChatMessageType;
//消息内容类型