ios开发 网络编程浅析

时间:2022-12-15 08:16:46

原文地址:http://www.cnblogs.com/hanjun/archive/2012/11/16/2774107.html

iphone包含了很多框架和库,从底层的套接字到不同层次的封装,可以方便地给程序添加网络功能。

    (1)BSD套接字。最底层的套接字,这是Unix网络开发常用的API。如果从其他系统移植程序,而程序用的是BSD套接字,那么网络部分可以继续使用这些API。

    (2)CFNetwork framework 。CFNetwork 也是比较底层的, 是对BSD套接字的一个扩展 。它是一个C语言的库,它是基于BSD套接字,提供了对网络协议的抽象。这些抽象使得用户更容易地操作套接字、处理网络的各种连接。。它集成了run-loop,因此使用CFNetwork不用自己去实现事件循环。CFNetwork 还包括了一些网络协议(如HTTP、FTP)的实现,可以在不了解这些协议细节的情况下直接使用。

    (3) Foundation framework是基于Objectice-C语言的库,它为CFNetwork API 提供了面向对象的抽象。

    (4)CFURL(C)/NSURL(Objective-C)是比较高层的API,它们提供了从Web和FTP服务器下载文件或其他资源的一种简单的方法。其中,CFURL是CFNetwork的一部分,NSURL是Foundation的一部分。

     (5)CFNetServices/NSNetServices提供了使用Bonjour注册和发现网络服务的方法。

      总之,CFNetwork framework 和 Foundation framework是最为强大的两个库,它们是比较底层、高效,提供的接口也非常丰富。

 

 

套接字     套接字是网络通信的基本构件,提供了不同主机间进程双向通信的端点。如果电话,只有当一方拨通另一方时,双方才能建立对话。通过套接字编程,程序可以跳过复杂的网络底层协议和结构,直接编制与平台无关的应用程序。目前,套接字已逐渐成为网络编程的通用接口。                                                                                       套接字存在于其特定的通信域(即协议族)中,只有隶属于同一协议族的套接字才能建立对话。一般情况下除非通信协议支持,只有相同类型的套接字才能相互传递数据。从套接字主要有两种类型:流式套接字(TCP)、数据报套接字(UDP)。                                                                                                                  流式套接字(SOCK_STREAM):该类套接字提供了面向连接的、可靠的、数据无错并且无重复的数据发送服务,而且发送的数据时按顺序被接受的。所有利用该套接字进行传递的数据均被视为连续的字节流且无长度限制。这对数据的稳定性、正确性和发送/接受顺序要求严格的应用十分适用,TCP协议使用该类接口,但其对线路的占用率相对比较高。流式套接字的实现屡见不鲜,如远程登录(TELNET)、文件传输协议(FTP)等使用了流式套接字。                                                                            数据报套接字(SOCK_DGRAM):数据报套接字提供了面向无连接的服务。它独立地以数据包形式发送数据(数据包长度不能大于32KB),不提供正确性检查,也不保证各数据包的发送顺序,因此可能出现数据的重发、丢失等现象,并且接受顺序由具体路由决定。然而,数据报的实现对网络线路占有率较低,NFS(网络文件系统)采用此类套接字,在TCP/IP 协议族中,UDP(UserDatagramProtocol)使用该类接口。

 

 1.CFSocket 

      CFSocket是对BSD套接字的一个抽象封装,它提供了BSD套接字的几乎全部功能,还集成了 run-loop。CFSocket可以处理任何类型的套接字,而不仅仅限于流式套接字。

   (1)创建CFSocket

    常用的方法是CFSocketCreate 和CFSocketCreateWithNative。

     CFSocketCreate 方法声明如下:

   CFSocketRef CFSocketCreate (

        CFAllocatorRef allocator,  //指定创建新套接字的内存分配器类型,传入NULL或kCFAllocatorDefault可以使用默认的分配器。一般默认即可。

        SInt32 protocolFamily,  //指定套接字的协议族。默认使用PF_INET,也就是平时说的IPV4。指定PF_INET6以使用IPV6协议。

SInt32 socketType,   //套接字类型,SOCK_STREAM或SOCK_DGRAM。

SInt32 protocol,    //套接字使用的协议,IPPROTO_TCP或IPPROTO_UDP。此项需要与套接字类型一致,设为0取默认值(如果socketType为SOCK_STREAM,此项默认值为IPPROTO_TCP,否则为IPPROTO_UDP)。

CFOptionFlags callBackTypes,  //CFSocket提供的run-loop可以在特定件发生时回调指定的函数。这个参数使用了位掩码,因此可以使用按位或运算指定多个类型。苹果公司将一些类型定义为枚举值如下所示:

    

枚举值

enum CFSocketCallBackType {

         kCFSocketNoCallBack = 0,

 kCFSocketReadCallBack =1,

 kCFSocketAcceptCallBack =2,

 kCFSocketDataCallBack = 3,

 kCFSocketConnectionCallBack = 4,

 kCFSocketWriteCallBack =8 

            };

            typedef enum CFSocketCallBackType CFSocketCallBackType; 

 

 

CFSocketCallBack callout,//指定回调函数,当指定的事件类型中的一个发生时函数被调用。这样,不用自己写循环等待连接、发送数据了。

const CFSocketContext *context //存储与套接字相关信息的数据结构,里面可以包含自定义数据。函数会将里面的内容拷出来,因此函数调用完后参数指向的内存不必再保留。可以为NULL。

   ); 

       CFSocketCreateWithNative方法,可以使用一个已经存在的BSD套接字来创建CRSocket,

                  CFSocketRef CFSocketCreateWithNative

                  {

 CFAllocatorRef allocator,

 CFSocketNativeHandle sock,

 CFOptionFlags callBackTypes,

 CFSocketCallBack callout,

 const CFSocketContext *context 

                  }; 

 

