从Samples中入门IOS开发(四)------ 基于socket的网络编程

SimpleNetworkStreams展示了如何基于Socket网络编程,实现了一个很典型的局域网内网络数据传输的场景,一个是client向server端发送本地的图片文件,另一个是client向server端下载图片到本地文件。抽取出来的一般流程:

  • server开启socket监听

此处IOS的一般做法是三步走:

第一步:创建系统级的socket,并绑定端口

[plain]  view plain copy
  1. port = 0;  
  2.   
  3. fd = socket(AF_INET, SOCK_STREAM, 0);  
  4. success = (fd != -1);  
  5.   
  6. if (success) {  
  7.     memset(&addr, 0, sizeof(addr));  
  8.     addr.sin_len    = sizeof(addr);  
  9.     addr.sin_family = AF_INET;  
  10.     addr.sin_port   = 0;  
  11.     addr.sin_addr.s_addr = INADDR_ANY;  
  12.     err = bind(fd, (const struct sockaddr *) &addr, sizeof(addr));  
  13.     success = (err == 0);  
  14. }  
  15. if (success) {  
  16.     err = listen(fd, 5);  
  17.     success = (err == 0);  
  18. }  
  19. if (success) {  
  20.     socklen_t   addrLen;  
  21.   
  22.     addrLen = sizeof(addr);  
  23.     err = getsockname(fd, (struct sockaddr *) &addr, &addrLen);  
  24.     success = (err == 0);  
  25.       
  26.     if (success) {  
  27.         assert(addrLen == sizeof(addr));  
  28.         port = ntohs(addr.sin_port);  
  29.     }  
  30. }  

这里用port=0是让系统自动随机找一个空闲端口。其他都是基于c风格对系统函数的直接调用。

第二步:用IOS的socket(CFSocket)包装系统socket

[plain]  view plain copy
  1. CFSocketContext context = { 0, (__bridge void *) self, NULL, NULL, NULL };  
  2.   
  3. assert(self->_listeningSocket == NULL);  
  4. self->_listeningSocket = CFSocketCreateWithNative(  
  5.     NULL,   
  6.     fd,   
  7.     kCFSocketAcceptCallBack,   
  8.     AcceptCallback,   
  9.     &context  
  10. );  
  11. success = (self->_listeningSocket != NULL);  
  12.   
  13. if (success) {  
  14.     CFRunLoopSourceRef  rls;  
  15.       
  16.     fd = -1;        // listeningSocket is now responsible for closing fd  
  17.   
  18.     rls = CFSocketCreateRunLoopSource(NULL, self.listeningSocket, 0);  
  19.     assert(rls != NULL);  
  20.       
  21.     CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);  
  22.       
  23.     CFRelease(rls);  
  24. }  

这里包装socket的目的是便于后面的事件侦听和处理,把基于原生态socket的开发转到IOS的层面上来,这里accept事件侦听函数是AcceptCallback,并在单独thread中执行。

第三步:通过NSNetService发布socket

[plain]  view plain copy
  1. if (success) {  
  2.     self.netService = [[NSNetService alloc] initWithDomain:@"local." type:@"_x-SNSUpload._tcp." name:@"Test" port:port];  
  3.     success = (self.netService != nil);  
  4. }  
  5. if (success) {  
  6.     self.netService.delegate = self;  
  7.       
  8.     [self.netService publishWithOptions:NSNetServiceNoAutoRename];  
  9.       
  10.     // continues in -netServiceDidPublish: or -netService:didNotPublish: ...  
  11. }  

这里是基于NSNetService把先前创建的socket发布出去,便于clienti连接和请求。

  • client发起socket连接请求

这里是client通过前面server发布出来了netservice,发起对socket的连接:

[plain]  view plain copy
  1. netService = [[NSNetService alloc] initWithDomain:@"local." type:@"_x-SNSUpload._tcp." name:@"Test"];  

  • server监听并处理数据请求

server会在accept的事件侦听的回调函数里对socket打开stream,并侦听stream上的各种IO事件:

