網(wǎng)絡(luò) - Socket通訊相關(guān)回顧

Socket


  • Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口谱姓。


  • 網(wǎng)絡(luò)中進(jìn)程之間如何通信
    • 網(wǎng)絡(luò)中進(jìn)程之間如何通信?首要解決的問題是如何唯一標(biāo)識(shí)一個(gè)進(jìn)程后控,否則通信無從談起匿级!在本地可以通過進(jìn)程PID來唯一標(biāo)識(shí)一個(gè)進(jìn)程,但是在網(wǎng)絡(luò)中這是行不通的媳板。其實(shí)TCP/IP協(xié)議族已經(jīng)幫我們解決了這個(gè)問題桑腮,網(wǎng)絡(luò)層的“ip地址”可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的主機(jī),而傳輸層的“協(xié)議+端口”可以唯一標(biāo)識(shí)主機(jī)中的應(yīng)用程序(進(jìn)程)蛉幸。這樣利用三元組(ip地址破讨,協(xié)議,端口)就可以標(biāo)識(shí)網(wǎng)絡(luò)的進(jìn)程了奕纫,網(wǎng)絡(luò)中的進(jìn)程通信就可以利用這個(gè)標(biāo)志與其它進(jìn)程進(jìn)行交互提陶。
    • 使用TCP/IP協(xié)議的應(yīng)用程序通常采用應(yīng)用編程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已經(jīng)被淘汰),來實(shí)現(xiàn)網(wǎng)絡(luò)進(jìn)程之間的通信匹层。就目前而言隙笆,幾乎所有的應(yīng)用程序都是采用socket,而現(xiàn)在又是網(wǎng)絡(luò)時(shí)代又固,網(wǎng)絡(luò)中進(jìn)程通信是無處不在仲器,這就是我為什么說“一切皆socket”。
  • socket起源于Unix仰冠,而Unix/Linux基本哲學(xué)之一就是“一切皆文件”乏冀,都可以用“打開open –> 讀寫write/read –> 關(guān)閉close”模式來操作。我的理解就是Socket就是該模式的一個(gè)實(shí)現(xiàn)洋只,socket即是一種特殊的文件辆沦,一些socket函數(shù)就是對其進(jìn)行的操作(讀/寫IO昼捍、打開、關(guān)閉)
    關(guān)于如下問題參考這里
網(wǎng)絡(luò)中進(jìn)程之間如何通信肢扯?
socket的基本操作
    socket()函數(shù)
    bind()函數(shù)
    listen()妒茬、connect()函數(shù)
    accept()函數(shù)
    read()、write()函數(shù)等
    close()函數(shù)

iOS網(wǎng)絡(luò)編程層次模型


  • Cocoa層:NSURL蔚晨,Bonjour乍钻,Game Kit,WebKit
  • Core Foundation層:基于 C 的 CFNetwork 和 CFNetServices
  • OS層:基于 C 的 BSD socket

Cocoa層:是最上層的基于 Objective-C 的 API铭腕,比如 URL訪問银择,NSStream,Bonjour累舷,GameKit等浩考,這是大多數(shù)情況下我們常用的 API。Cocoa 層是基于 Core Foundation 實(shí)現(xiàn)的被盈。

Core Foundation層:因?yàn)橹苯邮褂?socket 需要更多的編程工作析孽,所以蘋果對 OS 層的 socket 進(jìn)行簡單的封裝以簡化編程任務(wù)。該層提供了 CFNetwork 和 CFNetServices只怎,其中 CFNetwork 又是基于 CFStream 和 CFSocket袜瞬。

OS層:最底層的 BSD socket 提供了對網(wǎng)絡(luò)編程最大程度的控制,但是編程工作也是最多的身堡。因此吞滞,蘋果建議我們使用 Core Foundation 及以上層的 API 進(jìn)行編程。

C/S架構(gòu)程序設(shè)計(jì)基本框架


常用的Socket類型
有兩種:流式Socket(SOCK_STREAM)和數(shù)據(jù)報(bào)式Socket(SOCK_DGRAM)盾沫。流式是一種面向連接的Socket,針對于面向連接的TCP服務(wù)應(yīng)用殿漠;數(shù)據(jù)報(bào)式Socket是一種無連接的Socket赴精,對應(yīng)于無連接的UDP服務(wù)應(yīng)用。

  • TCP C/S架構(gòu)程序設(shè)計(jì)基本框架
  • UDP C/S架構(gòu)程序設(shè)計(jì)基本框架

