AsyncSocket学习

iOS网络编程的标准推荐是CFNetwork 库,Cocoa AsyncSocket开源库对其进行了很好封装 ,用它来简化CFNetwork的调用,它提供了多线程异步操作

主要特性

通过队列实现非阻塞的读和写,而且可选超时。调用它读取和写入,操作完成后,通过协议回调。

socket自动接收。它将为每个连接启动新的实例,也可以立即关闭这些连接。

委托(delegate)支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接,都可以通过委托模式调用。

基于run loop的,而不是线程的。虽然可以在主线程或者工作线程中使用它,但你不需要这样做。它异步的调用委托方法,使用NSRunLoop。委托方法包括socket的参数,可让你在多个实例中区分

只有一个类。你无需操作流或者socket,这个类帮你做了全部
支持基于IPV4和IPV6的TCP流

使用:简单的通讯客户端

创建工程,导入:AsynSocket.h .m与AsynUdpSocket.h .m四个文件 及CFNetwork.framework。

TCP客户端

#import "AsyncSocket.h"

@interface HelloiPhoneViewController : UIViewController {
UITextField    * textField;
AsyncSocket * asyncSocket;
}

@property (retain, nonatomic) IBOutlet UITextField *textField;

- (IBAction) buttonPressed: (id)sender;
- (IBAction) textFieldDoneEditing: (id)sender;

@end

在需要联接地方使用connectToHost联接服务器

其中initWithDelegate的参数中self是必须。这个对象指针中的各个Socket响应的函数将被ASyncSocket所调用.initWithDelegate把将当前对象传递进去,这样只要在当前对象方法实现相应方法

asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
if(![asyncSocket connectToHost:host on:port error:&err])
{
    NSLog(@"Error: %@", err);
}

NSData

无论SOCKET收发都采用NSData对象。
NSData主要是带一个(id)data指向的数据空间和长度 length。将NSString 转换成NSData 对象:

NSData* xmlData = [@"testdata" dataUsingEncoding:NSUTF8StringEncoding];

NSData 转换成NSString对象

NSData * data;
NSString *result = [[NSString alloc] initWithData:data         encoding:NSUTF8StringEncoding];

发送数据

通过AsyncSocket::writeData方法来发送数据

- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

例如:

NSData* aData= [@"test data" dataUsingEncoding: NSUTF8StringEncoding];
[sock writeData:aData withTimeout:-1 tag:1];

在onSocket重载函数中,采用专门用来处理SOCKET的发送数据的方法

-(void)onSocket(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"thread(%),onSocket:%p didWriteDataWithTag:%d",[[NSThread currentThread] name],sock,tag);
}

接收Socket数据

在onSocket重载函数中,用是专门用来处理SOCKET的接收数据的

-(void) onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

在中间将其转换成NSString进行显示

NSString* aStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"===%@",aStr);

TCP连接读取指定长度的数据

socket连接,可能会读取固定长度的字节

[socket readDataToLength: withTimeout :tag]

各方法的说明

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

发生错误,socket关闭,可以在call-back过程调用”unreadData”去取得socket的最后的数据字节,当连接的时候,该委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前调用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

当socket由于或没有错误而断开连接,如果你想要在断开连接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 释放则不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

当产生一个socket去处理连接时调用,此方法会返回 线程上的run-loop 的新的socket和其应处理的委托,如果省略,则使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

当socket正准备读和写的时候调用,host属性是一个IP地址,而不是一个DNS 名称

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

当socket已完成所要求的数据读入内存时调用,如果有错误则不调用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

当一个socket读取数据,但尚未完成读操作的时候调用,如果使用 readToData: or readToLength: 方法 会发生,可以被用来更新进度条等东西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

当一个socket已完成请求数据的写入时候调用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

当一个socket写入一些数据,但还没有完成整个写入时调用,它可以用来更新进度条等东西。

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用读操作已超时,但还没完成时调用,此方法允许随意延迟超时。如果返回一个正的时间间隔,读取的超时将有一定量的扩展;如果不实现这个方法,或返回一个负的时间间隔。elapsed是指原超时的总和,加上先前通过这种方法添加的任何补充;length是指读操作到目前为止已读取的字节数。 注意,如果返回正数的话,这个方法可能被一个单独的读取多次调用。

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一个写操作已达到其超时但还没完成时调用,同上。