[plain]  view plain copy
  1. CFStreamCreatePairWithSocket(NULL, fd, &readStream, NULL);  
  2. assert(readStream != NULL);  
  3.   
  4. self.networkStream = (__bridge NSInputStream *) readStream;  
  5.   
  6. CFRelease(readStream);  
  7.   
  8. [self.networkStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];  
  9.   
  10. self.networkStream.delegate = self;  
  11. [self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  12.   
  13. [self.networkStream open];  

[plain]  view plain copy
  1. - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode  
  2.     // An NSStream delegate callback that's called when events happen on our   
  3.     // network stream.  
  4. {  
  5.     assert(aStream == self.networkStream);  
  6.     #pragma unused(aStream)  
  7.   
  8.     switch (eventCode) {  
  9.         case NSStreamEventOpenCompleted: {  
  10.             [self updateStatus:@"Opened connection"];  
  11.         } break;  
  12.         case NSStreamEventHasBytesAvailable: {  
  13.             NSInteger       bytesRead;  
  14.             uint8_t         buffer[32768];  
  15.   
  16.             [self updateStatus:@"Receiving"];  
  17.   
  18.             // Pull some data off the network.  
  19.               
  20.             bytesRead = [self.networkStream read:buffer maxLength:sizeof(buffer)];  
  21.             if (bytesRead == -1) {  
  22.                 [self stopReceiveWithStatus:@"Network read error"];  
  23.             } else if (bytesRead == 0) {  
  24.                 [self stopReceiveWithStatus:nil];  
  25.             } else {  
  26.                 NSInteger   bytesWritten;  
  27.                 NSInteger   bytesWrittenSoFar;  
  28.   
  29.                 // Write to the file.  
  30.                   
  31.                 bytesWrittenSoFar = 0;  
  32.                 do {  
  33.                     bytesWritten = [self.fileStream write:&buffer[bytesWrittenSoFar] maxLength:bytesRead - bytesWrittenSoFar];  
  34.                     assert(bytesWritten != 0);  
  35.                     if (bytesWritten == -1) {  
  36.                         [self stopReceiveWithStatus:@"File write error"];  
  37.                         break;  
  38.                     } else {  
  39.                         bytesWrittenSoFar += bytesWritten;  
  40.                     }  
  41.                 } while (bytesWrittenSoFar != bytesRead);  
  42.             }  
  43.         } break;  
  44.         case NSStreamEventHasSpaceAvailable: {  
  45.             assert(NO);     // should never happen for the output stream  
  46.         } break;  
  47.         case NSStreamEventErrorOccurred: {  
  48.             [self stopReceiveWithStatus:@"Stream open error"];  
  49.         } break;  
  50.         case NSStreamEventEndEncountered: {  
  51.             // ignore  
  52.         } break;  
  53.         default: {  
  54.             assert(NO);  
  55.         } break;  
  56.     }  
  57. }  

以上代码是server监听到有数据发过来时,把数据写入本地文件中,这里实际上就是把网络inputstream写入File的outputstream。这里handleevent方法是当设置了self.networkStream.delegate = self后IO事件的回调函数,handleevent里就需要根据不同的事件类型进行不同的处理。

  • client发送和接受数据流

client的数据处理与server类似也是通过对网络stream的事件监听来完成:

[plain]  view plain copy
  1. self.networkStream = output;  
  2. self.networkStream.delegate = self;  
  3. [self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];  
  4.   
  5. [self.networkStream open];  

[plain]  view plain copy
  1. - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode  
  2.     // An NSStream delegate callback that's called when events happen on our   
  3.     // network stream.  
  4. {  
  5.     assert(aStream == self.networkStream);  
  6.     #pragma unused(aStream)  
  7.   
  8.     switch (eventCode) {  
  9.         case NSStreamEventOpenCompleted: {  
  10.             [self updateStatus:@"Opened connection"];  
  11.         } break;  
  12.         case NSStreamEventHasBytesAvailable: {  
  13.             assert(NO);     // should never happen for the output stream  
  14.         } break;  
  15.         case NSStreamEventHasSpaceAvailable: {  
  16.             [self updateStatus:@"Sending"];  
  17.               
  18.             // If we don't have any data buffered, go read the next chunk of data.  
  19.               
  20.             if (self.bufferOffset == self.bufferLimit) {  
  21.                 NSInteger   bytesRead;  
  22.                   
  23.                 bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize];  
  24.                   
  25.                 if (bytesRead == -1) {  
  26.                     [self stopSendWithStatus:@"File read error"];  
  27.                 } else if (bytesRead == 0) {  
  28.                     [self stopSendWithStatus:nil];  
  29.                 } else {  
  30.                     self.bufferOffset = 0;  
  31.                     self.bufferLimit  = bytesRead;  
  32.                 }  
  33.             }  
  34.               
  35.             // If we're not out of data completely, send the next chunk.  
  36.               
  37.             if (self.bufferOffset != self.bufferLimit) {  
  38.                 NSInteger   bytesWritten;  
  39.   
  40.                 bytesWritten = [self.networkStream write:&self.buffer[self.bufferOffset] maxLength:self.bufferLimit - self.bufferOffset];  
  41.                 assert(bytesWritten != 0);  
  42.                 if (bytesWritten == -1) {  
  43.                     [self stopSendWithStatus:@"Network write error"];  
  44.                 } else {  
  45.                     self.bufferOffset += bytesWritten;  
  46.                 }  
  47.             }  
  48.         } break;  
  49.         case NSStreamEventErrorOccurred: {  
  50.             [self stopSendWithStatus:@"Stream open error"];  
  51.         } break;  
  52.         case NSStreamEventEndEncountered: {  
  53.             // ignore  
  54.         } break;  
  55.         default: {  
  56.             assert(NO);  
  57.         } break;  
  58.     }  
  59. }  
这里的过程与server端正好相反,是从file的Inputstream中读入数据,并写入网络的outputsteam中。

以上就是我理解的IOS中Socket网络编程的一般模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值