在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 0x10-網(wǎng)絡(luò)的世界
0x0E-單線程備份(下)
0x11-套接字編程-1
0x05-C語(yǔ)言指針:(Volume-1)
0x13-套接字編程-HTTP服務(wù)器(1)
0x0C-開(kāi)始行動(dòng)
C 語(yǔ)言進(jìn)階
第一部分
0x05-C語(yǔ)言指針(Volume-2)
0x08-C語(yǔ)言效率(下)
0x07-C語(yǔ)言效率(上)
0x04 C代碼規(guī)范
0x0F-多線程備份
0x05-C語(yǔ)言變量
第四部分
0x16-套接字編程-HTTP服務(wù)器(4)
0x0D-單線程備份(上)
總結(jié)
0x01-C語(yǔ)言序言
0x15-套接字編程-HTTP服務(wù)器(3)
0x14-套接字編程-HTTP服務(wù)器(2)
0x17-套接字編程-HTTP服務(wù)器(5)
第三部分
我的C語(yǔ)言
0x06-C語(yǔ)言預(yù)處理器
0x09-未曾領(lǐng)略的新風(fēng)景
0x0A-C線程和Glib的視角
第二部分
0x10-網(wǎng)絡(luò)的世界
0x12-套接字編程-2
0x03-C代碼
0x0B-C語(yǔ)言錯(cuò)誤處理

0x10-網(wǎng)絡(luò)的世界

0x10-網(wǎng)絡(luò)的世界

寫在最前方

  • 網(wǎng)絡(luò)編程沒(méi)有想象之中的難,但是同樣一句廢話,也沒(méi)有想象之后那么容易。
  • 接下來(lái)記錄的是對(duì)于網(wǎng)絡(luò)編程的一些教接近底層的東西,也就是稱之為系統(tǒng)接口函數(shù)的東西,通常叫做系統(tǒng)編程,
  • 當(dāng)然網(wǎng)絡(luò)編程在非學(xué)院派看來(lái),是使用一些成熟的庫(kù)(這是對(duì)于C語(yǔ)言來(lái)說(shuō),當(dāng)然很少有人愿意這么做,但個(gè)人覺(jué)得有了庫(kù)的C就和其他高級(jí)語(yǔ)言更像了)(注:C/C++都沒(méi)有標(biāo)準(zhǔn)網(wǎng)絡(luò)庫(kù),所以只能使用第三方開(kāi)發(fā)的庫(kù),所謂亂世出英雄。C++在 C++17 似乎要有了。), 例如libev這一類的。
  • 最后,還是先將底層基礎(chǔ)打好為妙。

開(kāi)始首先是萬(wàn)物根源的協(xié)議信息

概念

  • 最具誤導(dǎo)性的當(dāng)屬于 TCP/IP 協(xié)議了
    • 所謂 TCP/IP 協(xié)議指的并不是一個(gè)協(xié)議,往往在生活中聽(tīng)見(jiàn)的術(shù)語(yǔ)如:IP地址, TCP連接 等,總會(huì)被誤導(dǎo),以為就是一個(gè)東西
    • 實(shí)際上它們都是彼此獨(dú)立的 協(xié)議 ,只不過(guò)會(huì)相互合作罷了
    • TCP/IP說(shuō)的是一個(gè) 協(xié)議族 ,也就是說(shuō)是一堆協(xié)議的統(tǒng)稱
  • 對(duì)比 OSITCP/IP 參考模型:
