苹果的Socket编程,常用的是CFNetwork库编程,Socket编程是一个异步的过程,socket的联接方法,不是会等到连接完成后再执行下一行代码,如果你想在一个连接完全执行完之前与一个Socket完全绑定,那么就会容易得到一个意想不到的结果。
连接一台Socket服务器你需要确定两个信息,一个是Socket服务器的域名或者IP地址,另一个是服务器监听的端口号.在尝试一个新的Socket连接的时候我们最好先添加一个连接事件监听器.
CFNetwork库编程,其封装好的开源库是 cocoa AsyncSocket库,它在调用读和写方法完成后,会返回通知,并且能自动地进行socket接收和为每个连接启动新的实例,并且简化了一些流程。
代码如下:
1 //客户端 2 #import "AsyncSocket" 3 @interface HelloIphoneViewController:UIViewController{ 4 UITextField *textField; 5 AsyncSocket *asyncSocket; 6 } 7 @property(retain,nonatomic)IBOutlet UITextField *textFiled; 8 -(IBAction)buttonPressed:(id)sender; 9 -(IBAction)textFiledDoneEditing:(id)sender;
//这里用port=0是让系统自动随机找一个空闲端口。其他都是基于c风格对系统函数的直接调用。
//从Socket服务器读数据
对于Socket实例,先收到NSData事件后,然后调用如下两个方法一个,如radByte()或者readInt()什么,在事件控制器确定不会去读过去的bytesAvailable,socket是一个异步的过程,那么我们就不能在socekt一连接,就马上去读数据,而是要等到客户端从服务器载入全部的数据在去读取数据,否者在数据可用之前读数据会产生错误,因此,我们可以通过NSData事件广播到Socket实例,这样就可以知道什么时候,可以读取数据。在需要连接的地方使用connectToHost联接服务器,代码如下:
1 asyncSocket=[[AsyncSocket alloc] initWithDelegate:self]; 2 NSError *err=nil; 3 if(![asyncSocket connectToHost on:port error:&err]){ 4 NSLog(@"Error:%@",err); 5 } 6 NSD
NSData拥有id和length两个属性的数据空间
1 //NSString 转成NSData 2 NSData *xmlData=[@"data" dataUsingEncoding:NSUTF8StringEncoding]; 3 //NSData转换成NSString 4 NSData *data; 5 NSString *result=[[NSString alloc] initWithData:data encoding:NSUTR8StringEncoding];
通过AsyncSocket:writeDate来发送数据
1 -(void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 2 NSData *aData=[@"tes" dataUsingEncoding:NSUTR8StringEncoding]; 3 [socke writeData:aData withTimeout -1 tag:1];
通过重载来处理发送的数据
1 -(void)onSocket:(AsyncSocet *)sock didWriteDataWithTag:(long)tag 2 {{ 3 NSLog(@"thread(%),onSocket%p didWriteDataWithTag:%d",[[NSThread currentThread] name] ,sock,tag); 4 }
接收Socket数据,在onSocket重载函数
1 -(void)onSocket:(AsyncSocet *)sock didReadData:(NSData *)data withTag:(long)tag;
当收到的数据为ASCII编码,你可以通过方法重新构建一个字符串.
1 NSString *aStr=[[NSString alloc] initWithData:data encoding:NSUTR8StringEncoding]; 2 NSLog(@"==%@",aStr); 3 [aStr release]; 4 TCP连接读取制定长度的数据 5 socket可以读取固定长度的字节 6 [socket readdataToLength:withTimeout:tag]; 7 //============================================
部分方法的解析
1 -(void)onSocket:(AsyncSocket *)sock willDisconnectError:(NSError *)err
发生错误,socket关闭,可以在call-back过程调用"unreadData"去取得socket的最后的数据字节,当连接的时候,该委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前调用;
1 -(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket(AsyncSocket *)didAcceptNewSocket;
当socket由于或没有错误而断开连接,如果你想要在断开连接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 释放则不安全;
1 -(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket(AsyncSocket *)didAcceptNewSocket
当产生一个socket去处理连接时调用,此方法会返回 线程上的run-loop 的新的socket和其应处理的委托,
如果省略,则使用[NSRunLoop cunrrentRunLoop];
1 -(void)onSocket(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
当一个socket写入一些数据,但还没有完成整个写入时调用,它可以用来更新进度条等东西;
1 -(NSTimeInterval)onSocket(AsyncSocket *)sock shouldTimeoutreadWithTag:(long)tag elapsect(NSTimeInterval)
2 exapsed bytesDone:(NSUInteger)length
使用读操作已超时但还没完成时调用,此方法允许随意延迟超时,如果返回一个正的时间间隔,
读取的超时将有一定量的扩展,如果不实现这个方法,或会像往常一样返回一个负的时间间隔,elapsed参数是 原超时的总和,加上先前通过这种方法添加的任何补充, length参数是
读操作到目前为止已读取的字节数, 注意,如果返回正数的话,这个方法可能被一个单独的读取多次调用.
当传入的连接被接受,AsyncSocket调用多个委托方法,这些方法按时间:
1、onSocket:didAcceptNewSocket; 2、onSocket:wantsRunLoopForNewSocket; 3、onSocketWillConnect
-(BOOL)acceptOnPort(UInit16)port error:(NSError *)errptr;
告诉socket开始听取和接受指定端口上的连接,当一个连接到来的时候,AsyncSocket实例将调用各种委托方法,socket将听取所有可用的接口(wifi,以太网等)
-(BOOL)connectToHost(NSString *)hostname onPort(UInit16) 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()