SocketDemo


主要使用:NSStream绞幌、CFStream蕾哟、CFSocket

效果圖

客戶端 + 服務(wù)端.png

客戶端主要代碼完整代碼下載地址

#include <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
static ViewController *selfClass =nil;

@interface ViewController ()<NSStreamDelegate>
{
    NSInteger flag;
}

@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, assign) NSInteger kPort;

@property (weak, nonatomic) IBOutlet UITextField *port;

@property (weak, nonatomic) IBOutlet UITextField *serviceIP;

@property (weak, nonatomic) IBOutlet UILabel *message;

@property (weak, nonatomic) IBOutlet UITextField *putText;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    selfClass = self;
    // Do any additional setup after loading the view, typically from a nib.
}

- (IBAction)connectService:(id)sender {
    flag = 0;
    [self initNetworkCommunication];
}
- (IBAction)reviveMsg:(id)sender {
    flag = 1;
    [self initNetworkCommunication];
}
- (void)initNetworkCommunication {
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.serviceIP.text, (int)[self.port.text intValue], &readStream, &writeStream);
    _inputStream = (__bridge_transfer NSInputStream *)readStream;
    _outputStream = (__bridge_transfer NSOutputStream *)writeStream;
    
    [_inputStream setDelegate:self];
    [_outputStream setDelegate:self];
    
    [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    [_inputStream open];
    [_outputStream open];
    
}