OSI TCP/IP
應(yīng)用層 表示層 會(huì)話層 應(yīng)用層
傳輸層 傳輸層
網(wǎng)絡(luò)層 網(wǎng)絡(luò)層
鏈路層 物理層 網(wǎng)絡(luò)接口層
  • 其中最常接觸的
    • 位于 網(wǎng)絡(luò)層IP 協(xié)議,大家所熟知的 IP地址 就是由它進(jìn)行封裝并傳往下一層
    • 位于 傳輸層TCP/UDP 兩個(gè)協(xié)議, 一個(gè)是面向連接(STREAM), 一個(gè)是面向數(shù)據(jù)(DGRAM)的,實(shí)際上還有一個(gè)但這里不記錄。
    • 查看自身 網(wǎng)絡(luò)信息的辦法
      • *nix: 在 Terminal 中輸入 ifconfig -a
      • Windows: 在 PowerShell 中輸入 ipconfig
  • 概念模糊的 DNS
    • 其實(shí)很簡(jiǎn)單,它的作用就是用來(lái)找到域名所對(duì)應(yīng)的 IP地址
    • 為什么?因?yàn)?IP地址 太難記了!如果你覺(jué)得 IPv4 地址還難不倒你,那請(qǐng)你試試 IPv6
    • 怎么查看域名對(duì)應(yīng)的 IP地址,當(dāng)然先不考慮 CDN
      • *nixWindows 都可以通過(guò) ping <domain name> 命令進(jìn)行查詢
  • MAC地址端口號(hào)
    • 對(duì)于前者,實(shí)際上應(yīng)該是最熟悉不過(guò)的,對(duì)于網(wǎng)絡(luò)上的主機(jī)而言,每一臺(tái)主機(jī)就有一個(gè)專屬的 MAC地址
    • 后者則是相當(dāng)于一個(gè)房子的門,這個(gè)比喻在各大教材中廣泛引用,但也的確貼切,假設(shè) IP地址 是房子的地址,那么到了別人家要知道門在哪才行。

一個(gè)完整的應(yīng)用程序傳輸數(shù)據(jù)時(shí)候 封裝 的過(guò)程(從右二向左依次封裝):

以太網(wǎng)首部 IP TCP/UDP 真實(shí)數(shù)據(jù) 尾部
MAC地址 IP地址 TCP或者UDP協(xié)議 應(yīng)用程序數(shù)據(jù) 效驗(yàn)碼
源和目的MAC地址以及 及前層協(xié)議類型 源和目的端口號(hào)及前層應(yīng)用程序首部信息 應(yīng)用軟件信息和真正的數(shù)據(jù)

其中端口號(hào)實(shí)際上就是 應(yīng)用程序的信息

接收數(shù)據(jù)時(shí)的 拆解 順序與 封裝 正好相反。

  • 其中在傳輸過(guò)程中,作為接收方最開(kāi)始使用的是 網(wǎng)絡(luò)接口層/數(shù)據(jù)鏈路層 的驅(qū)動(dòng)程序(即操作系統(tǒng)自帶或另行安裝,總之不用使用的程序員寫就對(duì)了),來(lái)判斷這個(gè)包是否屬于我,判斷的依據(jù)就是 MAC地址,如果是再判斷什么協(xié)議

    • 在此處的協(xié)議可不止 IP協(xié)議, 也可能是 ARP協(xié)議 等。之后就是就事論事交給相應(yīng)的處理軟件去處理(拆解)就行
    • 科普: MAC地址是 48bit 的, 前24bitIEEE 分配, 后24bit 由廠商分配。原則上是唯一的。
  • MAC地址IP地址

    • 既然前方說(shuō)到 MAC地址IP地址 都能夠作為識(shí)別另一個(gè)主機(jī)的唯一標(biāo)識(shí),但是為什么需要有兩個(gè)相同功能的東西?
    • 是,在一開(kāi)始,網(wǎng)絡(luò)很小的情況下,例如我們?cè)谕粋€(gè)局域網(wǎng)中,我們之間需要通信的時(shí)候,只需要使用ARP協(xié)議,進(jìn)行廣播,向在一個(gè)網(wǎng)絡(luò)中的所有主機(jī)發(fā)送消息就行,剩下的就讓其他主機(jī)去判斷(通過(guò)MAC地址)這個(gè)數(shù)據(jù)是不是發(fā)給我的。
      • ARP協(xié)議 的作用就是在同一個(gè)網(wǎng)絡(luò)中,通過(guò) 廣播 找出符合自己要求的主機(jī)的 MAC地址 ,如果不在同一個(gè)網(wǎng)絡(luò)中,又想知道對(duì)方的 MAC地址, 那只能借助把每個(gè)網(wǎng)絡(luò)鏈接在一起的 網(wǎng)關(guān) 來(lái)幫助你發(fā)送 。 總之進(jìn)行網(wǎng)絡(luò)通信時(shí)必須知道對(duì)方的 IP地址 和 MAC地址
    • 但是如果是現(xiàn)在整個(gè)互聯(lián)網(wǎng)呢?不算 IPv6 ,就算 IPv4 也是幾十億的存在,如果我從中國(guó)向國(guó)外發(fā)送信息,廣播整個(gè)互聯(lián)網(wǎng)的所有主機(jī),那就炸了!
    • 所以我們需要對(duì)世界網(wǎng)絡(luò)進(jìn)行分區(qū),讓大區(qū)域包含小區(qū)域,就像國(guó)家-省-市區(qū)... , 很遺憾的是 MAC地址 是跟計(jì)算機(jī)相關(guān)而不是和位置相關(guān)的。所以我們有了 IP協(xié)議
    • IP協(xié)議 所附帶的產(chǎn)品 IP地址 的作用就在幫助計(jì)算機(jī)識(shí)別自己是否在同一個(gè)網(wǎng)絡(luò)中( 這里省略了子網(wǎng)掩碼的作用 )。
  • 實(shí)際上,在進(jìn)行網(wǎng)絡(luò)編程的時(shí)候,以上細(xì)節(jié)幾乎都被隱藏起來(lái),留給我們的只是可供使用的接口。