-(void)onSocketDidSecure:(AsyncSocket *)sock;

当socket需要ssl/tls时,调用startTLS方法,成功后调用。如果ssl/tls是无效的证书,socket将会立即关闭,onSocket:willDisconnectWithError:代理方法会返回ssl错误代码。

-(BOOL)canSafelySetDelegate

改变委托之前,需查看当前的委托是否有未完成的操作(读/写),应在安全连接或接受委托之前改变委托。一旦接收或连接方法之一被调用,AsyncSocket实例会被锁定,其他接收/连接方法在没有先断开socket不会被调用。如果尝试失败或超时,这些方法要么返回NO, 要么调用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect。当传入的连接被接受,AsyncSocket调用多个委托方法。这些方法调用顺序:
1. onSocket:didAcceptNewSocket:
2. onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:

服务器端代码需要保留公认的socket,onSocket:didAcceptNewSocket:方法可以做到这一点。在配置好的socket尽心读和写流是,onSocket:didConnectToHost:port 方法将被循环调用。
在使用多线程时需注意,如果要想通过onSocket:wantsRunLoopForNewSocket:方法,移动一个socket去到另一个循环的socket,应该在调用读和写或者startTLS方法前,等待onSocket:didConnectToHost:port:方法被调用。否则读和写在不正确的runloop中,可能导致数据错误。

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

socket听取和接受端口上的连接,当一个连接到来的时候,AsyncSocket实例将调用各种委托方法。socket将听取所有可用的接口(wifi,以太网等)。

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

连接给定的主机和端口,主机hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

连接到一个给定的地址,制定一个sockaddr结构,包含一个NSData对象,例如,NSData对象从NSNetService的地址方法返回,如果有一个现有的sockaddr结构,可以将它转换到一个NSData对象,像这样:

struct sockaddr sa  -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
-(void)disconnect;

立即断开,任何未处理的读或写都将被丢弃。如果socket还没有断开,在这个方法返回之前,onSocketDidDisconnect 委托方法将会被立即调用。注意推荐释放AsyncSocket实例的方式:

[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
-(void)disconnectAfterReading;

在已经完成了所有进行中的读取后断开,在调用之后,新的读取和写入方法将无用,即使仍有待写入,socket也会断开 。

- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;

返回当前连接的socket的本地和远程主机和端口,如果没有连接会返回nil或0,主机将会是一个IP地址。

-NSData *)connectedAddress
-NSData *)localAddresss

返回当前连接的socket的本地和远程的地址,NSData对象是socketaddr结构包裹。

readData和writeData方法不是block(它们是异步的)。当读完成后,委托方法onSocket:didReadData:withTag: 被调用;当写完成后,委托方法onSocket:didWriteDataWithTag: 被调用。可以选择任何读/写操作的超时设置(使用负时间间隔,表示一直等待)。如果读/写操作超时,相应的 onSocket:shouldTimeout…委托方法被调用,允许我们去延长超时。
超时后,onSocket:willDisconnectWithError: 方法被调用,紧接着是 onSocketDidDisconnect方法。tag是为了方便,可以使用它作为数组的索引、步数、state id 、指针等。

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

读取socket上第一次成为可用的字节,如果timeout值是负数的,读操作将不使用timeout。

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

读取socket上第一次成为可用的字节,字节将被追加到给定的字节缓冲区。从给定的偏移量开始,如果需要,给定的缓冲区大小将会自动增加。如果timeout值是负数的,读操作将不使用timeout。如果缓冲区为空,socket会为我们创建一个缓冲区。如果bufferOffset是大于给定的缓冲区的长度,该方法将无用,委托将不会被调用。如果你传递一个缓冲区,当AsyncSocket在使用它的时候你不能以任何方式改变它。完成之后,onSocket:didReadData:withTag 返回的数据将是一个给定的缓冲区的子集,也就是说,它将会被引用到被追加的给定的缓冲区的字节。

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

读取给定的字节数,如果length为0,方法将无用,委托将不会被调用。

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

读取给定的字节数,在给定的偏移开始,字节将被追加到给定的字节缓冲区。

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

