音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

iOS应用中使用AsyncSocket库处理Socket通信的用法讲解
日期:2016-05-11 09:17:53   来源:脚本之家

用socket可以实现像QQ那样发送即时消息的功能。客户端和服务端需要建立长连接,在长连接的情况下,发送消息。客户端可以发送心跳包来检测长连接。

在iOS开发中使用socket,一般都是用第三方库AsyncSocket,不得不承认这个库确实很强大。下载地址CocoaAsyncSocket

特性

AsyncSocket类是支持TCP的。
AsyncUdpSocket是支持UDP的。
AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作,本地cocoa类的基于delegate的完整支持。主要有以下特性:

  • 队列的非阻塞的读和写,而且可选超时。你可以调用它读取和写入,它会当完成后告知你。
  • 自动的socket接收。如果你调用它接收连接,它将为每个连接启动新的实例,当然,也可以立即关闭这些连接。
  • 委托(delegate)支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接,都可以通过委托模式调用。
  • 基于run loop的,而不是线程的。虽然可以在主线程或者工作线程中使用它,但你不需要这样做。它异步的调用委托方法,使用NSRunLoop。委托方法包括socket的参数,可让你在多个实例中区分。
  • 自包含在一个类中。你无需操作流或者socket,这个类帮你做了全部。
  • 支持基于IPV4和IPV6的TCP流。

AsyncUdpSocket是UDP/IP socket网络库,包装自CFSocket。它的工作很像TCP版本,只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操作,完整的委托支持,基于runloop,自包含的类,以及支持IPV4和IPV6。

使用AsyncSocket的时候可以做一层封装,根据需求提供几个接口出来。比如:连接、断开连接、发送消息等等。还有接受消息,接受到的消息可以通过通知、代理、block等传出去。

下面简单介绍一下对AsyncSocket使用.一般来说,一个用户只需要建立一个socket长连接,所以可以用单例类方便使用。

定义单列类:LGSocketServe

LGSocketServe.h

复制代码 代码如下:

//
//  LGSocketServe.h
//  AsyncSocketDemo
//

#import <Foundation/Foundation.h>
#import "AsyncSocket.h"

@interface LGSocketServe : NSObject<AsyncSocketDelegate>

+ (LGSocketServe *)sharedSocketServe;


@end


LGSocketServe.m
复制代码 代码如下:

//
//  LGSocketServe.m
//  AsyncSocketDemo
//

#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"];

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    Windows系统Linux系统苹果MACAndroidiOS系统鸿蒙系统