今天发现了一个iOS Socket 很好的学习材料
原文连接:
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的类
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
- \
- static classname *shared##classname = nil; \
- \
- + (classname *)shared##classname \
- { \
- @synchronized(self) \
- { \
- if (shared##classname == nil) \
- { \
- shared##classname = [[self alloc] init]; \
- } \
- } \
- \
- return shared##classname; \
- } \
- \
- + (id)allocWithZone:(NSZone *)zone \
- { \
- @synchronized(self) \
- { \
- if (shared##classname == nil) \
- { \
- shared##classname = [super allocWithZone:zone]; \
- return shared##classname; \
- } \
- } \
- \
- return nil; \
- } \
- \
- - (id)copyWithZone:(NSZone *)zone \
- { \
- return self; \
- } \
- \
- - (id)retain \
- { \
- return self; \
- } \
- \
- - (NSUInteger)retainCount \
- { \
- return NSUIntegerMax; \
- } \
- \
- - (void)release \
- { \
- } \
- \
- - (id)autorelease \
- { \
- return self; \
- }
Stream.h
- #import <Foundation/Foundation.h>
- #import <CFNetwork/CFNetwork.h>
- #import <SystemConfiguration/SystemConfiguration.h>
- #import <netinet/in.h>
- #import <arpa/inet.h>
-
- @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;
- @end
Stream.m
- #import "Stream.h"
- #import "SynthesizeSingleton.h"
-
- @implementation Stream
-
- SYNTHESIZE_SINGLETON_FOR_CLASS(Stream);
-
- -(void)startClient
- {
- _hasEstablished = NO;
- CFReadStreamRef readStream = NULL;
- CFWriteStreamRef writeStream = NULL;
- NSString *server = ;
-
-
-
- CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
- (CFStringRef)server,
- 1234,
- &readStream,
- &writeStream);
-
-
- if(readStream && writeStream)
- {
- inStream = (NSInputStream *)readStream;
- outStream = (NSOutputStream *)writeStream;
- }
- else
- {
-
- }
- }
-
- -(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];
-
- 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)
- {
- 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];
-
- }
- }
- else
- {
- int actuallyRead;
- uint8_t buffer[32768];
- if (!dataBuffer) {
- dataBuffer = [[NSMutableData alloc] init];
- }
-
- actuallyRead = [inStream read:buffer maxLength:sizeof(buffer)];
- if(actuallyRead == -1){
- [self closeStreams];
-
- }else if(actuallyRead == 0){
-
- }else{
- [dataBuffer appendBytes:buffer length:actuallyRead];
- remainingToRead -= actuallyRead;
- }
-
- if(remainingToRead == 0)
- {
- _isFirstFourBytes = YES;
- [self manageData:dataBuffer];
- [dataBuffer release];
- dataBuffer = nil;
- }
- }
- break;
- }
- case NSStreamEventEndEncountered:
- {
- [self closeStreams];
- break;
- }
- case NSStreamEventErrorOccurred:
- {
- if([[aStream streamError] code])
- {
- [self closeStreams];
- break;
- }
- }
- case NSStreamEventOpenCompleted:
- {
- _hasEstablished = YES;
- break;
- }
- case NSStreamEventHasSpaceAvailable:
- {
- break;
- }
- case NSStreamEventNone:
- default:
- break;
- }
- }
-
-
- -(BOOL)isServerAvailable{
- NSString *addressString = ;
- 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])
- {
-
- }
- else
- {
- if(inStream == nil || outStream == nil)
- {
- [[Stream sharedStream] startClient];
- [[Stream sharedStream] openStreams];
- _isFirstFourBytes = YES;
- }
-
- if(inStream != nil && outStream != nil)
- {
- _currentObject = currentObject;
- _numCondition = numCondition;
- if(_hasEstablished)
- {
- NSData *requestData = [requestString dataUsingEncoding:NSUTF8StringEncoding];
- int dataLength = [requestData length];
-
-
- uint8_t len[4];
- for(int i = 0;i<4;i++)
- {
- len[i] = (Byte)(dataLength>>8*(3-i)&0xff);
- }
- [/i]
-
- 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];
- else
- actuallyWritten = [outStream write:marker maxLength:32768];
-
- if ((actuallyWritten == -1) || (actuallyWritten == 0))
- {
- [self closeStreams];
-
- }
- else
- {
- remainingToWrite -= actuallyWritten;
- marker += actuallyWritten;
- }
- }
- else
- {
- break;
- }
- }
- }
- else
- {
-
- }
- }
- }
- }
-
- -(void)manageData:(NSData *)receivedData{
- [_currentObject getData:receivedData condition:_numCondition];
- }
-
- - (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;
}
}