TCP/IP網(wǎng)絡(luò)編程筆記

一统抬、基礎(chǔ)

1.1 套接字定義

套接字是網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)能浖O(shè)備。我們可以通過套接字完成數(shù)據(jù)傳輸灭贷。

1.2 相關(guān)函數(shù)

網(wǎng)絡(luò)編程中接受連接請求的套接字創(chuàng)建過程如下:

  1. 調(diào)用socket函數(shù)創(chuàng)建套接字
  2. 調(diào)用bind函數(shù)分配IP和端口號
  3. 調(diào)用listen函數(shù)轉(zhuǎn)換為可接受請求狀態(tài)
  4. 調(diào)用accept受理連接請求
  • Linux平臺相關(guān)函數(shù)
    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);
         成功返回文件描述符温学,失敗返回-1
    int bind(int socketfd, struct sockadr* myaddr, socklen_t addrlen);
    int listen(int socketfd, int backlog);
    int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
    int connect(int sockdf, struct sockaddr* serv_addr, socklen_t addrlen);
         成功返回0,失敗返回-1
    
  • Windows平臺相關(guān)函數(shù)
    #include <winsock2.h>
    //需要鏈接ws2_32.lib
      //初始化
    int WSAStartup(WORD wVersionRequested,LPWSADATA lpwSAData);
            成功返回0甚疟,失敗返回錯誤代碼
            wVersionRequested    使用的Winsock版本信息
            lpwSAData                   WSADATA結(jié)構(gòu)體變量的地址
    MAKEWORD(1仗岖,2);  //宏函數(shù),主版本為1览妖,副版本2轧拄,返回0x0201
    //注銷
    int WSACleanup();
       成功返回0,失敗返回SOCKET_ERROR
    //套接字
    SOCKET socket(int af, int type, int protocol);
       成功時返回套接字句柄讽膏,失敗返回INVALID_SOCKET
    int bind(SOCKET s, const struct sockaddr* name, int namelen);
       成功返回0檩电,失敗返回SOCKET_ERROR
    int  listen(SOCKET s, int backlog);
       成功返回0,失敗返回SOCKET_ERROR
    SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
       成功時返回套接字句柄府树,失敗返回INVALID_SOCKET
    int connect(SOCKET s, const struct sockaddr* name, int namelen);
       成功返回0是嗜,失敗返回SOCKET_ERROR
    int closesocket(SOCKET s);
       成功返回0,失敗返回SOCKET_ERROR
    

1.3 類型和設(shè)置

1.3.1 協(xié)議(Protocol)

  • 協(xié)議族分類
    • PF_INET IPV4協(xié)議族
    • PF_INET6 IPV6協(xié)議族
    • PF_LOCAL 本地通信的Unix協(xié)議族
    • PF_PACKET 底層套接字的協(xié)議族
    • PF_IPX IPX Novell協(xié)議族
      通過套接字的第一個參數(shù)傳遞使用的協(xié)議分類信息挺尾,domain決定使用的protocol鹅搪。
  • 套接字類型
    類型指的是套接字的數(shù)據(jù)傳輸方式,決定了協(xié)議族不能決定數(shù)據(jù)傳輸方式遭铺,如PF_INET可能存在多種數(shù)據(jù)傳輸方式丽柿。
    • SOCK_STREAM(面向連接的套接字)
      面向連接的套接字具有以下特點:

      • 傳輸過程中數(shù)據(jù)不會消失
      • 按序傳輸數(shù)據(jù)
      • 傳輸?shù)臄?shù)據(jù)不存在數(shù)據(jù)邊界

      收發(fā)數(shù)據(jù)的內(nèi)部有緩沖(字節(jié)數(shù)組)。套接字傳輸?shù)臄?shù)據(jù)保存到該數(shù)組魂挂。在數(shù)組容量內(nèi)甫题,可能在充滿緩沖后一次讀取,也可能分多次讀取涂召。如果緩沖被填滿坠非,傳輸套接字將停止傳輸。

    • SOCK_DGRAM(面向消息的套接字)
      面向消息的套接字特點: 不可靠的果正、不按序傳遞炎码、以數(shù)據(jù)的高速傳遞為目的的套接字盟迟。

1.4 地址族和字節(jié)序

1.4.1 地址族

