從Samples中入門IOS開發(四)—— 基於socket的網路編程

來源:互聯網
上載者:User

SimpleNetworkStreams展示了如何基於Socket網路編程,實現了一個很典型的區域網路內網路資料轉送的情境,一個是client向server端發送本地的圖片檔案,另一個是client向server端下載圖片到本地檔案。抽取出來的一般流程:

  • server開啟socket監聽

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

第一步:建立系統級的socket,並綁定連接埠

    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是讓系統自動隨機找一個空閑連接埠。其他都是基於c風格對系統函數的直接調用。

第二步:用IOS的socket(CFSocket)封裝系統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);}

這裡封裝socket的目的是便於後面的事件偵聽和處理,把基於原生態socket的開發轉到IOS的層面上來,這裡accept事件偵聽函數是AcceptCallback,並在單獨thread中執行。

第三步:通過NSNetService發布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: ...}

這裡是基於NSNetService把先前建立的socket發布出去,便於clienti串連和請求。

  • client發起socket串連請求

這裡是client通過前面server發布出來了netservice,發起對socket的串連:

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

  • server監聽並處理資料請求

server會在accept的事件偵聽的回呼函數裡對socket開啟stream,並偵聽stream上的各種IO事件:

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;    }}

以上代碼是server監聽到有資料發過來時,把資料寫入本地檔案中,這裡實際上就是把網路inputstream寫入File的outputstream。這裡handleevent方法是當設定了self.networkStream.delegate = self後IO事件的回呼函數,handleevent裡就需要根據不同的事件類型進行不同的處理。

  • client發送和接受資料流

client的資料處理與server類似也是通過對網路stream的事件監聽來完成:

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;    }}

這裡的過程與server端正好相反,是從file的Inputstream中讀入資料,並寫入網路的outputsteam中。

以上就是我理解的IOS中Socket網路編程的一般模式。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.