ArticleDirectory
- II. Introduction to BSD Socket API
-
-
-
-
-
-
-
-
-
- 3. Server Workflow
- 4. Client Workflow
-
- 5. client code example
[Simple introduction to cocoa] socket for iOS Network Programming
Luo chaohui (http://www.cnblogs.com/kesalin)
This article follows the "signature-non-commercial use-consistency" creation public agreement
I. Network Programming hierarchy model for iOS
In the previous article 《In-depth introduction to bonjour network programming in cocoa, I introduced how to program bonjour In the MAC system. In that article, I also introduced that the network programming hierarchy in cocoa is divided into three layers, although the example demonstrates the MAC system, it is the same for iOS systems. The Network Programming hierarchy of IOS is also divided into three layers:
- Cocoa layer: nsurl, bonjour, game kit, WebKit
- Core Foundation layer:C-basedCfnetwork and cfnetservices
- OS layer: C-based BSD socket
The cocoa layer is the top-layer objective-C-based API, such as URL access, nsstream, bonjour, and gamekit. This is a common API in most cases. The cocoa layer is implemented based on the core foundation.
Core Foundation layer: because the direct use of socket requires more programming work, Apple simply encapsulates the OS-layer socket to simplify programming tasks. This layer provides cfnetwork and cfnetservices. cfnetwork is based on cfstream and cfsocket.
OS layer: the underlying BSD socket provides maximum control over network programming, but programming is also the most. Therefore, Apple recommends that we use core Foundation and APIs above for programming.
This article will introduce how to use the underlying socket for programming in iOS, which is no big difference from using C/C ++ for socket programming in windows.
Source: https://github.com/kesalin/iOSSnippet/tree/master/KSNetworkDemo
The running effect is as follows:
II. Introduction to BSD Socket API
BSD socket APIs are similar to Winsock APIs. Common APIs are listed below:
API |
Explanations |
IntSocket(INT addressfamily, int type, Int Protocol)
IntClose(INT socketfiledescriptor) |
SocketCreate and initialize the socket. The file descriptor of the socket is returned. If the descriptor is-1, the creation fails. CloseDisable socket. Generally, the addressfamily parameter is IPv4 (af_inet) or IPv6 (af_inet6 ). Type indicates the socket type, which is usually stream (sock_stream) or data packet datasync (sock_dgram ). The Protocol parameter is usually set to 0 so that the system can automatically select an appropriate protocol. For stream socket, it will be TCP (ipproto_tcp ), for dataphin, UDP protocol (ipproto_udp) is used ). |
int bind (INT socketfiledescriptor, sockaddr * addresstobind, int addressstructlength) |
bind the socket with the specified host address and port number. If the socket is successfully bound, 0 is returned, and-1 is returned if the socket fails to be bound. after successful binding, depending on the Protocol (TCP/UDP, we can perform different operations on the socket: UDP: because UDP is not connected, after binding, you can use UDP socket to transmit data. TCP: TCP requires an end-to-end connection, to establish a TCP connection server, you must call listen (INT socketfiledescriptor, int backlogsize) to set the server's Buffer Queue to receive client connection requests. backlogsize indicates the size of the Client Connection Request Buffer Queue. After the listen setting is called, the server waits for the client request and then calls the following accept to accept the client connection request. |
IntAccept(INT socketfiledescriptor, sockaddr * clientaddress, intclientaddressstructlength) |
Accept client connection requests and save client network address information to clientaddress. After the client connection request is accepted by the server, the link between the client and the server is established and the two can communicate. |
IntConnect(INT socketfiledescriptor, sockaddr * serveraddress, intserveraddresslength) |
The client sends a connection request to the server with the specified network address. If the connection succeeds, 0 is returned. If the connection fails,-1 is returned. After the server is established, the client calls this interface to initiate a connection request to the server. This interface is optional for UDP. If this interface is called, the default network address of the UDP socket is set. For TCP socket, This is where three handshakes are established. Note: This interface will block the current thread until the server returns. |
Hostent *Gethostbyname(Char * hostname) |
Use DNS to find the IP address corresponding to a specific host name. If the corresponding IP address is not found, null is returned. |
IntSend(INT socketfiledescriptor, char * buffer, int bufferlength, int flags) |
The number of bytes successfully sent when data is sent through socket. Otherwise,-1 is returned. Once the connection is established, you can send or receive data through the send/receive interface. Note that you can call the UDP socket with the default network address configured by connect to receive data. |
IntReceive(INT socketfiledescriptor, char * buffer, int bufferlength, int flags) |
Reads data from the socket, and returns the number of bytes successfully read after successful reading; otherwise,-1 is returned. Once the connection is established, you can send or receive data through the send/receive interface. Note that you can call the UDP socket with the default network address configured by connect to send data. |
IntSendto(INT socketfiledescriptor, char * buffer, int bufferlength, intflags, sockaddr * destinationaddress, intdestinationaddresslength) |
If data is sent to a specific network address through UDP socket, the number of bytes that are successfully sent is returned. Otherwise,-1 is returned. Because UDP can send data to multiple network addresses, you can specify a specific network address to send data to it. |
IntRecvfrom(INT socketfiledescriptor, char * buffer, int bufferlength, intflags, sockaddr * fromaddress, int * fromaddresslength) |
Reads data from a UDP socket, saves the network address information of the sender, and returns the number of bytes that are successfully read after successful reading; otherwise,-1 is returned. Because UDP can receive data from multiple network addresses, you need to provide additional parameters to save the identity of the Data sender. |
3. Server Workflow
With the above Socket API explanation, we will summarize the server workflow below.
- The server calls socket (...) to create a socket;
- The server calls listen (...) to set the buffer;
- The server uses accept (...) to accept client requests to establish a connection;
- After the server establishes a connection with the client, it can send data to or receive data from the client through send (...)/receive;
- The server calls close to close the socket;
Because iOS devices are usually used as clientsCodeTo demonstrate how to create an iOS server, but refer to the previous article: in-depth introduction to cocoa bonjour network programming to see how to create a desktop server on a Mac system.
4. Client Workflow
Since iOS devices are usually used as clients, the following describes how to write client code. First, summarize the client workflow.
- The client calls socket (...) to create a socket;
- The client calls connect (...) to initiate a connection request to the server to establish a connection;
- After the client establishes a connection with the server, it can send data to or receive data from the client through send (...)/receive;
- The client calls close to close the socket;
5. client code example
The following code implements the workflow of the client above:
-( Void ) Loaddatafromserverwithurl :( nsurl * ) URL {nsstring * Host = [Url host]; nsnumber * Port = [Url port]; // Create socket // Int Socketfiledescriptor = Socket (Af_inet, sock_stream, 0 ); If (- 1 = Socketfiledescriptor) {nslog ( @" Failed to create socket. " ); Return ;} // Get IP address from host // Struct Hostent * remotehostent = Gethostbyname([Host utf8string]); If (Null = Remotehostent) {close (socketfiledescriptor); [self networkfailedwitherrormessage: @" Unable to resolve the hostname of the warehouse server. " ]; Return ;} Struct In_addr * remoteinaddr = ( Struct In_addr *) remotehostent-> h_addr_list [ 0 ]; // Set the socket Parameters // Struct Sockaddr_in socketparameters; socketparameters. sin_family = Af_inet; socketparameters. sin_addr = * Remoteinaddr; socketparameters. sin_port = Htons ([port intvalue]); // Connect the socket // Int Ret = Connect (Socketfiledescriptor ,( Struct Sockaddr *) & socketparameters, Sizeof (Socketparameters )); If (- 1 = RET) {close (socketfiledescriptor); nsstring * Errorinfo = [nsstring stringwithformat: @" > Failed to connect to % @: % @ " , Host, port]; [self networkfailedwitherrormessage: errorinfo]; Return ;} Nslog ( @" >>Successfully connected to % @: % @ " , Host, Port); nsmutabledata * Data = [[Nsmutabledata alloc] init]; bool waitingfordata = Yes; // Continually receive data until we reach the end of the data // Int Maxcount = 5 ; // Just for test. Int I = 0 ; While (Waitingfordata & I < Maxcount ){ Const Char * Buffer [ 1024 ]; Int Length = Sizeof (Buffer ); // Read a buffer's amount of data from the socket; the number of bytes read is returned // Int Result =Recv (Socketfiledescriptor, & buffer, length, 0 ); If (Result> 0 ) {[Data appendbytes: Buffer length: result];} Else { // If we didn't get any data, stop the receive Loop // Waitingfordata = No ;} ++I ;} // Close the socket // Close(Socketfiledescriptor); [self networksucceedwithdata: Data];}
As mentioned above, interfaces such as connect/Recv/send are all blocking, so we need to place these operations in non-UI threads. As follows:
Nsthread * backgroundthread =[[Nsthread alloc] initwithtarget: Self selector: @ selector (loaddatafromserverwithurl :)Object: Url]; [backgroundthread start];
Similarly, when the task fails due to data or network exceptions, We need to update the UI, which also needs to be done in the UI thread. As follows:
-( Void ) Networkfailedwitherrormessage :( nsstring * ) Message { // Update UI // [[Nsoperationqueue mainqueue] addoperationwithblock: ^ {Nslog ( @" % @ " , Message); self. deleetextview. Text = Message; self. connectbutton. Enabled = Yes; [self. networkactivityview stopanimating];} -( Void ) Networksucceedwithdata :( nsdata * ) Data { // Update UI // [[Nsoperationqueue mainqueue] addoperationwithblock: ^ {Nsstring * Resultsstring = [[Nsstring alloc] initwithdata: data encoding: nsutf8stringencoding]; nslog ( @" > Received string: '% @' " , Resultsstring); self. deleetextview. Text =Resultsstring; self. connectbutton. Enabled = Yes; [self. networkactivityview stopanimating];}