也許,許多大學(xué)計(jì)算機(jī)基礎(chǔ)課程,會(huì)講到 IP地址 有種類,分為 A,B,C...類,老師還介紹了各種類型的地址范圍。

但是在現(xiàn)代,這種分類早已經(jīng)失效,或者說(shuō)正在逐漸消失,因?yàn)楫?dāng)下的 IP 地址的 子網(wǎng)掩碼 可以是任意位,并以反斜杠跟在 IP地址后方。

比較現(xiàn)代的 IP地址 表示形式一般如此 1.185.223.1/24 代表著子網(wǎng)掩碼是由 24個(gè) 從左至右連續(xù)的的二進(jìn)制1 組合而成,其余位為0。稱為CIDR分類

夾在中間

事實(shí)上有一些實(shí)用且挺炫酷的函數(shù),可以先提一下

  • 域名 和 IP地址 的互查
    • gethostbyname 用于域名查找 IP信息及各類信息
      • struct hostent * gethostbyname(const char * hostname)
      • struct hostent 是存儲(chǔ)查找到的各類型信息,后方會(huì)有介紹
      • hostname 即要查詢的域名
    • gethostbyaddr 用于IP地址查找 域名及各類信息
      • struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family)
        • addr 是要查詢的 IP地址,之所以是 const char * 是因?yàn)镃語(yǔ)言歷史遺留的原因,實(shí)際上其類型應(yīng)為 struct in_addr *(IPv4)
        • len 地址的長(zhǎng)度,即 IPv4 為4, IPv6 為16
        • family 即協(xié)議的種類, IPv4AF_INET, IPv6AF_INET6
struct hostent 的成員 . 類型 . 解釋
h_name char * 官方名稱
h_aliases char ** 域名集合,以NULL結(jié)尾
h_addrtype int 地址族的類型 AF_INET 或 AF_INET6
h_length int 地址的長(zhǎng)度 4 或 16
h_addr_list char ** IP的集合,以NULL結(jié)尾, 實(shí)際上每個(gè)元素的類型為 struct in_addr*
  • 其中第二和最后一個(gè)是關(guān)注的重點(diǎn)所在,可以在調(diào)用函數(shù)之后,輸出信息

    實(shí)際上,這并不是一個(gè)好的方法,在后方將記錄 現(xiàn)代人的我們 該如何做到這些事情,以上只是以前的TCP/IP 編程

只適用于 IPv4

套接字網(wǎng)絡(luò)編程初始