IP是網(wǎng)絡(luò)協(xié)議的簡寫鸠儿,是為了收發(fā)網(wǎng)絡(luò)數(shù)據(jù)而分配給計算機的值浓体。只要有IP就能像數(shù)據(jù)發(fā)送到目標計算機,但無法發(fā)送給最終的應(yīng)用程序茎活。
端口號是為了區(qū)分程序中創(chuàng)建的套接字而分配給套接字的序號歉闰。NIC通過IP向計算機內(nèi)部發(fā)生數(shù)據(jù)辖众,操作系統(tǒng)利用端口號將數(shù)據(jù)分配給套接字。
IP地址分為IPV4(4字節(jié))和IPV6(16字節(jié))兩類和敬。IPV6是為了解決201年前后IP地址耗盡而提出的標準凹炸。IPV4分為A、B昼弟、C啤它、D四類,由網(wǎng)絡(luò)ID和主機主機ID構(gòu)成私杜。 IP網(wǎng)絡(luò)數(shù)據(jù)傳輸時蚕键,先基于網(wǎng)絡(luò)地址(網(wǎng)絡(luò)ID)把數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)。網(wǎng)絡(luò)收到數(shù)據(jù)后衰粹,根據(jù)主機ID將數(shù)據(jù)發(fā)送到目標計算機锣光。
地址分類

  • A類地址首字節(jié) 0-127,首位以0開始
  • B類地址首字節(jié)128-191铝耻,首位以10開始
  • C類地址首字節(jié)192-223誊爹,首位以110開始

端口號

  • 端口號由16位構(gòu)成,可分配端口號為0-65535
  • 知名端口號:0-1023瓢捉。一般分配給特定程序频丘。

1.4.2 地址信息表示

struct sockaddr_in
{
    sa_family_t       sin_family;  //地址族
    uint16_t          sin_port;     //16位端口號
    struct in_addr    sin_addr;  //32位IP地址
    char              sin_zero[8];  //不使用
}

in_addr結(jié)構(gòu)體聲明為uint32_t,只需要當作32位整數(shù)泡态。
sin_zero是使結(jié)構(gòu)體與socd_addr保持一致而插入的成員搂漠。
之所以使用sockaddr_in,而不直接使用sockaddr某弦,原因在于sockaddr結(jié)構(gòu)體桐汤,如下:

struct sock_addr
{
    sa_family_t sin_family;
    char sa_data[14];
}

sa_data中包含地址和端口號,其余部分為0靶壮。直接填充這些信息會比較麻煩怔毛,于是有了sockaddr_in,最后轉(zhuǎn)換為sockaddr型結(jié)構(gòu)體。

1.4.3 字節(jié)序

大端序: 高位字節(jié)放在地位地址
小端序: 高位字節(jié)放在高位地址
在通過網(wǎng)絡(luò)傳輸數(shù)據(jù)時約定統(tǒng)一方式腾降,即網(wǎng)絡(luò)字節(jié)序拣度。網(wǎng)絡(luò)字節(jié)序是大端序
字節(jié)序轉(zhuǎn)換

unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
//h--->host字節(jié)序
//n-->network字節(jié)序
//s-->2字節(jié)short,常用于端口號轉(zhuǎn)換
//l-->4字節(jié)抗果,常用于IP轉(zhuǎn)換

地址字符串的字節(jié)序轉(zhuǎn)換

#include <arpa/inet.h>

in_addr_t inet_addr(const char* string);  //點分十進制字符串---> 大端序整數(shù)值

int inet_aton(const char* string,  struct in_addr* addr);  //string轉(zhuǎn)換結(jié)果保存到addr地址值中

char* inet_ntoa(struct in_addr adr);  //轉(zhuǎn)換為字符串形式筋帖。長期保存時,需要對char*內(nèi)容進行復(fù)制

網(wǎng)絡(luò)地址初始化

struct sockaddr_in addr;
char* server_ip = "211.217.168.11"; //ip字符串
char* serv_port = "9190";  //端口號

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(server_ip);
addr.sin_port = htons(server_port);

INADDR_ANY
利用INADDR_ANY分配Ip地址窖张,將自動獲取計算機的地址幕随,若計算機有多個IP地址蚁滋,則可以從不同IP宿接、相同端口號中接收數(shù)據(jù)。服務(wù)器一般采用這種方式辕录。

1.5 域名和網(wǎng)絡(luò)地址

將容易記睦霎、易表達的域名分配并取代IP地址。域名是賦予服務(wù)器端的虛擬地址走诞,而非實際地址副女。通過DNS請求轉(zhuǎn)換地址。若DNS服務(wù)器無法解析蚣旱,會詢問其他DNS服務(wù)器碑幅,并提供給用戶。

//
#include <netdb.h>

struct hostent* gethostbyname(const char* hostname);

structhostent* gethostbyaddr(const char* addr, socklen_t len, int family);

1.6套接字 多種可選項

1.6.1 可選項

