iOS开发之AsyncSocket使用教程
用socket可以实现像QQ那样发送即时消息的功能。客户端和服务端需要建立长连接,在长连接的情况下,发送消息。客户端可以发送心跳包来检测长连接。
在iOS开发中使用socket,一般都是用第三方库AsyncSocket,不得不承认这个库确实很强大。下载地址CocoaAsyncSocket
。
使用AsyncSocket的时候可以做一层封装,根据需求提供几个接口出来。比如:连接、断开连接、发送消息等等。还有接受消息,接受到的消息可以通过通知、代理、block等传出去。
简单介绍一下对AsyncSocket使用.一般来说,一个用户只需要建立一个socket长连接,所以可以用单例类方便使用。
定义单列类:LGSocketServe
LGSocketServe.h
//
// LGSocketServe.h
// AsyncSocketDemo
//
// Created by ligang on 15/4/3.
// Copyright (c) 2015年 ligang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "AsyncSocket.h"
@interface LGSocketServe : NSObject<AsyncSocketDelegate>
+ (LGSocketServe *)sharedSocketServe;
@end
LGSocketServe.m
//
// LGSocketServe.m
// AsyncSocketDemo
//
// Created by ligang on 15/4/3.
// Copyright (c) 2015年 ligang. All rights reserved.
//
#import "LGSocketServe.h"
@implementation LGSocketServe
static LGSocketServe *socketServe = nil;
#pragma mark public static methods
+ (LGSocketServe *)sharedSocketServe {
@synchronized(self) {
if(socketServe == nil) {
socketServe = [[[self class] alloc] init];
}
}
return socketServe;
}
+(id)allocWithZone:(NSZone *)zone
{
@synchronized(self)
{
if (socketServe == nil)
{
socketServe = [super allocWithZone:zone];
return socketServe;
}
}
return nil;
}
@end
建立socket长连接
LGSocketServe.h
@property (nonatomic, strong) AsyncSocket *socket; // socket
// socket连接
- (void)startConnectSocket;
LGSocketServe.m
//自己设定
#define HOST @"192.168.0.1"
#define PORT 8080
//设置连接超时
#define TIME_OUT 20
- (void)startConnectSocket
{
self.socket = [[AsyncSocket alloc] initWithDelegate:self];
[self.socket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
if ( ![self SocketOpen:HOST port:PORT] )
{
}
}
- (NSInteger)SocketOpen:(NSString*)addr port:(NSInteger)port
{
if (![self.socket isConnected])
{
NSError *error = nil;
[self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:&error];
}
return 0;
}
宏定义一下HOST、PORT、TIME_OUT,实现startConnectSocket方法。这个时候要设置一下AsyncSocket的代理AsyncSocketDelegate。当长连接成功之后会调用:
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
//这是异步返回的连接成功,
NSLog(@"didConnectToHost");
}
心跳
LGSocketServe.h
@property (nonatomic, retain) NSTimer *heartTimer; // 心跳计时器
LGSocketServe.m
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
//这是异步返回的连接成功,
NSLog(@"didConnectToHost");
//通过定时器不断发送消息,来检测长连接
self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];
[self.heartTimer fire];
}
// 心跳连接
-(void)checkLongConnectByServe{
// 向服务器发送固定可是的消息,来检测长连接
NSString *longConnect = @"connect is here";
NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:1 tag:1];
}
在连接成功的回调方法里,启动定时器,每隔2秒向服务器发送固定的消息来检测长连接。(这个根据服务器的需要就可以了)
断开连接
1,用户手动断开连接
LGSocketServe.h
// 断开socket连接
-(void)cutOffSocket;
LGSocketServe.m
-(void)cutOffSocket
{
self.socket.userData = SocketOfflineByUser;
[self.socket disconnect];
}
cutOffSocket是用户断开连接之后,不在尝试重新连接。
2,wifi断开,socket断开连接
LGSocketServe.m
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);
if (err.code == 57) {
self.socket.userData = SocketOfflineByWifiCut;
}
}
wifi断开之后,会回调onSocket:willDisconnectWithError:方法,err.code == 57,这个时候设置self.socket.userData = SocketOfflineByWifiCut。
重新连接
socket断开之后会回调:
LGSocketServe.m
- (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSLog(@"7878 sorry the connect is failure %ld",sock.userData);
if (sock.userData == SocketOfflineByServer) {
// 服务器掉线,重连
[self startConnectSocket];
}
else if (sock.userData == SocketOfflineByUser) {
// 如果由用户断开,不进行重连
return;
}else if (sock.userData == SocketOfflineByWifiCut) {
// wifi断开,不进行重连
return;
}
}
在onSocketDidDisconnect回调方法里面,会根据self.socket.userData来判断是否需要重新连接。
发送消息
LGSocketServe.h
// 发送消息
- (void)sendMessage:(id)message;
LGSocketServe.m
//设置写入超时 -1 表示不会使用超时
#define WRITE_TIME_OUT -1
- (void)sendMessage:(id)message
{
//像服务器发送数据
NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];
}
//发送消息成功之后回调
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
}
发送消息成功之后会调用onSocket:didWriteDataWithTag:,在这个方法里可以进行读取消息。
接受消息
LGSocketServe.m
//设置读取超时 -1 表示不会使用超时
#define READ_TIME_OUT -1
#define MAX_BUFFER 1024
//发送消息成功之后回调
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
//读取消息
[self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
}
//接受消息成功之后回调
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
//服务端返回消息数据量比较大时,可能分多次返回。所以在读取消息的时候,设置MAX_BUFFER表示每次最多读取多少,当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息,然后才解析
if( data.length < MAX_BUFFER )
{
//收到结果解析...
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dic);
//解析出来的消息,可以通过通知、代理、block等传出去
}
[self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
接受消息后去解析,然后可以通过通知、代理、block等传出去。在onSocket:didReadData:withTag:回调方法里面需要不断读取消息,因为数据量比较大的话,服务器会分多次返回。所以我们需要定义一个MAX_BUFFER的宏,表示每次最多读取多少。当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息,然后才解析 。
出错处理
LGSocketServe.m
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSData * unreadData = [sock unreadData]; // ** This gets the current buffer
if(unreadData.length > 0) {
[self onSocket:sock didReadData:unreadData withTag:0]; // ** Return as much data that could be collected
} else {
NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);
if (err.code == 57) {
self.socket.userData = SocketOfflineByWifiCut;
}
}
}
socket出错会回调onSocket:willDisconnectWithError:方法,可以通过unreadData来读取未来得及读取的buffer。
使用
导入#import “LGSocketServe.h”
LGSocketServe *socketServe = [LGSocketServe sharedSocketServe];
//socket连接前先断开连接以免之前socket连接没有断开导致闪退
[socketServe cutOffSocket];
socketServe.socket.userData = SocketOfflineByServer;
[socketServe startConnectSocket];
//发送消息 @"hello world"只是举个列子,具体根据服务端的消息格式
[socketServe sendMessage:@"hello world"];
以上是AsyncSocket的简单使用,在实际开发过程中依然会碰到很多问题,欢迎加我的微信公众号iOS开发:iOSDevTip,一起讨论AsyncSocket中遇到的问题。
AsyncSocketDemo下载地址: AsyncSocketDemo
Posted by 李刚 Apr 3rd, 2015 4:57 pm ios开发
本文出处刚刚在线:http://www.superqq.com/blog/2015/04/03/ioskai-fa-zhi-asyncsocketshi-yong-jiao-cheng/
自由转载-请在开头注明本文出处。
微信公众号iOS开发:iOSDevTip
iOS开发之AsyncSocket使用教程的更多相关文章
- iOS开发之Socket通信实战--Request请求数据包编码模块
实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncS ...
- iOS开发之UISearchBar初探
iOS开发之UISearchBar初探 UISearchBar也是iOS开发常用控件之一,点进去看看里面的属性barStyle.text.placeholder等等.但是这些属性显然不足矣满足我们的开 ...
- iOS开发之UIImage等比缩放
iOS开发之UIImage等比缩放 评论功能真不错 评论开通后,果然有很多人吐槽.谢谢大家的支持和关爱,如果有做的不到的地方,还请海涵.毕竟我一个人的力量是有限的,我会尽自己最大的努力大家准备一些干货 ...
- iOS开发之 Xcode6 添加xib文件,去掉storyboard的hello world应用
iOS开发之 Xcode6.1创建仅xib文件,无storyboard的hello world应用 由于Xcode6之后,默认创建storyboard而非xib文件,而作为初学,了解xib的加载原理 ...
- iOS开发之loadView、viewDidLoad及viewDidUnload的关系
iOS开发之loadView.viewDidLoad及viewDidUnload的关系 iOS开发之loadView.viewDidLoad及viewDidUnload的关系 标题中所说的3个方 ...
- iOS开发之info.pist文件和.pch文件
iOS开发之info.pist文件和.pch文件 如果你是iOS开发初学者,不用过多的关注项目中各个文件的作用.因为iOS开发的学习路线起点不在这里,这些文件只会给你学习带来困扰. 打开一个项目,我们 ...
- iOS开发之WKWebView简单使用
iOS开发之WKWebView简单使用 iOS开发之 WKWebVeiw使用 想用UIWebVeiw做的,但是突然想起来在iOS8中出了一个新的WKWebView,算是UIWebVeiw的升级版. ...
- iOS 开发之Block
iOS 开发之Block 一:什么是Block.Block的作用 UI开发和网络常见功能的实现回调,按钮事件的处理方法是回调方法. 1. 按钮事件 target action 机制. 它是将一 ...
- iOS开发之Xcode常用调试技巧总结
转载自:iOS开发之Xcode常用调试技巧总结 最近在面试,面试过程中问到了一些Xcode常用的调试技巧问题.平常开发过程中用的还挺顺手的,但你要突然让我说,确实一脸懵逼.Debug的技巧很多,比如最 ...
随机推荐
- sim卡中电话本(ADN)的简要格式
ADN的格式 ADN存放于sim卡下面3f00/7f10/6f3a,记录文件格式,其最小记录格式为14,最长为255(?),记录个数最大为255(?) 其后数14个字节是必有的,其前12个字节是电话号 ...
- 如何在 静态编译的QT 5.5.1 中 使用数据库插件连接 ODBC(调用静态插件)
前段时间由于工作的关系,需要编写一个将数据插入到 Sql server 2012 的桌面软件. 由于之前使用的是MFC,偶然间接触到了Qt后,被它的简洁惊艳到了,于是便毅然而然的转投到了Qt的怀抱,哈 ...
- 茴香豆的第五种写法---设置ExpandableListView系统自带图标按下效果
1 编写groupindicator_selector.xml如下: <?xml version="1.0" encoding="utf-8"?> ...
- C++中顶层const和底层const
1. 顶层 const 与底层 const 概念 指针本身是一个对象,由于,指针实际相应着内存单元的一段存储空间,然而,指针所指向的也是一个数据对象,因此,指针是一个常量与指针所指向的是一个常量是两个 ...
- css组件规范
7月份研究了下 写了下总结. 笔记地址
- HTML系列(二):头部meta元素
有关name: 一.页面关键字 网站关键字:用户通过搜索引擎能搜到该网站的词汇.最好控制在10个以内. 基本语法: <meta name="keywords" content ...
- css背景渐变兼容(兼容所有ie)
css3里面一行可以搞定的事,换到ie里,要用滤镜,在网上找了很多,不知道什么原因都没用,终于找到个有用的,放在这里,方便大家用,自己也找得到~ 完整型代码,兼容所有浏览器: background: ...
- .aspx.cs传值与取值
1:.aspx中post传值 $.post("ABP_ExchangeRatelz.aspx", { option: "isdelete", Ori_Curre ...
- mysql之数据库特性认识
最近面试经常被面试官问道关于数据库方面的知识,于是总结一下面试官问的题以及自己对数据库的认识 1.之前百度面试官问了我一个特别基础的sql问题:如何清除表的所有记录,以前在学校做项目开发的时候有使用过 ...
- BZOJ 1832: [AHOI2008]聚会( LCA )
LCA模板题...不难发现一定是在某2个人的LCA处集合是最优的, 然后就3个LCA取个最小值就OK了. 距离就用深度去减一减就可以了. 时间复杂度O(N+MlogN) (树链剖分) -------- ...