- (void)close {
    [_outputStream close];
    [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [_outputStream setDelegate:nil];
    [_inputStream close];
    [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [_inputStream setDelegate:nil];
}

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSString *event;
    switch (eventCode) {
        case NSStreamEventNone:
            event = @"NSStreamEventNone";
            NSLog(@"%@",event);
            break;
        case NSStreamEventOpenCompleted:
            event = @"NSStreamEventOpenCompleted";
            NSLog(@"%@",event);
            break;
        case NSStreamEventHasBytesAvailable:
            event = @"NSStreamEventHasBytesAvailable";
            NSLog(@"%@",event);
            if (flag == 1 && aStream == _inputStream) {
                NSMutableData *input = [[NSMutableData alloc] init];
                uint8_t buffer[1024];
                NSInteger len;
                while ([_inputStream hasBytesAvailable]) {
                    len = [_inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0) {
                        [input appendBytes:buffer length:len];
                    }
                }
                NSString *result = [[NSString alloc] initWithData:input encoding:NSUTF8StringEncoding];
                NSLog(@"%@",result);
                _message.text = result;
            }
        case NSStreamEventHasSpaceAvailable:
            event = @"NSStreamEventHasSpaceAvailable";
            NSLog(@"%@",event);
            if (flag ==0 && aStream == _outputStream) {
                //輸出
                NSString * putText = selfClass.putText.text;
                UInt8 buff[1024];
                memcpy(buff, [putText UTF8String],[putText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
                [_outputStream write:buff maxLength: strlen((const char*)buff)+1];
                //必須關(guān)閉輸出流否則莲蜘,服務(wù)器端一直讀取不會(huì)停止谭确,
                [_outputStream close];
            }
            break;
        case NSStreamEventErrorOccurred:
            event = @"NSStreamEventErrorOccurred";
            NSLog(@"%@",event);
            [self close];
            break;
        case NSStreamEventEndEncountered:
            event = @"NSStreamEventEndEncountered";
            NSLog(@"%@",event);
            NSLog(@"Error:%ld:%@",[[aStream streamError] code], [[aStream streamError] localizedDescription]);
            break;
        default:
            [self close];
            event = @"Unknown";
            break;
    }
}

服務(wù)端主要代碼完整代碼下載地址

#import "ViewController.h"
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>

static ViewController *selfClass =nil;
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *portText;
@property (nonatomic, assign) NSInteger kPORT;
@property (weak, nonatomic) IBOutlet UITextField *putText;
@property (weak, nonatomic) IBOutlet UILabel *readText;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    selfClass = self;
    // Do any additional setup after loading the view, typically from a nib.
}

- (IBAction)startService:(id)sender {
    //設(shè)置服務(wù)端端口號(hào)
    self.kPORT = [self.portText.text integerValue];
    
    CFSocketRef service;
    /*CFSocketContext 參數(shù)cgindex version 版本號(hào)票渠,必須為0逐哈;
     *void *info; 一個(gè)指向任意程序定義數(shù)據(jù)的指針,可以在CFScocket對象剛創(chuàng)建的時(shí)候與之關(guān)聯(lián)问顷,被傳遞給所有在上下文中回調(diào),可為NULL昂秃;
     *CFAllocatorRetainCallBack retain; info指針中的retain回調(diào)禀梳,可以為NULL
     *CFAllocatorReleaseCallBack release; info指針中的release的回調(diào),可以為NULL
     *CFAllocatorCopyDescriptionCallBack copyDescription; info指針中的回調(diào)描述肠骆,可以為NULL
     */
    CFSocketContext CTX = {0,NULL,NULL,NULL,NULL};
    //CFSockerRef
    //內(nèi)存分配類型算途,一般為默認(rèn)的Allocator->kCFAllocatorDefault,
    //協(xié)議族,一般為Ipv4:PF_INET,(Ipv6,PF_INET6),
    //套接字類型,TCP用流式—>SOCK_STREAM蚀腿,UDP用報(bào)文式->SOCK_DGRAM,
    //套接字協(xié)議嘴瓤,如果之前用的是流式套接字類型:IPROTO_TCP,如果是報(bào)文式:IPPROTO_UDP,
    //回調(diào)事件觸發(fā)類型 *1,
    //觸發(fā)時(shí)候調(diào)用的方法 *2,
    //用戶定義的數(shù)據(jù)指針莉钙,用于對CFSocket對象的額外定義或者申明廓脆,可以為NULL
    service = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
    if (service == NULL) {
        return;
    }
    //設(shè)置是否重新綁定標(biāo)志
    int yes = 1;
    /* 設(shè)置socket屬性 SOL_SOCKET是設(shè)置tcp SO_REUSEADDR是重新綁定,yes 是否重新綁定*/
    setsockopt(CFSocketGetNative(service), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
    
    //設(shè)置端口和地址
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));       //memset函數(shù)對指定的地址進(jìn)行內(nèi)存拷貝
    addr.sin_len = sizeof(addr);
    addr.sin_family = AF_INET;            //AF_INET是設(shè)置 IPv4
    addr.sin_port = htons(self.kPORT);    //htons函數(shù) 無符號(hào)短整型數(shù)轉(zhuǎn)換成“網(wǎng)絡(luò)字節(jié)序”
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  //INADDR_ANY有內(nèi)核分配胆胰,htonl函數(shù) 無符號(hào)長整型數(shù)轉(zhuǎn)換成“網(wǎng)絡(luò)字節(jié)序”
    
    /* 從指定字節(jié)緩沖區(qū)復(fù)制狞贱,一個(gè)不可變的CFData對象*/
    CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr, sizeof(addr));
    
    /* 設(shè)置Socket*/
    if (CFSocketSetAddress(service, (CFDataRef)address) != kCFSocketSuccess) {
        fprintf(stderr, "Socket綁定失敗\n");
        CFRelease(service);
        return ;
    }
    /* 創(chuàng)建一個(gè)Run Loop Socket源 */
    CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, service, 0);
    /* Socket源添加到Run Loop中 */
    CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
    CFRelease(sourceRef);
    
    printf("Socket listening on port %zd\n", self.kPORT);
    /* 運(yùn)行Loop */
    CFRunLoopRun();
    
    
}

//接受客戶端請求后回調(diào)函數(shù)
void AcceptCallBack(
                    CFSocketRef socket,
                    CFSocketCallBackType type,
                    CFDataRef address,
                    const void *data,
                    void *info)
{
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    
    //data參數(shù)的含義是,如果是kCFSocketAcceptCallBack類型蜀涨,data是CFSocketNativeHandle類型的指針
    CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;
    
    //創(chuàng)建讀寫socket流
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock, &readStream, &writeStream);
    
    if (!readStream || !writeStream) {
        close(sock);
        fprintf(stderr, "CFStreamCreatePairWithSocket() 失敗\n");
        return;
    }
    
    CFStreamClientContext streamCtxt = {0,NULL,NULL,NULL,NULL};
    //注冊兩種回調(diào)函數(shù)
    CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, ReadStreamClientCallBack, &streamCtxt);
    CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, WriteStreamClientCallBack, &streamCtxt);
    
    //加入到循環(huán)當(dāng)中
    CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
    CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
    
    CFReadStreamOpen(readStream);
    CFWriteStreamOpen(writeStream);
}