套接字可選項是分層的塞绿。IPPROTO_IP是IP協(xié)議相關(guān)選項沟涨,IPPROTO_TCP是tcp協(xié)議相關(guān)可選項,SOL_SOCKET是套接字相關(guān)通用可選項异吻。
設(shè)置可選項

#include <sys/socket.h>

int getsockopt(int sock, int level, int optname, void* optval, socklen_t* optlen);
     sock 套接字文件描述符
     level 協(xié)議層
     optname 可選項名字
     optval    保持查看結(jié)果的緩沖地址
     optlen   緩沖大小

int setsockopt(int sock, int level, int optname, const void* optval, socklen_t optlen);

SO_TYPE 查看套接字類型
SO_SNDBUF/SO_RCVBUF 輸入輸出緩沖區(qū)大小
SO_REUSEADDR :默認為0(假),即無法分配Time-wait狀態(tài)下的套接字端口號, 為1(真)可將Time-wait狀態(tài)下的套接字端口號重新分配給新的套接字
套接字四次揮手后并非立即清除,二十經(jīng)過一段時間的Time-wait狀態(tài)裹赴。先斷開連接(發(fā)送FIN消息)的主機才經(jīng)過Time-wait狀態(tài)。因此服務(wù)器先斷開連接诀浪,無法立即重新運行棋返。
TCP_NODEALY:設(shè)置為1時,禁用Nagle算法雷猪。
tcp套接字默認使用Nagle算法(只有收到前一數(shù)據(jù)的ACK時,才發(fā)送下一數(shù)據(jù))交換數(shù)據(jù),因此最大限度的進行緩沖,直到收到ACK睛竣。為了提高網(wǎng)絡(luò)傳輸效率,必須使用Nagle算法求摇。但在網(wǎng)絡(luò)流量未受到太大影響時射沟,不使用Nagle算法會更快。如大文件數(shù)據(jù)月帝。

1.7 進程

1.8 IO復(fù)用與IO函數(shù)

無論連接多少客戶端躏惋,提供服務(wù)的進程只有一個。select是最具代表性的服務(wù)端復(fù)用方法嚷辅。

1.9 線程

1.10 信號處理

二簿姨、TCP

2.1 定義

IP層解決數(shù)據(jù)傳輸中的路徑選擇問題。TCP和UDP利用IP層提供的路徑信息完成實際的數(shù)據(jù)傳輸。

2.2 服務(wù)端

TCP服務(wù)端函數(shù)調(diào)用通常遵循如下順序:

  1. socket -- 創(chuàng)建套接字
  2. bind -- 分配套接字地址
  3. listen -- 等待連接請求狀態(tài)
  4. accept -- 允許連接
  5. read/wrote -- 數(shù)據(jù)交換
  6. close -- 斷開連接

listen后扁位,若有新的連接請求准潭,應(yīng)該按序處理。受理請求意味著進入可接受數(shù)據(jù)的狀態(tài)泼掠。
accept受理連接請求。其內(nèi)部產(chǎn)生用于數(shù)據(jù)IO的套接字择镇,并返回其文件描述符。
客戶端通過connect發(fā)起連接請求,當服務(wù)端接收連接請求或者發(fā)生異常中斷連接請求時返回苏携。服務(wù)端接收連接請求只是將請求記錄加入等待隊列衩侥,并不表示調(diào)用accept,不表示立即進行數(shù)據(jù)交換屡久。

2.3 迭代服務(wù)端

通過插入循環(huán)語句详幽,反復(fù)調(diào)用accept函數(shù)來持續(xù)受理客戶端連接請求的方式版姑。

2.4 迭代回聲服務(wù)/客戶端

TCP數(shù)據(jù)傳輸不存在邊界。服務(wù)端通過1次write函數(shù)傳輸數(shù)據(jù)健爬,但是如果數(shù)據(jù)太大夭拌,可能會分成多個數(shù)據(jù)包發(fā)送镶骗。另外,客戶端也可以未完全收到數(shù)據(jù)就read。解決方法是提前確定接收數(shù)據(jù)的大小。在無法預(yù)知數(shù)據(jù)大小的情況下,需要應(yīng)用層協(xié)議的定義。收發(fā)數(shù)據(jù)的過程中定義號規(guī)則以表示數(shù)據(jù)的邊界。

2.5 半關(guān)閉

IO緩沖特性:

  • I/O緩沖在每個套接字中單獨存在
  • 在創(chuàng)建套接字時自動生成
  • 關(guān)閉套接字后巴元,也會繼續(xù)傳輸輸出緩沖中的遺留數(shù)據(jù)
  • 關(guān)閉套接字修己,會丟失輸入緩沖中的數(shù)據(jù)

