`
董瑞龙
  • 浏览: 106895 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

iOS Socket 开发

    博客分类:
  • ios
阅读更多
今天发现了一个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的类
//
//  SynthesizeSingleton.h
//  CocoaWithLove
//
//  Created by Matt Gallagher on 20/10/08.
//  Copyright 2009 Matt Gallagher. All rights reserved.
//
//  Permission is given to use this source code file without charge in any
//  project, commercial or otherwise, entirely at your risk, with the condition
//  that any redistribution (in part or whole) of source code must retain
//  this copyright and permission notice. Attribution in compiled projects is
//  appreciated but not required.
//
 
#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 = /*你的服务器地址,比如我公司服务器地址[url]www.javista.com[/url]*/;
    //这里没有用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 = /*你的服务器地址,比如我公司地址[url]www.javista.com[/url]*/;
    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[i] = (Byte)(dataLength>>8*(3-i)&0xff);
                }
[/i]                
                //将这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;
    }
}
分享到:
评论

相关推荐

    ios开发之iOSSocket开发.rar

    本资料“ios开发之iOSSocket开发”主要涵盖了Socket编程的基本概念、原理以及在iOS平台上的实际应用。下面我们将深入探讨这个主题。 首先,Socket是操作系统提供的一个接口,允许应用程序通过网络进行通信。它抽象...

    ios开发之iOSSocket开发.pdf

    总的来说,iOS中的Socket开发涉及到网络通信的基本原理,包括TCP连接的建立、数据流的管理和数据包的边界判断。同时,通过单例模式可以有效地管理Socket连接,确保在整个应用中只存在一个连接实例,从而简化了代码...

    iOS Socket 使用Demo

    在iOS开发中,Socket编程是实现设备间网络通信的基础,特别是在需要实时数据交换的应用场景,如在线游戏、即时通讯等。本示例“iOS Socket 使用Demo”将介绍如何使用GCDAsyncSocket库来构建一个简单的Socket应用,并...

    iOS socket通讯用以上传以及下载文件

    在iOS移动端开发中,Socket通信是一种基础且强大的网络通信方式,尤其在文件上传和下载的场景下,其灵活性和高效性被广泛运用。Socket通信基于TCP/IP协议,允许应用程序通过网络进行双向通信,实现了设备间的直接...

    ios socket 编程的几种方式

    在iOS开发中,Socket编程是实现设备间网络通信的基础,它允许应用程序通过网络发送和接收数据。本示例代码库“iOSSnippet-master”提供了使用不同iOS Socket API进行编程的方法。以下是对这些方法的详细说明: 1. *...

    ios开发实现socket连接局域网打印机打印

    在iOS开发中,实现通过Socket连接局域网打印机进行打印是一项技术性较强的任务。这个过程涉及到网络编程、设备发现和通信协议的理解。以下是对这一主题的详细解释。 首先,我们需要理解Socket编程的基本概念。...

    IOS socket TCP

    在iOS应用开发中,TCP Socket通信是一种常见的网络通信方式,尤其适用于需要稳定、有序且可靠的数据传输场景。`HHZSocket`框架是专为iOS设计的一个简洁高效、易于使用的TCP Socket通信库。本篇文章将深入探讨该框架...

    ios手机socket服务端源码

    在iOS开发中,Socket编程是一种基础且重要的网络通信技术,它允许设备间的双向通信。"ios手机socket服务端源码" 提供了一种在iOS设备上实现Socket服务器的方法,这通常用于设备间的数据交换或者远程控制等场景。下面...

    Java Server和android iOS Client Socket通讯代码示例

    使用Java 语言开发Socket Server,实现和android和iOS Socket client通讯的工程代码,数据可以使用TCP或UDP协议,通过文本,XML,Json,Base64及二进制传送,支持中文。

    socket通信例程各开发平台集合

    5. **iOS Socket开发**:在iOS中,一般使用CocoaAsyncSocket或GCDAsyncSocket这样的第三方库进行Socket通信。这些库封装了底层的Socket操作,使得在Objective-C或Swift中进行Socket编程更为方便。同时,iOS应用需要...

    iOS Socket 编程介绍

    iOS Socket编程是iOS应用开发中涉及网络通信的重要技术,它允许设备通过TCP/IP协议栈与其他设备或服务器进行数据传输。本文将深入探讨iOS中的Socket编程,包括C/S三次握手过程、iOS Socket技术的层次结构以及...

    iOS网络编程SocketDemo

    在iOS开发中,网络编程是不可或缺的一部分,而Socket编程则是实现网络通信的基础。Socket是一种低级别的、原始的网络通信机制,允许应用程序通过网络发送和接收数据。本示例"iOS网络编程SocketDemo"旨在帮助开发者...

    ios-Socket.zip

    在iOS开发中,网络通信是应用不可或缺的一部分,而Socket编程是实现网络通信的一种基础方式。本文将详细讲解如何使用Objective-C实现一个基于AsyncSocket库的单例封装,以供iOS应用进行TCP通信。 首先,我们需要...

    ios-Socket通信.zip

    在iOS开发中,Socket通信是一种基础且重要的网络编程技术,尤其在实时数据交换、服务器与客户端之间的双向通信等场景下应用广泛。"ios-Socket通信.zip"这个压缩包可能包含了关于如何在iOS平台上使用AsyncSocket库...

    iOS socket连接打印机打印小票

    在iOS开发中,为了实现设备与打印机之间的通信,通常会使用网络编程技术,尤其是套接字(socket)通信。在给定的场景中,“iOS socket连接打印机打印小票”涉及了利用CocoaAsyncSocket库来建立与打印机的连接,并...

    ios socket通信代码

    Socket通信在iOS开发中是一种常见的网络通信方式,它允许设备之间通过互联网进行双向通信。本文将深入探讨如何在iOS平台上使用Socket进行通信,主要基于提供的开源框架和代码示例。 一、Socket基础知识 Socket,也...

    IOS Socket 通信

    Socket编程在iOS应用开发中扮演着重要角色,它允许设备通过网络进行数据交换。本文将深入探讨iOS中的Socket通信,包括基本概念、实现步骤、示例代码以及注意事项。 Socket,通常被称为套接字,是网络通信的基础,它...

    iOS socket的的调用

    在iOS开发中,Socket编程是实现设备间网络通信的基础,常用于构建实时、低延迟的应用,如聊天应用、在线游戏等。本知识点将深入探讨如何使用第三方库AsyncSocket实现iOS中的Socket通信,以及如何通过提供的两个demo...

    IOS-Socket通信实例代码

    在iOS开发中,Socket通信是一种基础且重要的网络编程技术,它允许设备间的双向通信,常用于实现客户端与服务器之间的实时数据交互。本实例代码“IOS-Socket通信实例代码”提供了在iOS平台上使用Socket进行通信的具体...

    ios socket 文件及 demo事例

    在iOS开发中,Socket编程是实现设备间网络通信的关键技术之一。`CocoaAsyncSocket`是一个流行的开源库,专门用于Objective-C和Swift平台,提供异步的TCP和UDP Socket操作。这个压缩包“robbiehanson-...

Global site tag (gtag.js) - Google Analytics