2.套接字函数

     创建好CFSocket后,就可以使用它提供的一系列函数了。通过CFSocketNative函数,还可以操作更底层的BSD套接字。以下几个函数式CFSocket中常用的。

    (1)CFSocketGetNative  返回系统套接字,通常是int类型的。获得这个套接字,可以使用Unix系统原生的套接字操作。调用setsockopt函数。

    (2) CFSocketConnectToAddress 用于将套接字连接到一个正在监听的套接字(服务器)。

    (3)CFSocketCopyAddress  返回CFSocket的地址,可以知道套接字正在哪个IP地址上监听。

    (4)CFSocketCopyPeerAddress 获得CFSocket连接到的远程套接字的地址。

    (5)CFSocketCreateRunLoopSource  为CFSocket创建一个CFRunLoop。

    (6)CFSocketSendData  发送数据,需要传入CFSocket、地址(NULL则发送到套接字已经连接到的地址)、要发送的数据(CFDataRef类型)、超时时间。返回值有kCFSocketSuccess、kCFSocketError和kCFSocketTimeout 3种。

 

3.回调函数

 

函数类型定义

typedef void (*CFSocketCallBack)(

    CFSocketRef s,

    CFSocketCallBackType callbackType,

    CFDataRef address,

    const void *data,

    void *info 

) ;


 

 

函数声明

void CallBackTest

(

CFSocketRef s,

CFSocketCallBackType callbackType,

CFDataRef address,

const void *data,

void *info 

) ;


 

    每个回调函数可以获得以下信息,根据类型不同,一些信息也会不一样。

(1)CFSocketRefs  事件对应的CFSocket,这样可以区分不同套接字产生的事件。

(2)CFSocketCallBackType callbackType  标识哪一种事件发生了。 

(3)CFDataRef address  包含底层sockaddr信息的CFData指针。可以通过这个参数获得的套接字连接到的远程地址。只有在kCFSocketAcceptCallBack和kCFSocketDataCallBack事件发生时才会提供这个参数。

(4)const void *data 根据回调类型指向不同的数据。如果是kCFSocketDataCallBack类型,这个参数为CFDataRef类型,包含接收到的数据;如果是kCFSocketAcceptCallBack类型,这个参数为指向CFSocketNativeHandle的指针;如果为kCFSocketConnectCallBack,连接在后台失败了,这个参数是指向SInt32类型错误代码的一个指针。其他情况这个参数为NULL。

(5)void *info  CFSocketContext结构体中的info成员,是自定义数据。CFSocketContext是在创建CFSocket时被指定的。 

 

4.CFSocketContext   

它是包含自定义数据及一些回调的结构体。 因为CFSocket通过run-loop异步通知发生的事件,当有很多连接的时候,如何保持对每个连接相关数据的跟踪变得困难起来。CFSocketContext允许为套接字绑定任何类型的数据,每个回调函数都可以得到这个数据。

CFSocketContext

   struct CFSocketContext

  {

CFIndex version;//结构体的版本号,要求必须为0。

void *info;//指向自定义数据的指针,在CFSocket创建时被关联的。

        CFAllocatorRetainCallBack retain;//对info指针的retain回调,可以为NULL。

CFAllocatorReleaseCallBack release;//对info指针的release回调,可以为NULL。

CGAllocatorCopyDescriptionCallBack copyDescription; //对info指针的copy description回调,可以为NULL。

  } ;

  typedef struct CFSocketContext CFSocketContext; 

CFStream  

     读写流以设备依赖的方式为交换数据提供了一个简单的途径。可以为内存中的数据、文件中的数据或网络上的数据(使用套接字)创建流,而不用把全部数据一次性读到内存中去。套接字流为通过套接字读写数据提供了一个简单易用的接口。每个套接字都可以与输入流和输出流绑定,允许异步的通信。CFStream包装了大部分读写字节流需要的工作,替换了C语言中传统的send和recv方法。CFSream对象包括两种类型:CFReadStream和CFWriteStream。