Linux的close和Windows的closescket意味著完全斷開連接尤辱,即無法傳輸和接受數(shù)據(jù)厢岂。這樣可能存在主機A發(fā)送數(shù)據(jù)后斷開連接光督,無法繼續(xù)接收B的數(shù)據(jù)结借。因此船老,Half-close產(chǎn)生堪置,即只關(guān)閉1個流坎匿。

半關(guān)閉函數(shù)

#include <sys/socket.h>

int shutdown(int sock, int howto);
     //howto為斷開方式信息

斷開方式如下:

  • SHUT_RD 斷開輸入流
  • SHUT_WR 斷開輸出流
  • SHUT_RWDR 同時斷開輸入輸出流

shutdown進行半關(guān)閉盾剩,同時發(fā)送EOF雷激。

2.6 并發(fā)服務(wù)器

2.7 IO分離

2.8 select服務(wù)端

2.8.1 select調(diào)用過程

  1. 設(shè)置文件描述符、指定監(jiān)視范圍驻粟、設(shè)置超時
  2. 調(diào)用select函數(shù)
  3. 查看調(diào)用結(jié)果

2.9 多線程服務(wù)端

三根悼、 UDP

3.1 定義

UDP提供不可靠數(shù)據(jù)傳輸。UDP不提供流控制機制蜀撑。根據(jù)端口號將傳到主機的數(shù)據(jù)交付給最終的UDP套接字挤巡。
TCP比UDP慢主要由于:

  • 收發(fā)數(shù)據(jù)前后進行的連接設(shè)置和清楚操作
  • 收發(fā)數(shù)據(jù)過程中為保證可靠性而添加的流控制

3.2 UDP服務(wù)端/客戶端

3.2.1 UDP數(shù)據(jù)IO函數(shù)

#include <sys/cosket.h>

ssize_t sendto(int sock, void* buf, size_t bnytes, int flags,  struct sockaddr* to, socklen_t addrlen)

ssize_t recvfrom(int sock, void* buf, size_t nbytes, int flags, struct sockaddr* from, socklen_t* addrlen)

3.2.2 套接字地址分配

sendto前完成地址分配。如果sendto時未分配地址信息酷麦,首次調(diào)用時自動分配IP和端口號矿卑,分配結(jié)果保留到程序結(jié)束。IP為主機IP沃饶,端口號為任意未分配端口母廷。
此外,也可以用bind分配ip和端口號绍坝。

3.3 數(shù)據(jù)傳輸特性和connect

存在數(shù)據(jù)邊界徘意。通過snedto傳輸數(shù)據(jù)過程如下:

  1. 向套接字注冊目標IP和端口號
  2. 傳輸數(shù)據(jù)
  3. 刪除注冊信息
    未注冊目標信息的套接字稱為未連接套接字。反之稱為connected套接字轩褐。
    向同一目標主機進行長時間通信時,將UDP套接字變?yōu)閏onnected套接字會提高效率玖详。傳輸過程中把介,1和3占傳輸?shù)?/3時間。

3.4 多播與廣播

四蟋座、Windows平臺

4.1 異步IO

4.2 重疊IO

4.3 IOCP

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拗踢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子向臀,更是在濱河造成了極大的恐慌巢墅,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件券膀,死亡現(xiàn)場離奇詭異君纫,居然都是意外死亡,警方通過查閱死者的電腦和手機芹彬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門蓄髓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舒帮,你說我怎么就攤上這事会喝《傅” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵肢执,是天一觀的道長枉阵。 經(jīng)常有香客問我,道長预茄,這世上最難降的妖魔是什么岭妖? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮反璃,結(jié)果婚禮上昵慌,老公的妹妹穿的比我還像新娘。我一直安慰自己淮蜈,他們只是感情好斋攀,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著梧田,像睡著了一般淳蔼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裁眯,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天鹉梨,我揣著相機與錄音,去河邊找鬼穿稳。 笑死存皂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的逢艘。 我是一名探鬼主播旦袋,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼它改!你這毒婦竟也來了疤孕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤央拖,失蹤者是張志新(化名)和其女友劉穎祭阀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲜戒,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡专控,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了袍啡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踩官。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖境输,靈堂內(nèi)的尸體忽然破棺而出蔗牡,到底是詐尸還是另有隱情颖系,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布辩越,位于F島的核電站嘁扼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏黔攒。R本人自食惡果不足惜趁啸,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望督惰。 院中可真熱鬧不傅,春花似錦、人聲如沸赏胚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽觉阅。三九已至崖疤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間典勇,已是汗流浹背劫哼。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留割笙,地道東北人权烧。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像咳蔚,于是被迫代替她去往敵國和親豪嚎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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