iOS应用中使用AsyncSocket库处理Socket通信的用法讲解

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

(0)

相关推荐

  • IOS开发网络篇—Socket编程详解

    一.网络各个协议:TCP/IP.SOCKET.HTTP等 网络七层由下往上分别为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 其中物理层.数据链路层和网络层通常被称作媒体层,是网络工程师所研究的对象: 传输层.会话层.表示层和应用层则被称作主机层,是用户所面向和关心的内容. http协议对应于应用层 tcp协议对应于传输层 ip协议对应于网络层 三者本质上没有可比性.  何况HTTP协议是基于TCP连接的. TCP/IP是传输层协议,主要解决数据如何在网络中传输:而HTTP是应用

  • IOS中使用 CocoaAsyncSocket​

    Socket 如今在 iOS 里对 Socket 的应用慢慢多了起来,就一个即时通讯,很多应用都有集成,那即时通讯功能一般来说就肯定是基于 Socket 的,Socket 这个话题我一直谈论的比较少,一是由于看过一些 C/C++ 操作 Socket 的代码被吓到了一直还有心理阴影存在,二是工作里没怎么遇到相关的需求应用,所以也没有去深入研究相关内容. 还没接触过 Socket 编程肯定也会听说过这几个关键字:包.握手.连接.TCP.UDP 等等,Socket 编程的文章网上大把大把有,而作为一个

  • IOS 详解socket编程[oc]粘包、半包处理

    IOS 详解socket编程[oc]粘包.半包处理 在做socket编程时,如果是做tcp连接,那就不可避免的会遇到粘包与半包的问题,粘包就是多组数据被一并接收了,粘在了一起,无法做划分:半包就是有数据接收不完整,无法处理.要解决粘包.半包的问题,一般在设计数据(消息)格式时会约定好一个字段专门用于描述数据包的长度,这样就使数据有了边界,依靠这个边界,就能把每组数据划分出来,数据不完整时也能获知数据的缺失. (当然也可以把数据设计成定长数据,但这样不够灵活:或者用\n,\r这类字符作为数据划分依

  • iOS 使用 socket 实现即时通信示例(非第三方库)

    其实写这个socket一开始我是拒绝的. 因为大家学C 语言和linux基础时肯定都有接触,客户端和服务端的通信也都了解过,加上现在很多开放的第三方库都不需要我们来操作底层的通信. 但是!还是想写.底层的东西最好了解下. 效果 由于5M的上传限制GIF可能看不清 我再截两张图吧 服务器 客户端A 客户端B 模型图 分析 由上图可以了解到服务器和客户端需要做哪些工作 服务器 抽象一点分为: 1.创建线程等待接收客户端的连接 2.接收并解析客户端发来的消息 3.给客户端发送消息 具体一点: 1.创建

  • iOS App通信之local socket示例

    之前看到一篇文章介绍到App之间的五种通信方式,它分别有URL Scheme,Keychain,UIPastedboard,UIDocumentInteractionController以及利用socket进行本地通信.前面4种都有用到过,也相对比较简单,几行代码的事.对于最后一种之前一直没用到过(原谅我还是个小白),所以今天试着写了下,这儿记录在这里和大家分享. 好了,废话不多说,开始: 首先,说下它的原理,其实很简单,一个App在本地的端口进行TCP的bind和listen,另外一个App在

  • iOS App之间的通信 local socket

    之前看到一篇文章介绍到App之间的五种通信方式,它分别有URL Scheme,Keychain,UIPastedboard,UIDocumentInteractionController以及利用socket进行本地通信.前面4种都有用到过,也相对比较简单,几行代码的事.对于最后一种之前一直没用到过(原谅我还是个小白),所以今天试着写了下,这儿记录在这里和大家分享. 好了,废话不多说,开始: 首先,说下它的原理,其实很简单,一个App在本地的端口进行TCP的bind和listen,另外一个App在

  • iOS开发项目- 基于WebSocket的聊天通讯(2)

    公司项目需要开发一个类似QQ.微信的即时IM聊天功能,做到实时监控消息,需要用的技术是websocket,今天整理下语言聊天这块:其实语言聊天,包含两部分,录音和音乐播放,关于简单语言聊天功能如下图: 录音 在AVFoundation框架中有一个AVAudioRecorder类专门处理录音操作,它同样支持多种音频格式.与AVAudioPlayer类似,你完全可以将它看成是一个录音机控制类,下面是常用的属性和方法: 先来了解下AVAudioRecorder的常用属性: @property (rea

  • iOS开发项目- 基于WebSocket的聊天通讯(1)

    公司项目需要开发一个类似QQ.微信的即时IM聊天功能,做到实时监控消息,需要用的技术是websocket. 概述WebSocket: 1.1 为什么我们需要WebSocket这样的实时的通信协议? WebSocket是web通信方式的一种,像我们熟知的HTTP协议也是web通信方式的一种.但是我们知道HTTP协议是一种无状态的协议,其服务端本身不具备识别客户端的能力,必须借助外部的一些信息比如说session和cookie,才能与特定的客户端保持通信.也就是说我们所发送的每一个HTTP的请求都会

  • iOS应用中使用AsyncSocket库处理Socket通信的用法讲解

    用socket可以实现像QQ那样发送即时消息的功能.客户端和服务端需要建立长连接,在长连接的情况下,发送消息.客户端可以发送心跳包来检测长连接. 在iOS开发中使用socket,一般都是用第三方库AsyncSocket,不得不承认这个库确实很强大.下载地址CocoaAsyncSocket . 特性 AsyncSocket类是支持TCP的. AsyncUdpSocket是支持UDP的. AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库.它提供了异步操

  • iOS 泛型中nullable、null resettable、null kindof 用法详解

    iOS9新出的关键字:用来修饰属性,或者方法的参数,方法的返回值 iOS9新出关键字nonnull,nullable,null_resettable,_Null_unspecified 需要注意的一点只能修饰对象,不能修饰基本数据类型. 虽然在项目的代码编写中不会经常用到,不过在调用苹果系统方法的时候还是会经常遇到,需要做一个总结 nullable作用:表示可以为空 nullable书写规范: // 方式一: @property (nonatomic, strong, nullable) NSS

  • iOS开发中使用SQL语句操作数据库的基本用法指南

    SQL代码应用示例 一.使用代码的方式批量添加(导入)数据到数据库中 1.执行SQL语句在数据库中添加一条信息 插入一条数据的sql语句: 点击run执行语句之后,刷新数据 2.在ios项目中使用代码批量添加多行数据示例 代码示例: 复制代码 代码如下: // //  main.m //  01-为数据库添加多行数据 // //  Created by apple on 14-7-26. //  Copyright (c) 2014年 wendingding. All rights reserv

  • iOS开发中实现一个简单的图片浏览器的实例讲解

    一.程序实现要求 1.要求 2. 界面分析 (1) 需要读取或修改属性的控件需要设置属性 序号标签 图片 图片描述 左边按钮 右边按钮 (2) 需要监听响应事件的对象,需要添加监听方法 左边按钮 右边按钮 二.实现基本功能的程序 复制代码 代码如下: // //  YYViewController.m //  03-图片浏览器初步 // //  Created by apple on 14-5-21. //  Copyright (c) 2014年 itcase. All rights rese

  • iOS应用开发中的文字选中操作控件UITextView用法讲解

    1.创建并初始化 创建UITextView的文件,并在.h文件中写入如下代码: 复制代码 代码如下: #import <UIKit/UIKit.h>        @interface TextViewController : UIViewController <UITextViewDelegate>    {                  UITextView *textView;    }        @property (nonatomic, retain) UITex

  • iOS开发中如何优雅的调试数据库详解

    背景 写代码难免出现bug. 储备些调试技能绝对能够提高你的工作效率,让bug无所遁形.相信大家应该都有所体会,我们在开发的时候,数据库的操作一直是一个很棘手的问题,后来发现Android下面有一个第三方的库还挺好用的,就模仿它搞了个iOS的,可以方便的通过浏览器查看.添加.删除.修改数据库.下面话不多说了,来一看看详细的介绍吧. 历史状况 我们来回想一下调试的过程: 如果在模拟器中调试: 找到模拟器应用中数据库的文件位置 拷回到一个比较方便打开的地方 安装一个数据库操作软件 打开数据库文件 s

  • Java Web项目中使用Socket通信多线程、长连接的方法

    很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序启动时,启动socket监听.我们的应用场景是在java项目中,需要外接如一个硬件设备,通过tcp通信,获取设备传上来的数据,并对数据做回应. 先看一下web的监听代码: import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class

  • IOS开发中加载大量网络图片优化方法

    IOS开发中加载大量网络图片如何优化 1.概述 在IOS下通过URL读一张网络图片并不像其他编程语言那样可以直接把图片路径放到图片路径的位置就ok,而是需要我们通过一段类似流的方式去加载网络图片,接着才能把图片放入图片路径显示.比如: -(UIImage *) getImageFromURL:(NSString *)fileURL { //NSLog(@"执行图片下载函数"); UIImage * result; NSData * data = [NSData dataWithCont

  • iOS创建与使用静态库

    在日常项目开发中,不论是为了两个公司项目上的业务交流还是为了减少项目的编译时间,有的时候我们会把项目中的私密内容打包成静态库,或者是把项目中变动较少一部分打包成静态库以便提高编译效率,那么下面我们就来学习一下"iOS-静态库的创建与使用": (一)iOS静态库.动态库与Framework静态库与动态库的区别 (1)什么是库? 库(Library)直白一点说就是一段编译好的二进制代码,加上头文件就可以供别人使用;(例如: iOS中Objective-C编译下的.h和.m文件,打包静态库后

  • IOS开发中异步网络请求上实现同步逻辑

    IOS开发中异步网络请求上实现同步逻辑 前提: 可能遇到一些问题,比如上传多个数据,需要等多个数据上传成功后做一定的处理,而且一个个上传,万一哪个上传失败了,后面就不需要上传了,直接报错. 之前ASI的网络库中是有同步请求的接口,所以很好处理,AFNetwork的网络库只有异步的网络请求,该怎么实现呢? 1.循环异步拼组 - (void)uploadFile:(NSArray *)imageArray atIndex:(NSInteger)index imagesCount:(NSInteger

随机推荐