# iOS中Socket開發--TCP篇

1.Socket簡介

Socket

百度百科:

網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個socket。
Socket的英文原義是“孔”或“插座”。作為BSD UNIX的進程通信機制,取后一種意思。通常也稱作"套接字",用于描述IP地址和端口,是一個通信鏈的句柄,可以用來實現不同虛擬機或不同計算機之間的通信。在Internet上的主機一般運行了多個服務軟件,同時提供幾種服務。每種服務都打開一個Socket,并綁定到一個端口上,不同的端口對應于不同的服務。Socket正如其英文原意那樣,像一個多孔插座。一臺主機猶如布滿各種插座的房間,每個插座有一個編號,有的插座提供220伏交流電, 有的提供110伏交流電,有的則提供有線電視節目。 客戶軟件將插頭插到不同編號的插座,就可以得到不同的服務。

圖說Socket

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層, 它是一組接口。

2.TCP和UDP的區別

TCP

面向連接、傳輸可靠(保證數據正確性,保證數據順序)、用于傳輸大量數據(流模式)、速度慢,建立連接需要開銷較多(時間,系統資源)。

UDP

面向非連接、傳輸不可靠、用于傳輸少量數據(數據包模式)速度快

區別

關于TCP是一種流模式的協議UDP是一種數據報模式的協議,這里要說明一下,TCP是面向連接的,也就是說,在連接持續的過程中,socket中收到的數據都是由同一臺主機發出的(劫持什么的不考慮),因此,知道保證數據是有序的到達就行了,至于每次讀取多少數據自己看著辦。

UDP是無連接的協議,也就是說,只要知道接收端的IP和端口,且網絡是可達的,任何主機都可以向接收端發送數據。這時候,如果一次能讀取超過一個報文的數據,則會亂套。比如,主機A向發送了報文P1,主機B發送了報文P2,如果能夠讀取超過一個報文的數據,那么就會將P1和P2的數據合并在了一起,這樣的數據是沒有意義的。

3.TCP的三次握手和四次揮手

TCP創建過程和連接拆除過程是由TCP/IP協議棧自動創建的。在此通過講解過程,期望能對TCP底層的運行機制的理解有所幫助。

TCP三次握手

所謂三次握手(Three-way Handshake),是指建立一個TCP連接時,需要客戶端和服務器總共發送3個包

三次握手的目的是連接服務器指定端口,建立TCP連接,并同步連接雙方的序列號和確認號并交換 TCP 窗口大小信息.在socket編程中,客戶端執行connect()時。將觸發三次握手。


了解一下幾個標識,SYN(synchronous),同步標識ACK (Acknowledgement),即確認標識seq應該是Sequence Number,序列號的意思,另外還有四次揮手的fin,應該是final,表示結束標識

第一次握手:客戶端發送一個TCP的SYN標識位置1的包指明客戶打算連接的服務器的端口,以及初始序號X,保存在包頭的序列號(Sequence Number)字段里。

第二次握手:服務器發回確認包(ACK)應答。即SYN標識位和ACK標識位均為1同時,將確認序號(Acknowledgement Number)設置為客戶的序列號加1以,即X+1。

第三次握手:客戶端再次發送確認包(ACK) SYN標識位為0,ACK標識位為1。并且把服務器發來ACK的序號字段+1,放在確定字段中發送給對方.并且在數據段放寫序列號的+1。

TCP的四次揮手

TCP的連接的拆除需要發送四個包,因此稱為四次揮手(four-way handshake)。客戶端或服務器均可主動發起揮手動作,在socket編程中,任何一方執行close()操作即可產生揮手操作。


其實有個問題,為什么連接的時候是三次握手,關閉的時候卻是四次揮手?

因為當Server端收到Client端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能并不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。

5.TCP的Socket具體實現

iOS提供了Socket網絡編程的接口CFSocket,不過這里使用BSD Socket。

基本TCP客戶—服務器程序設計基本框架


常用的Socket類型有兩種:流式Socket(SOCK_STREAM)和數據報式Socket(SOCK_DGRAM)流式是一種面向連接的Socket,針對于面向連接的TCP服務應用數據報式Socket是一種無連接的Socket,對應于無連接的UDP服務應用

Socket調用的主要庫函數

創建套接字

Socket(af, type, protocol)

建立地址和套簽字的練習

bind(sockid, local addr, addrlen)

