使用NSStream来实现Socket

来源:互联网 发布:abb码垛指令编程视频 编辑:程序博客网 时间:2024/05/17 07:33
原文连接:
http://www.cocoachina.com/bbs/read.php?tid=6146#


这个类使用了Singleton,因此永远只有一个实例。没有实例时会自动生成实例,可以在程序中的任何位置调用它。
一般来说,只要跟服务器建立一次连接即可,产生一对stream,分别是outStream和inStream,所有的数据都通过它们不断地发送和接收。
stream的end意味着连接中断,如果还需要访问服务器的话,得重新连接stream。(也就是重新实例化一下我这个类)
每次发送和接受的数据包大小需要自己控制,而不是等stream来告诉你这个数据包有多大,因为stream不会告诉你……
控制方法之一:通过添加一个特殊的后缀来判断,比如“<EOF>”,每次读到这个组合就认为数据读完。但是问题很明显,这个只能用于string。
控制方法之二:通过添加一个4字节的前缀来判断长度。这4个byte的byte[]数组,是当前数据包的长度信息,根据这个信息来读取一定长度的数据。
每次数据收完后,我用了一个取巧的方法来把数据返还给调用stream的函数……这个部分需要改进。

SynthesizeSingleton.h,实现singleton的类
C代码  收藏代码
  1. //  
  2. //  SynthesizeSingleton.h  
  3. //  CocoaWithLove  
  4. //  
  5. //  Created by Matt Gallagher on 20/10/08.  
  6. //  Copyright 2009 Matt Gallagher. All rights reserved.  
  7. //  
  8. //  Permission is given to use this source code file without charge in any  
  9. //  project, commercial or otherwise, entirely at your risk, with the condition  
  10. //  that any redistribution (in part or whole) of source code must retain  
  11. //  this copyright and permission notice. Attribution in compiled projects is  
  12. //  appreciated but not required.  
  13. //  
  14.    
  15. #define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \  
  16.  \  
  17. static classname *shared##classname = nil; \  
  18.  \  
  19. + (classname *)shared##classname \  
  20. { \  
  21.     @synchronized(self) \  
  22.     { \  
  23.         if (shared##classname == nil) \  
  24.         { \  
  25.             shared##classname = [[self alloc] init]; \  
  26.         } \  
  27.     } \  
  28.      \  
  29.     return shared##classname; \  
  30. } \  
  31.  \  
  32. + (id)allocWithZone:(NSZone *)zone \  
  33. { \  
  34.     @synchronized(self) \  
  35.     { \  
  36.         if (shared##classname == nil) \  
  37.         { \  
  38.             shared##classname = [super allocWithZone:zone]; \  
  39.             return shared##classname; \  
  40.         } \  
  41.     } \  
  42.      \  
  43.     return nil; \  
  44. } \  
  45.  \  
  46. - (id)copyWithZone:(NSZone *)zone \  
  47. { \  
  48.     return self; \  
  49. } \  
  50.  \  
  51. - (id)retain \  
  52. { \  
  53.     return self; \  
  54. } \  
  55.  \  
  56. - (NSUInteger)retainCount \  
  57. { \  
  58.     return NSUIntegerMax; \  
  59. } \  
  60.  \  
  61. - (void)release \  
  62. { \  
  63. } \  
  64.  \  
  65. - (id)autorelease \  
  66. { \  
  67.     return self; \  
  68. }  


Stream.h
C代码  收藏代码
  1. #import <Foundation/Foundation.h>    
  2. #import <CFNetwork/CFNetwork.h>  
  3. #import <SystemConfiguration/SystemConfiguration.h>  
  4. #import <netinet/in.h>  
  5. #import <arpa/inet.h>  
  6.    
  7. @interface Stream : NSObject {  
  8.     NSInputStream    *inStream;   
  9.     NSOutputStream    *outStream;   
  10.     NSMutableData    *dataBuffer;  
  11.    
  12.     BOOL            _hasEstablished;  
  13.     id                _currentObject;  
  14.     int                _numCondition;  
  15.    
  16.     BOOL            _isFirstFourBytes;  
  17.     uint            remainingToRead;  
  18. }  
  19.    
  20. + (Stream *)sharedStream;  
  21. -(void)requestData:(NSString *)requestString whoRequest:(id)currentObject condition:(int)numCondition;  
  22. -(void)manageData:(NSData *)receivedData;  
  23. @end  


Stream.m
C代码  收藏代码
  1. #import "Stream.h"  
  2. #import "SynthesizeSingleton.h"  
  3.    
  4. @implementation Stream  
  5.    
  6. SYNTHESIZE_SINGLETON_FOR_CLASS(Stream);  
  7.    
  8. -(void)startClient  
  9. {  
  10.     _hasEstablished = NO;  
  11.     CFReadStreamRef        readStream = NULL;  
  12.     CFWriteStreamRef    writeStream = NULL;  
  13.     NSString            *server = /*你的服务器地址,比如我公司服务器地址[url]www.javista.com[/url]*/;  
  14.     //这里没有用NSStream的getStreamsToHost,是因为真机编译时有黄色提示说不存在这个函数。  
  15.     //虽然真机能用,但我担心上传到APP Store时会被reject,所以就用了更底层的CFStreamCreatePairWithSocketToHost。  
  16.     //其实一点都不难,一样用的~  
  17.     CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,  
  18.                                        (CFStringRef)server,  
  19.                                        1234,//服务器接收数据的端口  
  20.                                        &readStream,  
  21.                                        &writeStream);  
  22.    
  23.    
  24.     if(readStream && writeStream)  
  25.     {  
  26.         inStream = (NSInputStream *)readStream;  
  27.         outStream = (NSOutputStream *)writeStream;  
  28.     }  
  29.     else  
  30.     {  
  31.         //Error Control  
  32.     }  
  33. }  
  34.    
  35. -(void)closeStreams{  
  36.     [[PromptView sharedPromptView] dismissPromptView];  
  37.     [inStream close];  
  38.     [outStream close];  
  39.     [inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  40.     [outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  41.     [inStream setDelegate:nil];  
  42.     [outStream setDelegate:nil];  
  43.     [inStream release];  
  44.     [outStream release];  
  45.     inStream = nil;  
  46.     outStream = nil;  
  47. }  
  48.    
  49. -(void)openStreams{  
  50.     [inStream retain];  
  51.     [outStream retain];  
  52.     [inStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey];  
  53.     [outStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey];  
  54.     //不需要SSL的话,下面这行可以去掉。  
  55.     CFWriteStreamSetProperty((CFWriteStreamRef)outStream, kCFStreamPropertySSLSettings, [NSMutableDictionary dictionaryWithObjectsAndKeys:(id)kCFBooleanFalse,kCFStreamSSLValidatesCertificateChain,kCFBooleanFalse,kCFStreamSSLIsServer,nil]);  
  56.     [inStream setDelegate:self];  
  57.     [outStream setDelegate:self];  
  58.     [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  59.     [outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  60.     [inStream open];  
  61.     [outStream open];  
  62. }  
  63.    
  64. - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode   
  65. {  
  66.     switch(eventCode) {  
  67.         case NSStreamEventHasBytesAvailable:  
  68.         {  
  69.             if(_isFirstFourBytes)//读取前4个字节,算出数据包大小  
  70.             {  
  71.                 uint8_t bufferLen[4];  
  72.                 if([inStream read:bufferLen maxLength:4] == 4)  
  73.                 {  
  74.                     remainingToRead = ((bufferLen[0]<<24)&0xff000000)+((bufferLen[1]<<16)&0xff0000)+((bufferLen[2]<<8)&0xff00)+(bufferLen[3] & 0xff);  
  75.                     _isFirstFourBytes = NO;  
  76.                 }  
  77.                 else  
  78.                 {  
  79.                     [self closeStreams];  
  80.                     //Error Control  
  81.                 }  
  82.             }  
  83.             else//根据数据包大小读取数据  
  84.             {  
  85.                 int actuallyRead;  
  86.                 uint8_t buffer[32768];//32KB的缓冲区,缓冲区太小的话会明显影响真机上的通信速度  
  87.                 if (!dataBuffer) {  
  88.                     dataBuffer = [[NSMutableData alloc] init];  
  89.                 }  
  90.    
  91.                 actuallyRead = [inStream read:buffer maxLength:sizeof(buffer)];  
  92.                 if(actuallyRead == -1){  
  93.                     [self closeStreams];  
  94.                     //Error Control  
  95.                 }else if(actuallyRead == 0){  
  96.                     //Do something if you want  
  97.                 }else{  
  98.                     [dataBuffer appendBytes:buffer length:actuallyRead];  
  99.                     remainingToRead -= actuallyRead;  
  100.                 }  
  101.    
  102.                 if(remainingToRead == 0)  
  103.                 {  
  104.                     _isFirstFourBytes = YES;  
  105.                     [self manageData:dataBuffer];//数据接收完毕,把数据送回调用sream的函数  
  106.                     [dataBuffer release];  
  107.                     dataBuffer = nil;  
  108.                 }  
  109.             }  
  110.             break;  
  111.         }  
  112.         case NSStreamEventEndEncountered://连接断开或结束  
  113.         {  
  114.             [self closeStreams];  
  115.             break;  
  116.         }  
  117.         case NSStreamEventErrorOccurred://无法连接或断开连接  
  118.         {  
  119.             if([[aStream streamError] code])//确定code不是0……有时候正常使用时会跳出code为0的错误,但其实一点问题都没有,可以继续使用,很奇怪……  
  120.             {  
  121.                 [self closeStreams];  
  122.                 break;  
  123.             }  
  124.         }  
  125.         case NSStreamEventOpenCompleted:  
  126.         {  
  127.             _hasEstablished = YES;  
  128.             break;  
  129.         }  
  130.         case NSStreamEventHasSpaceAvailable:  
  131.         {  
  132.             break;  
  133.         }  
  134.         case NSStreamEventNone:  
  135.         default:  
  136.             break;  
  137.     }  
  138. }  
  139.    
  140. //判断是否能连接到服务器。这个函数用来判断网络是否连通还好,要真的判断服务器上对应的端口是否可以连接,不是很好用来着……  
  141. -(BOOL)isServerAvailable{  
  142.     NSString *addressString = /*你的服务器地址,比如我公司地址[url]www.javista.com[/url]*/;  
  143.     if (!addressString) {  
  144.         return NO;  
  145.     }  
  146.    
  147.     SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [addressString UTF8String]);  
  148.     SCNetworkReachabilityFlags flags;  
  149.    
  150.     BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);  
  151.     CFRelease(defaultRouteReachability);  
  152.    
  153.     if (!didRetrieveFlags)  
  154.     {  
  155.         return NO;  
  156.     }  
  157.    
  158.     BOOL isReachable = flags & kSCNetworkFlagsReachable;  
  159.     BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;  
  160.     return (isReachable && !needsConnection) ? YES : NO;  
  161. }  
  162.    
  163. -(void)requestData:(NSString *)requestString whoRequest:(id)currentObject condition:(int)numCondition  
  164. {  
  165.     if(![self isServerAvailable])//如果无法连通到服务器  
  166.     {  
  167.         //Error Control  
  168.     }  
  169.     else  
  170.     {  
  171.         if(inStream == nil || outStream == nil)  
  172.         {  
  173.             [[Stream sharedStream] startClient];  
  174.             [[Stream sharedStream] openStreams];  
  175.             _isFirstFourBytes = YES;  
  176.         }  
  177.    
  178.         if(inStream != nil && outStream != nil)  
  179.         {  
  180.             _currentObject = currentObject;//记下是谁调用了requestData(记下了它的指针)  
  181.             _numCondition = numCondition;//参数,以便有时候需要区分同一个类里发来的不同请求  
  182.             if(_hasEstablished)  
  183.             {  
  184.                 NSData *requestData = [requestString dataUsingEncoding:NSUTF8StringEncoding];  
  185.                 int dataLength = [requestData length];  
  186.    
  187.                 //创建前4个字节用来表示数据包长度  
  188.                 uint8_t len[4];  
  189.                 for(int i = 0;i<4;i++)  
  190.                 {  
  191.                     len[i] = (Byte)(dataLength>>8*(3-i)&0xff);  
  192.                 }  
  193. [/i]                  
  194.                 //将这4个字节添加到数据的开头  
  195.                 NSMutableData *dataToSend = [NSMutableData dataWithBytes:len length:4];  
  196.                 [dataToSend appendData:requestData];  
  197.    
  198.                 int remainingToWrite = dataLength+ 4;  
  199.                 void * marker = (void *)[dataToSend bytes];  
  200.                 int actuallyWritten;  
  201.    
  202.                 while ([outStream hasSpaceAvailable]) {  
  203.                     if (remainingToWrite > 0) {  
  204.                         actuallyWritten = 0;  
  205.    
  206.                         if(remainingToWrite < 32768)  
  207.                             actuallyWritten = [outStream write:marker maxLength:remainingToWrite];//不足32KB数据时发送剩余部分  
  208.                         else  
  209.                             actuallyWritten = [outStream write:marker maxLength:32768];//每次32KB数据  
  210.    
  211.                         if ((actuallyWritten == -1) || (actuallyWritten == 0))   
  212.                         {  
  213.                             [self closeStreams];  
  214.                             //Error control  
  215.                         }  
  216.                         else  
  217.                         {  
  218.                             remainingToWrite -= actuallyWritten;  
  219.                             marker += actuallyWritten;                        
  220.                         }  
  221.                     }  
  222.                     else  
  223.                     {  
  224.                         break;  
  225.                     }  
  226.                 }  
  227.             }  
  228.             else  
  229.             {  
  230.                 //Error Control  
  231.             }  
  232.         }  
  233.     }  
  234. }  
  235.    
  236. -(void)manageData:(NSData *)receivedData{  
  237.     [_currentObject getData:receivedData condition:_numCondition];//执行_currentObject指针所指向的类里的getData函数,并把收到的数据传递过去  
  238. }  
  239.    
  240. - (void)dealloc {  
  241.     [super dealloc];  
  242. }  
  243.    
  244. @end  


用的时候,在调用stream的类的头文件里#import这个Stream.h,并添加一个函数叫- (void)getData:(NSData *)receivedData condition:(int)numCondition;
发送时:

[[Stream SharedStream] requestData:@"login"/*需要发送的命令*/ whoRequest:self/*把自己的指针传递过去*/ condition:0/*用以区分不同功能的请求*/];


接收完毕后Stream会调用这个类里的getData函数,这个函数写法如下:

- (void)getData:(NSData *)receivedData condition:(int)numCondition{
    switch(numCondition)
    {
        case 0:
            //Do something
            break;
        case 1:
            //Do something different
            break;
        default:
            break;
    }
}
原创粉丝点击