读取字节直到(包括)传入的作为分隔的”data”参数。如果”data”参数传递0或者0长度的数据,该方法将无用,委托将不会被调用。
从socket读取一行,使用”data”参数作为行的分隔符 (如HTTP的CRLF)。注意,此方法不是字符集,因此,如果一个分隔符出现,它自然可以作为进行编码的一部分,读取将提前结束。

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

读取字节直到(包括)传入的作为分隔的“data”参数,从给定的偏移量开始,字节将被追加到给定的字节缓冲区。从socket读取一行,使用”data”参数作为行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

将data写入socket,当完成的时候委托被调用。

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回当前读或写的进度,从0.0 到 1.0 或者 如果没有读/写的时候返回Nan(使用isNan来检查)。tag、done、total如果不为空的话,它们将会被填补。

- (void)startTLS:(NSDictionary *)tlsSettings;

确保使用ssl/tls连接。这方法可被随时调用,tls握手将会等待所有的读/写完成之后进行。在这个方法被调用后,任何读写计划 将会发生在安全链接。对于tlsSettings参数可能的keys和TLS设置的值:
* kCFStreamSSLLevel
* kCFStreamSSLAllowsExpiredCertificates
* kCFStreamSSLAllowsExpiredRoots
* kCFStreamSSLAllowsAnyRoot
* kCFStreamSSLValidatesCertificateChain
* kCFStreamSSLPeerName
* kCFStreamSSLCertificates
* kCFStreamSSLIsServer
如果你传递空或者空字典,将使用默认的字典。默认设置将检查以确保由签署可信的第三方证书机构和没有过期的远程连接的证书。然而,它不会验证证书上的名字,除非你给它一个名字,通过kCFStreamSSLPeerName键去验证。这是很重要安全的影响因素,想象一下你正试图创建一个到MySecureServer.com的安全连接,但因为一个被攻击的DNS服务器,你的socket被定向到MaliciousServer.com,如果你只是使用默认设置,MaliciousServer.com 有一个有效的证书,默认设置将无法监测到任何问题,因为证书是有效的。在这个特殊的情况下,要妥善保护你的连接,应设置kCFStreamSSLPeerName性质为MySecureServer.com。
如果事前你不知道对等的名字的远程主机(例如,你不确认它是domain.com” or “www.domain.com”),那么你可以使用默认设置来验证证书,然后在获得验证的发行后,使用X509Certificate类来验证,X509Certificate类是CocoaAsyncSocket开源项目的一部分。

-(void)enablePrebuffering

对于处理readDataToData请求,数据是必须从socket以小增量的方式读取出来的,通过允许AsyncSocket一次性读大块数据和存储任何一个小的内部缓冲区溢出来大大提高性能。这被称为预缓冲,一些数据在你获取它之前就可能已被读取出来。如果你经常使用readDataToData,使用预缓冲会有更好的性能,尤其是在iphone上,默认的预缓冲状态是由DEFAULT_PREBUFFERING 定义控制的,强烈建议设置其为yes。由于可能存在些不可预见的因素,预缓冲默认是禁用的。

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

当你创建一个AsyncSocket,它被添加到当前线程runloop。对于手动创建的socket,在线程上你打算使用它,创建起来也非常简单。当一个新的socket被创建,委托方法 onSocket:wantsRunLoopForNewSocket 会被调用,允许你在一个单独的线程上放置socket,这个工作最好结合在同一个线程池设计。但是,如果在一个单独的线程上,你需要移动一个socket,这个方法可以用来完成任务。此方法必须从当前运行的线程/runloop的socket 调用。
注意:此方法调用后,所有进一步的方法应该从给定的runloop上调用这个对象,此外,所有委托调用将会发送到给定的runloop。

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允许你配置 socket 使用的 运行循环模式。运行循环模式设置默认是NSRunLoopCommonModes,你可能需要添加模式 :

  • NSModalPanelRunLoopMode;
  • NSEventTrackingRunLoopMode;
  • NSRunLoopCommonModes;

当修改模式时,socket将自动继承相同的运行循环模式。

-(NSArray *)runLoopModes

返回当前正在运行的AsyncSocket实例的循环模式。

-(NSData *)unreadData;

当前socket上未读去的数据。当一个错误的事件发生,方法 onSocket:willDisconnectWithError: 将会被调用,并去读取留在socket上的任何数据。

+ (NSData *)CRLFData;   // 0x0D0A
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值