ArticleDirectory
[Cocoa] nsstream for iOS Network Programming
Luo chaohui (http://www.cnblogs.com/kesalin)
This article follows the "signature-non-commercial use-consistency" creation public agreement 1. Introduction to nsstream First, let's review it. In the previous article [Exploring cocoa] socket for iOS network programming, we mentioned that the Layer Model of IOS network programming is divided into three layers:
- Cocoa layer: nsurl, bonjour, game kit, WebKit
- Core Foundation layer:C-basedCfnetwork and cfnetservices
- OS layer: C-based BSD socket
Previous articles: socket for iOS network programming and 《Cfnetwork for iOS Network ProgrammingThis article describes the underlying socket and cfnetwork at the core foundation layer. Nsstream is actually a simple encapsulation of cfnetwork with objective-C, It uses The nsstreamdelegate protocol is used to implement the callback function in the cfnetwork. Similarly, runloop works well with nsstream. Nsstream has two entity classes: nsinputstream and nsoutputstream, which correspond to cfreadstream and cfwritestream in cfnetwork respectively. ExampleCodeSee: Https://github.com/kesalin/iOSSnippet/tree/master/KSNetworkDemo II. Introduction to nsstream APIs
NsstreamClass has the following interfaces:
-(Void) Open;
-(Void) Close;
-(ID<Nsstreamdelegate>) Delegate;
-(Void) Setdelegate :(ID<Nsstreamdelegate>) Delegate;
-(Void) Scheduleinrunloop :(Nsunloop*) Arunloop formode :(Nsstring*) Mode;
-(Void) Removefromrunloop :(Nsunloop*) Arunloop formode :(Nsstring*) Mode;
-(Nsstreamstatus) Streamstatus;
-(Nserror*) Streamerror;
Some nsstream interfaces are similar to cfnetwork interfaces, such as opening, closing, obtaining status and error information, and combining them with runloop. As mentioned above, nsstream implements the callback function in cfnetwork through nsstreamdelegate. This Optional Protocol has only one interface:
-(Void) Stream :(Nsstream*) Astream handleevent :(Nsstreamevent) Eventcode;
Nsstreamevent is a stream event enumeration:
Typedef Ns_options(Nsuinteger, nsstreamevent ){
Nsstreameventnone =0,
Nsstreameventopencompleted =1Ul <0,
Nsstreameventhasbytesavailable =1Ul <1,
Nsstreameventhasspaceavailable =1Ul <2,
Nsstreameventerroroccurred =1Ul <3,
Nsstreameventendencountered =1Ul <4
};
The meanings of these event enumerations are similar to those of cfstreameventtype in cfnetwork, so they are no longer repeated here.
NsinputstreamClass has the following interfaces:
-(Nsinteger) Read :(Uint8_t*) Buffer maxlength :(Nsuinteger) Len;
When data is read from the stream to the buffer, the buffer length should not be less than Len. This interface returns the actual length of data read (the maximum length is Len ).
-(Bool) Getbuffer :(Uint8_t**) Buffer length :(Nsuinteger*) Len;
Obtain the data and size of the current stream. Note that the buffer is valid only before the next stream operation.
-(Bool) Hasbytesavailable;
Check whether there is any data in the stream.
NsoutputstreamClass has the following interfaces:
-(Nsinteger) Write :(Const Uint8_t*) Buffer maxlength :(Nsuinteger) Len;
Writes data in the buffer to the stream, and returns the actual number of bytes written.
-(Bool) Hasspaceavailable;
Check whether there is any space for writing in the stream.
From these interfaces, nsstream is a simple objective-C encapsulation on the cfnetwork. But nsstream in IOS does not support nshost, which is a defect and apple is aware of this problem (http://developer.apple.com/library/ios/#qa/qa1652/_index.html). We can implement this feature through nsstream's extended functions:
@ Implementation Nsstream (streamstohost) + ( Void ) Getstreamstohostnamed :( Nsstring * ) HostnamePort:( Nsinteger) PortInputstream:( Out Nsinputstream **) InputstreamptrOutputstream:( Out Nsoutputstream ** ) Outputstreamptr {cfreadstreamref readstream; cfwritestreamref writestream; Assert (hostname ! = Nil); Assert (Port > 0 ) & (Port < 65536 ); Assert (inputstreamptr ! = NULL) | (outputstreamptr! = Null); readstream = NULL; writestream =NULL; cfstreamcreatepairwithsockettohost (null, (_ bridge cfstringref) hostname, port, (inputstreamptr ! = NULL )? & Readstream: NULL), (outputstreamptr ! = NULL )? & Writestream: NULL )); If (Inputstreamptr! = Null ){ * Inputstreamptr = Cfbridgingrelease (readstream );} If (Outputstreamptr! =Null ){ * Outputstreamptr = Cfbridgingrelease (writestream );}} @ End
3. Client sample code
Similar to the previous example, here I only demonstrate the client example. Similarly, we also start network operations in a background thread:
Nsurl * url = [nsurl urlwithstring: [nsstring stringwithformat:@"% @: % @", Serverhost, SERVERPORT]; nsthread* Backgroundthread =[[Nsthread alloc] initwithtarget: Self selector: @ selector (loaddatafromserverwithurl :)Object: Url]; [backgroundthread start];
Create an nsinputstream in loaddatafromserverwithurlAnd set its delegate, add it to the event source of Run-loop, then open the stream and run runloop:
-(Void) Loaddatafromserverwithurl :( nsurl *) URL {nsinputstream*Readstream; [nsstream getstreamstohostnamed: [url host] Port: [[url port] integervalue] inputstream:&Readstream outputstream: NULL]; [readstream setdelegate: Self]; [readstream scheduleinrunloop: [nsunloop currentrunloop] formode: Unknown]; [readstream open]; [[nsunloop currentrunloop] Run];}
Because we use ksnsstreamviewcontroller as the delegate of nsinputstream, we need to implement the delgate in ksnsstreamviewcontroller:
# Pragma Mark nsstreamdelegate -( Void ) Stream :( nsstream *) Stream handleevent :( nsstreamevent) eventcode {nslog ( @" > Nsstreamdelegate in thread % @ " , [Nsthread currentthread]); Switch (Eventcode ){ Case Nsstreameventhasbytesavailable :{ If (_ Receiveddata = Nil) {_ receiveddata =[[Nsmutabledata alloc] init];} uint8_t Buf [kbuffersize]; Int Numbytesread = [(Nsinputstream *) stream read: Buf maxlength:Kbuffersize ]; If (Numbytesread> 0 ) {[Self didreceivedata: [nsdata datawithbytes: Buf length: numbytesread];} Else If (Numbytesread = 0 ) {Nslog ( @" > End of stream reached " );} Else {Nslog ( @" > Read error occurred " );} Break ;} Case Nsstreameventerroroccurred: {nserror * Error = [Stream streamerror]; Nsstring * Errorinfo = [nsstring stringwithformat: @" Failed while reading stream; error '% @' (code % d) " , Error. localizeddescription, error. Code]; [self cleanupstream: stream]; [self networkfailedwitherrormessage: errorinfo];} Case Nsstreameventendencountered: {[self cleanupstream: stream]; [self didfinishreceivingdata]; Break ;} Default : Break ;}}
When Data Reading is complete or reading fails, call the cleanupstream method to close the stream:
-(Void) Cleanupstream :( nsstream *) Stream {[stream removefromrunloop: [nsunloop currentrunloop] formode: nsdefaultrunloopmode]; [stream close]; stream=Nil ;}
Iv. Conclusion
Through the example above, we can see that nsstream only uses objective-C to encapsulate a simple layer of cfnetwork, but it makes it much easier for us to program using socket, so in most cases, we should give priority to nsstream for socket programming.