選擇使用 C 語(yǔ)言進(jìn)行編程

  • 在網(wǎng)絡(luò)編程中,最常實(shí)用的兩種連接方式 TCPUDP
  • 最常編程的平臺(tái) POSIX 標(biāo)準(zhǔn)->*nix平臺(tái)標(biāo)準(zhǔn)Windows 平臺(tái)標(biāo)準(zhǔn)
    • 實(shí)際上,后者也是參考前者進(jìn)行一些細(xì)微的改變(指的是接口)

對(duì)比兩種不同連接方式的不同地位的創(chuàng)建,使用

TCP服務(wù)器 TCP客戶端 UDP服務(wù)器 UDP客戶端 注釋
socket() socket() socket() socket() 創(chuàng)建套接字
bind() bind() bind() 綁定所分配IP地址和端口號(hào)
listen() connect() 客戶端則綁定IP地址和端口號(hào),并等待連接;服務(wù)器則是等待連接
accept() 服務(wù)器接受連接
... ... sendto/recvfrom() sendto/recvfrom() 對(duì)于UDP即是連接也是操作
close() close() close() close 雙向直接關(guān)閉連接
shutdown() shutdown() shutdown() shutdown() 可選擇方向的關(guān)閉連接,即更加靈活

如此對(duì)比雖然有一些小瑕疵,但是能夠大體上反映出真?zhèn)€網(wǎng)絡(luò)編程上不同方式的區(qū)別

注1: 對(duì)于 sendto recvfrom 這兩個(gè)接口函數(shù),并不一定是只能用在 UDP類型的 套接字上,同樣 TCP類型的 套接字也能使用,但是這么做并沒(méi)有什么意義。

注2: 實(shí)際上 UDP 沒(méi)有所謂的 服務(wù)器和和護(hù)短,因?yàn)楸緛?lái)就是單純的互相發(fā)來(lái)發(fā)去。客戶端端口 一般是隨機(jī)的

以上是 *nix平臺(tái)下的標(biāo)準(zhǔn), Windows下的操作方式和 API有細(xì)微不同,但大部分是一致的。

Windows *nix
socket() socket()
bind() bind()
connect() connect()
listen() listen()
accept() accept()
closesocket() close()
send() send()
read() read()
sendto() sendto()
recvfrom() recvfrom()

不僅僅是接口名字相同,參數(shù)個(gè)數(shù)以及功能也是一致,即使有一個(gè)例外,其參數(shù)以及使用方法也相同。

那豈不是可以直接移植了?

并不!

Windows 套接字編程時(shí) , 由于 Windows 將其實(shí)現(xiàn)為動(dòng)態(tài)庫(kù),所以在使用時(shí)需要將其加載進(jìn)程序。

故而多加了加載操作。

int WSAStartup(
  WORD      wVersionRequested,
  LPWSADATA lpWSAData  /* 這是一個(gè)結(jié)構(gòu)體, 傳入類型為WSADATA*  */
);
int WSACleanup(void);

每當(dāng)在 Windows 上進(jìn)行套接字編程時(shí),總要指定某個(gè)版本的套接字庫(kù):

WSADATA wsaData;
int err_code;
/*
* MAKEWORD()的作用在于將版本號(hào)轉(zhuǎn)為指定格式傳入
* 當(dāng)下(2015-10)套接字庫(kù)的版本號(hào)最高是 2.2
*/
err_code = WSAStartup(MAKEWORD(2, 2), &wsaData);
/* TODO Something */
WSACleanup();

這是最基本的在 Windows 上使用 套接字 編程的流程,但是如果本平臺(tái)的套接字庫(kù)最高版本并不符合當(dāng)前要求呢?

那么首先會(huì)將套接字版本庫(kù)盡可能設(shè)置到平臺(tái)的 最高版本 ,可以通過(guò)結(jié)構(gòu)體 WSADATA 進(jìn)行查詢

if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
  printf("Could not find a usable version of Winsock.dll\n");
  WSACleanup();
  return 1;
}

總體而言, Windows平臺(tái)*uix平臺(tái) 的區(qū)別在于,前者使用時(shí)需要 加載和清除 套接字庫(kù) 其余邏輯流程一致,畢竟只有統(tǒng)一才能越利于編程世界的發(fā)展。

上一篇:0x03-C代碼下一篇:0x12-套接字編程-2