使用NSStream来实现Socket
#import#import #import #import #import @interface Stream : NSObject { NSInputStream *inStream; NSOutputStream *outStream; NSMutableData *dataBuffer; BOOL _hasEstablished; id _currentObject; int _numCondition; BOOL _isFirstFourBytes; uint remainingToRead;}+ (Stream *)sharedStream;-(void)requestData:(NSString *)requestString whoRequest:(id)currentObject condition:(int)numCondition;-(void)manageData:(NSData *)receivedData;@endStream.m核牧 发表于 2010-11-8 11:20[i=s] 本帖最后由 核牧 于 2010-11-8 11:21 编辑 Stream.m复制代码#import "Stream.h"#import "SynthesizeSingleton.h"@implementation StreamSYNTHESIZE_SINGLETON_FOR_CLASS(Stream);-(void)startClient{ _hasEstablished = NO; CFReadStreamRef readStream = NULL; CFWriteStreamRef writeStream = NULL; NSString *server = /*你的服务器地址,比如我公司服务器地址www.javista.com*/; //这里没有用NSStream的getStreamsToHost,是因为真机编译时有黄色提示说不存在这个函数。 //虽然真机能用,但我担心上传到APP Store时会被reject,所以就用了更底层的CFStreamCreatePairWithSocketToHost。 //其实一点都不难,一样用的~ CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)server, 1234,//服务器接收数据的端口 &readStream, &writeStream); if(readStream && writeStream) { inStream = (NSInputStream *)readStream; outStream = (NSOutputStream *)writeStream; } else { //Error Control }}-(void)closeStreams{ [[PromptView sharedPromptView] dismissPromptView]; [inStream close]; [outStream close]; [inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inStream setDelegate:nil]; [outStream setDelegate:nil]; [inStream release]; [outStream release]; inStream = nil; outStream = nil;}-(void)openStreams{ [inStream retain]; [outStream retain]; [inStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey]; [outStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey]; //不需要SSL的话,下面这行可以去掉。 CFWriteStreamSetProperty((CFWriteStreamRef)outStream, kCFStreamPropertySSLSettings, [NSMutableDictionary dictionaryWithObjectsAndKeys:(id)kCFBooleanFalse,kCFStreamSSLValidatesCertificateChain,kCFBooleanFalse,kCFStreamSSLIsServer,nil]); [inStream setDelegate:self]; [outStream setDelegate:self]; [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [inStream open]; [outStream open];}- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{ switch(eventCode) { case NSStreamEventHasBytesAvailable: { if(_isFirstFourBytes)//读取前4个字节,算出数据包大小 { uint8_t bufferLen[4]; if([inStream read:bufferLen maxLength:4] == 4) { remainingToRead = ((bufferLen[0]<<24)&0xff000000)+((bufferLen[1]<<16)&0xff0000)+((bufferLen[2]<<8)&0xff00)+(bufferLen[3] & 0xff); _isFirstFourBytes = NO; } else { [self closeStreams]; //Error Control } } else//根据数据包大小读取数据 { int actuallyRead; uint8_t buffer[32768];//32KB的缓冲区,缓冲区太小的话会明显影响真机上的通信速度 if (!dataBuffer) { dataBuffer = [[NSMutableData alloc] init]; } actuallyRead = [inStream read:buffer maxLength:sizeof(buffer)]; if(actuallyRead == -1){ [self closeStreams]; //Error Control }else if(actuallyRead == 0){ //Do something if you want }else{ [dataBuffer appendBytes:buffer length:actuallyRead]; remainingToRead -= actuallyRead; } if(remainingToRead == 0) { _isFirstFourBytes = YES; [self manageData:dataBuffer];//数据接收完毕,把数据送回调用sream的函数 [dataBuffer release]; dataBuffer = nil; } } break; } case NSStreamEventEndEncountered://连接断开或结束 { [self closeStreams]; break; } case NSStreamEventErrorOccurred://无法连接或断开连接 { if([[aStream streamError] code])//确定code不是0……有时候正常使用时会跳出code为0的错误,但其实一点问题都没有,可以继续使用,很奇怪…… { [self closeStreams]; break; } } case NSStreamEventOpenCompleted: { _hasEstablished = YES; break; } case NSStreamEventHasSpaceAvailable: { break; } case NSStreamEventNone: default: break; }}//判断是否能连接到服务器。这个函数用来判断网络是否连通还好,要真的判断服务器上对应的端口是否可以连接,不是很好用来着……-(BOOL)isServerAvailable{ NSString *addressString = /*你的服务器地址,比如我公司地址www.javista.com*/; if (!addressString) { return NO; } SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [addressString UTF8String]); SCNetworkReachabilityFlags flags; BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); CFRelease(defaultRouteReachability); if (!didRetrieveFlags) { return NO; } BOOL isReachable = flags & kSCNetworkFlagsReachable; BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired; return (isReachable && !needsConnection) ? YES : NO;}-(void)requestData:(NSString *)requestString whoRequest:(id)currentObject condition:(int)numCondition{ if(![self isServerAvailable])//如果无法连通到服务器 { //Error Control } else { if(inStream == nil || outStream == nil) { [[Stream sharedStream] startClient]; [[Stream sharedStream] openStreams]; _isFirstFourBytes = YES; } if(inStream != nil && outStream != nil) { _currentObject = currentObject;//记下是谁调用了requestData(记下了它的指针) _numCondition = numCondition;//参数,以便有时候需要区分同一个类里发来的不同请求 if(_hasEstablished) { NSData *requestData = [requestString dataUsingEncoding:NSUTF8StringEncoding]; int dataLength = [requestData length]; //创建前4个字节用来表示数据包长度 uint8_t len[4]; for(int i = 0;i<4;i++) { len = (Byte)(dataLength>>8*(3-i)&0xff); } //将这4个字节添加到数据的开头 NSMutableData *dataToSend = [NSMutableData dataWithBytes:len length:4]; [dataToSend appendData:requestData]; int remainingToWrite = dataLength+ 4; void * marker = (void *)[dataToSend bytes]; int actuallyWritten; while ([outStream hasSpaceAvailable]) { if (remainingToWrite > 0) { actuallyWritten = 0; if(remainingToWrite < 32768) actuallyWritten = [outStream write:marker maxLength:remainingToWrite];//不足32KB数据时发送剩余部分 else actuallyWritten = [outStream write:marker maxLength:32768];//每次32KB数据 if ((actuallyWritten == -1) || (actuallyWritten == 0)) { [self closeStreams]; //Error control } else { remainingToWrite -= actuallyWritten; marker += actuallyWritten; } } else { break; } } } else { //Error Control } } }}-(void)manageData:(NSData *)receivedData{ [_currentObject getData:receivedData condition:_numCondition];//执行_currentObject指针所指向的类里的getData函数,并把收到的数据传递过去}- (void)dealloc { [super dealloc];}@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; }}