1.CFReadStream

   它提供了一系列从套接字读取数据相关的方法,下面列出其中一些常用的函数。

(1)CFReadStreamOpen和CFReadStreamClose  打开和关闭一个读取流(read stream),分配或释放相关资源。读取流打开后,才能从中读取数据。

(2)CFReadStreamRead  从流中读取数据的函数,返回实际读取的字节数。这个方法会阻塞直到接收到数据。

(3)CFReadStreamGetBufer  返回指向读取流内部未读数据缓存区的指针。

(4)CFReadStreamGetStatus  返回读取流的当前状态。 

 

读取流的当前状态 
  •  kCFStreamStatusNotOpen(读取流没有被打开)
  • kCFStreamStatusOpening(读取流正在被打开)
  • kCFStreamStatusOpen(读取流已经被打开)
  • kCFStreamReading(正在对流执行读取操作)
  • kCFSreamStatusAtEnd(读取流中没有可以读取的数据)
  • kCFStreamStatusClosed(读取流已经被关闭)
  • kCFStreamStatusError(读取流的时候发生了错误)
(5)CFReadStreamHasBytesAvailable。

 

返回一个布尔值,表示是否存在数据可以不阻塞地读取。如果不通过实际读取操作函数无法确定是否存在数据,则返回TRUE。

(6) CFReadStreamSheduleWithRunLoop和CFReadStreamUnscheduleFromRunLoop。

通过调用CFReadStreamSheduleWithRunLoop,指定run loop和run loop模式,使用CFReadStreamSetClient方法设置的回调函数在事件发生时会被调用。比如,在流被打开、有数据可以读取等事件发生时调用回调函数。使用 CFReadStreamUnscheduleFromRunLoop函数从run loop中移除。

(7)CFReadStreamSetClient。

 设置回调函数在一些事件发生时被调用。回调函数的声明类似于如下所示。

  void CallBackTest(

CFReadStreamRef stream,

CFStreamEventType eventType,

void *clientCallBackInfo 

  ) 

 与CFSocket的回调函数类似,回调函数可以得到事件类型和相关的数据。事件类型的定义如下。

   typedef enum

  {

kCFStreamEventNone = 0,(没有事件发生)

kCFStreamEventOpenCompleted = 1,(流被成功打开)

kCFStreamEventHasBytesAvailable = 2,(有数据可以读取)

kCFStreamEventCanAcceptBytes = 4,(流可以接受写入数据(用于写入流))

kCFStreamEventErrorOccurred = 8(在流上有错误发生)

kCFStreamEventEndEncountered = 16 (到达了流的结束位置)

  } CFStreamEventType;

 

 2.CFWriteStream

 CFWriteStream和CFReadStream相对应,它提供了向套接字流写入数据(发送数据)的方法。下面列出一些常用的函数。

(1)CFWriteStreamOpen和CFWriteStreamClose

打开和关闭一个写入流(write stream),分配或释放相关资源。写入流打开后,才能向其中写入数据。

(2)CFWriteStreamWrite

向流中写入数据的函数,返回实际发送的字节数。这个方法会阻塞,直到数据被发送完。 

(3)CFWriteStreamStatus

返回写入流的当前状态,为以下几种。

  • kCFStreamStatusNotOpen(写入流没有被打开)
  • kCFStreamStatusOpening(写入流正在被打开)
  • kCFStreamStatusOpen(写入流已经被打开)
  • kCFStreamWriting(正在对流执行写入操作)
  • kCFStreamStatusAtEnd(没有数据可以向流中写入)
  • kCFStreamStatusClosed(写入流已经被关闭)
  • kCFStreamStatusError (写入流的时候发生了错误)

(4)CFWriteStreamCanAcceptBytes

返回一个布尔值,表示是否可以不阻塞地向流中写入数据。如果不通过实际写入操作函数无法确定是否可以写入,则返回TRUE。 

(5)CFWriteStreamSheduleWithRunloop 和 CFWriteStreamUnscheduleFromRunLoop

通过调用 CFWriteStreamSheduleWithRunloop ,指定run loop和run loop模式,使用CFWriteStreamSetClient方法设置的回调函数在事件发生时会被调用。比如,在流被打开、可以发送数据事件发生时调用回调函数。使用CFWriteStreamUnscheduleFromRunLoop函数从run loop中移出。

(6)CFWriteStreamSetClient

 设置回调函数在一些事件发生时被调用。回调函数的声明类似于如下所示。

  void CallBackTest (

  CFWriteStreamRef stream,

CFStreamEventType eventType,

void *clientCallBackInfo 

  ) ;

 与CFSocket的回调函数类似,回调函数可以得到事件类型和相关的数据。