服務器端監聽客戶端的請求

listen(Sockid, quenlen)

建立服務器/客戶端的連接(面向連接TCP)
客戶端請求連接

Connect(Sockid, destaddr, addrlen)

服務器端等待從編號為Sockid的Socket上接收客戶連接請求

newsockid = accept(Sockid,Clientaddr, paddrlen)

發送/接收數據
面向對象

send(sockid, buff, bufflen)
recv()

面向無連接

sendto(sockid, buff, ..., addrlen)
recvform()

釋放嵌套字

close(socked)

TCP下Socket具體實現

服務器的工作流程:首先調用socket函數創建一個Socket,然后調用bind函數將其與本機地址以及一個本地端口號綁定,然后調用listen在相應的socket上監聽,當accpet接收到一個連接服務請求時,將生成一個新的socket。服務器顯示該客戶機的IP地址,并通過新的socket向客戶端發送字符串" hi,I am server!"。最后關閉該socket。

服務器參考代碼:

#import <Foundation/Foundation.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int err = 0;
        int fd = socket(AF_INET, SOCK_STREAM, 0);
        BOOL success = (fd != -1);
        // 第一次握手
        if (success) {
            NSLog(@"socket success");
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            addr.sin_len = sizeof(addr);
            addr.sin_family = AF_INET;
            addr.sin_port = htons(1024);
            addr.sin_addr.s_addr = INADDR_ANY;
            err = bind(fd, (const struct sockaddr *)&addr, sizeof(addr));
            success = (err == 0);
        }
        // 第二次握手
        if (success) {
            NSLog(@"bind(綁定) success");
            err = listen(fd, 5);//開始監聽
            success = (err == 0);
        }
        // 第三次握手
        if (success) {
            NSLog(@"listen success");
            while (true) {
                struct sockaddr_in peeraddr;
                int peerfd;
                socklen_t addrLen;
                addrLen = sizeof(peeraddr);
                NSLog(@"prepare accept");
                peerfd = accept(fd, (struct sockaddr *)&peeraddr, &addrLen);
                success = (peerfd != -1);
                if (success) {
                    NSLog(@"accept success,remote address:%s,port:%d",inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
                    char buf[1024];
                    ssize_t count;
                    size_t len = sizeof(buf);
                    do {
                        count=recv(peerfd, buf, len, 0);
                        NSString* str = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];
                        NSLog(@"%@",str);
                    } while (strcmp(buf, "exit") != 0);
                }
                // 關閉
                close(peerfd);
            }
        }
    }
    return 0;
}

客戶端參考代碼

#import <Foundation/Foundation.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>

int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
        int err;
        int fd = socket(AF_INET, SOCK_STREAM, 0);
        BOOL success = (fd != -1);
        struct sockaddr_in addr;
        if (success) {
            NSLog(@"socket success");
            memset(&addr, 0, sizeof(addr));
            addr.sin_len=sizeof(addr);
            addr.sin_family=AF_INET;
            addr.sin_addr.s_addr=INADDR_ANY;
            err=bind(fd, (const struct sockaddr *)&addr, sizeof(addr));
            success=(err==0);
        }
        if (success) {
            struct sockaddr_in peeraddr;
            memset(&peeraddr, 0, sizeof(peeraddr));
            peeraddr.sin_len = sizeof(peeraddr);
            peeraddr.sin_family = AF_INET;
            peeraddr.sin_port = htons(1024);
//            peeraddr.sin_addr.s_addr = INADDR_ANY;
            peeraddr.sin_addr.s_addr = inet_addr("172.16.10.120");
//            這個地址是服務器的地址,
            socklen_t addrLen;
            addrLen = sizeof(peeraddr);
            NSLog(@"connecting");
            err = connect(fd, (struct sockaddr *)&peeraddr, addrLen);
            success = (err == 0);
            if (success) {
//                struct sockaddr_in addr;
                err = getsockname(fd, (struct sockaddr *)&addr, &addrLen);
                success = (err == 0);
                if (success) {
                    NSLog(@"connect success,local address:%s,port:%d",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
                    char buf[1024];
                    do {
                        printf("input message:");
                        scanf("%s", buf);
                        send(fd, buf, 1024, 0);
                    } while (strcmp(buf, "exit") != 0);
                }
            }
            else{
                NSLog(@"connect failed");
            }
        }
    }
    return 0;
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容