Simplenetworkstreams demonstrates how to program on the Socket network to implement a typical scenario of network data transmission in the LAN. One is that the client sends a local image file to the server, the other is that the client downloads images from the server to a local file. General process:
- Server enables socket listening
Here, IOS generally follows three steps:
Step 1: Create a system-level socket and bind the port
port = 0; fd = socket(AF_INET, SOCK_STREAM, 0); success = (fd != -1); if (success) { memset(&addr, 0, sizeof(addr)); addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = INADDR_ANY; err = bind(fd, (const struct sockaddr *) &addr, sizeof(addr)); success = (err == 0); } if (success) { err = listen(fd, 5); success = (err == 0); } if (success) { socklen_t addrLen; addrLen = sizeof(addr); err = getsockname(fd, (struct sockaddr *) &addr, &addrLen); success = (err == 0); if (success) { assert(addrLen == sizeof(addr)); port = ntohs(addr.sin_port); } }
Port = 0 is used to allow the system to randomly find an idle port. Others are direct calls to system functions based on the C style.
Step 2: Use IOS socket (cfsocket) to package system socket
CFSocketContext context = { 0, (__bridge void *) self, NULL, NULL, NULL };assert(self->_listeningSocket == NULL);self->_listeningSocket = CFSocketCreateWithNative( NULL, fd, kCFSocketAcceptCallBack, AcceptCallback, &context);success = (self->_listeningSocket != NULL);if (success) { CFRunLoopSourceRef rls; fd = -1; // listeningSocket is now responsible for closing fd rls = CFSocketCreateRunLoopSource(NULL, self.listeningSocket, 0); assert(rls != NULL); CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); CFRelease(rls);}
The purpose of packaging the socket here is to facilitate subsequent event listening and processing, and transfer the development based on the original ecological socket to the IOS layer. Here, the accept event listening function is acceptcallback, and run it in a separate thread.
Step 3: Use nsnetservice to publish a socket
if (success) { self.netService = [[NSNetService alloc] initWithDomain:@"local." type:@"_x-SNSUpload._tcp." name:@"Test" port:port]; success = (self.netService != nil);}if (success) { self.netService.delegate = self; [self.netService publishWithOptions:NSNetServiceNoAutoRename]; // continues in -netServiceDidPublish: or -netService:didNotPublish: ...}
The previously created socket is released based on nsnetservice to facilitate clienti connection and request.
- The client initiates a socket connection request.
Here, the client releases netservice through the server and initiates a connection to the socket:
netService = [[NSNetService alloc] initWithDomain:@"local." type:@"_x-SNSUpload._tcp." name:@"Test"];
- Server listens for and processes data requests
The server will enable stream on the socket in the callback function of the accept event listening, and listen on various Io events on the stream:
CFStreamCreatePairWithSocket(NULL, fd, &readStream, NULL);assert(readStream != NULL);self.networkStream = (__bridge NSInputStream *) readStream;CFRelease(readStream);[self.networkStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];self.networkStream.delegate = self;[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];[self.networkStream open];
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode // An NSStream delegate callback that's called when events happen on our // network stream.{ assert(aStream == self.networkStream); #pragma unused(aStream) switch (eventCode) { case NSStreamEventOpenCompleted: { [self updateStatus:@"Opened connection"]; } break; case NSStreamEventHasBytesAvailable: { NSInteger bytesRead; uint8_t buffer[32768]; [self updateStatus:@"Receiving"]; // Pull some data off the network. bytesRead = [self.networkStream read:buffer maxLength:sizeof(buffer)]; if (bytesRead == -1) { [self stopReceiveWithStatus:@"Network read error"]; } else if (bytesRead == 0) { [self stopReceiveWithStatus:nil]; } else { NSInteger bytesWritten; NSInteger bytesWrittenSoFar; // Write to the file. bytesWrittenSoFar = 0; do { bytesWritten = [self.fileStream write:&buffer[bytesWrittenSoFar] maxLength:bytesRead - bytesWrittenSoFar]; assert(bytesWritten != 0); if (bytesWritten == -1) { [self stopReceiveWithStatus:@"File write error"]; break; } else { bytesWrittenSoFar += bytesWritten; } } while (bytesWrittenSoFar != bytesRead); } } break; case NSStreamEventHasSpaceAvailable: { assert(NO); // should never happen for the output stream } break; case NSStreamEventErrorOccurred: { [self stopReceiveWithStatus:@"Stream open error"]; } break; case NSStreamEventEndEncountered: { // ignore } break; default: { assert(NO); } break; }}
The above code writes data to a local file when the server detects that data has been sent. In fact, the network inputstream is written to the outputstream of the file. Here, the handleevent method is the callback function of the IO event after self. networkstream. Delegate = self is set. In handleevent, different processing needs to be performed based on different event types.
- Client sends and accepts data streams
The client's data processing is similar to that of the server through listening to network stream events:
self.networkStream = output;self.networkStream.delegate = self;[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];[self.networkStream open];
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode // An NSStream delegate callback that's called when events happen on our // network stream.{ assert(aStream == self.networkStream); #pragma unused(aStream) switch (eventCode) { case NSStreamEventOpenCompleted: { [self updateStatus:@"Opened connection"]; } break; case NSStreamEventHasBytesAvailable: { assert(NO); // should never happen for the output stream } break; case NSStreamEventHasSpaceAvailable: { [self updateStatus:@"Sending"]; // If we don't have any data buffered, go read the next chunk of data. if (self.bufferOffset == self.bufferLimit) { NSInteger bytesRead; bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize]; if (bytesRead == -1) { [self stopSendWithStatus:@"File read error"]; } else if (bytesRead == 0) { [self stopSendWithStatus:nil]; } else { self.bufferOffset = 0; self.bufferLimit = bytesRead; } } // If we're not out of data completely, send the next chunk. if (self.bufferOffset != self.bufferLimit) { NSInteger bytesWritten; bytesWritten = [self.networkStream write:&self.buffer[self.bufferOffset] maxLength:self.bufferLimit - self.bufferOffset]; assert(bytesWritten != 0); if (bytesWritten == -1) { [self stopSendWithStatus:@"Network write error"]; } else { self.bufferOffset += bytesWritten; } } } break; case NSStreamEventErrorOccurred: { [self stopSendWithStatus:@"Stream open error"]; } break; case NSStreamEventEndEncountered: { // ignore } break; default: { assert(NO); } break; }}
The process here is opposite to the server. It reads data from the inputstream of the file and writes it to the outputsteam of the network.
The above is the general Socket network programming mode in IOS.