//讀取操作瞎嬉,讀取客戶端發(fā)送的數(shù)據(jù)
static UInt8 buff[255];
void ReadStreamClientCallBack (CFReadStreamRef stream,CFStreamEventType eventType, void* clientCallBackInfo ) {
    
    CFReadStreamRef inputStream = stream;
    
    if (NULL != inputStream) {
        CFReadStreamRead(inputStream, buff, 255);
        
        printf("接收到的數(shù)據(jù) :%s\n", buff);
        selfClass.readText.text = [NSString stringWithCString:(char*)buff encoding:NSUTF8StringEncoding];
        CFReadStreamClose(inputStream);
        //從循環(huán)中移除
        CFReadStreamUnscheduleFromRunLoop(inputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
        inputStream = NULL;
    }
    
}

/* 寫入流操作 客戶端在讀取數(shù)據(jù)時(shí)候調(diào)用 */
void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo)
{
    CFWriteStreamRef    outputStream = stream;
    //輸出
    NSString * putText = selfClass.putText.text;
    UInt8 buff[1024];
    memcpy(buff, [putText UTF8String],[putText lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
    
    if(NULL != outputStream)
    {
        CFWriteStreamWrite(outputStream, buff, strlen((const char*)buff)+1);
        //關(guān)閉輸出流
        CFWriteStreamClose(outputStream);
        //從循環(huán)中移除
        CFWriteStreamUnscheduleFromRunLoop(outputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
        outputStream = NULL;
    }
}

寫在最后


客戶端完整代碼下載地址
服務(wù)端完整代碼下載地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市厚柳,隨后出現(xiàn)的幾起案子氧枣,更是在濱河造成了極大的恐慌别垮,老刑警劉巖碳想,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逊移,死亡現(xiàn)場離奇詭異龙填,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扇商,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來红且,“玉大人暇番,你說我怎么就攤上這事”诔辏” “怎么了舆乔?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吊宋。 經(jīng)常有香客問我颜武,道長鳞上,這世上最難降的妖魔是什么篙议? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任移怯,我火速辦了婚禮这难,結(jié)果婚禮上雁佳,老公的妹妹穿的比我還像新娘糖权。我一直安慰自己星澳,他們只是感情好禁偎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布腿堤。 她就那樣靜靜地躺著,像睡著了一般笆檀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酗洒,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音樱衷,去河邊找鬼。 笑死酒唉,一個(gè)胖子當(dāng)著我的面吹牛矩桂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侄榴,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼牲蜀,長吁一口氣:“原來是場噩夢啊……” “哼涣达!你這毒婦竟也來了度苔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤箩张,失蹤者是張志新(化名)和其女友劉穎甩骏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體先慷,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饮笛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了论熙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片福青。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出无午,到底是詐尸還是另有隱情媒役,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布宪迟,位于F島的核電站酣衷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏踩验。R本人自食惡果不足惜鸥诽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望箕憾。 院中可真熱鬧牡借,春花似錦、人聲如沸袭异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽御铃。三九已至碴里,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間上真,已是汗流浹背咬腋。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睡互,地道東北人根竿。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓润匙,卻偏偏與公主長得像忿偷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子朱躺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Socket基礎(chǔ)概念 網(wǎng)絡(luò)中進(jìn)程之間如何通信妻怎? 網(wǎng)絡(luò)中進(jìn)程之間如何通信壳炎?首要解決的問題是如何唯一標(biāo)識(shí)一個(gè)進(jìn)程,否則...
    DiamondsAndRust閱讀 4,756評(píng)論 2 54
  • 原文地址:http://www.cnblogs.com/skynet/archive/2010/12/12/190...
    archyly閱讀 1,065評(píng)論 0 8
  • 大綱 一.Socket簡介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
    VD2012閱讀 2,361評(píng)論 0 5
  • 前言 我們深諳信息交流的價(jià)值逼侦,那網(wǎng)絡(luò)中進(jìn)程之間如何通信匿辩,如我們每天打開瀏覽器瀏覽網(wǎng)頁時(shí),瀏覽器的進(jìn)程怎么與web服...
    Chars閱讀 2,985評(píng)論 2 117
  • 今天把楊勇先生的《中國式眾籌》又重新閱讀了一遍榛丢,對于眾籌咖啡館的功能和定位铲球,又有了進(jìn)一步的深刻理解,其實(shí)不限于咖啡...
    賀華文PCC教練閱讀 505評(píng)論 0 1