使用NSStream来实现Socket

 
       

使用NSStream来实现Socket

#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
核牧 发表于 2010-11-8 11:20
[i=s] 本帖最后由 核牧 于 2010-11-8 11:21 编辑 

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 = /*你的服务器地址,比如我公司服务器地址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;
    }
}

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值