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

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

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

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

寫在最前方

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

開始首先是萬物根源的協(xié)議信息

概念

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

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

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

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

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

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

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

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

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

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

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

夾在中間

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

  • 域名 和 IP地址 的互查
    • gethostbyname 用于域名查找 IP信息及各類信息
      • struct hostent * gethostbyname(const char * hostname)
      • struct hostent 是存儲查找到的各類型信息,后方會有介紹
      • hostname 即要查詢的域名
    • gethostbyaddr 用于IP地址查找 域名及各類信息
      • struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family)
        • addr 是要查詢的 IP地址,之所以是 const char * 是因為C語言歷史遺留的原因,實際上其類型應(yīng)為 struct in_addr *(IPv4)
        • len 地址的長度,即 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 地址的長度 4 或 16
h_addr_list char ** IP的集合,以NULL結(jié)尾, 實際上每個元素的類型為 struct in_addr*
  • 其中第二和最后一個是關(guān)注的重點所在,可以在調(diào)用函數(shù)之后,輸出信息

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

只適用于 IPv4

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

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

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

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

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

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

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

注2: 實際上 UDP 沒有所謂的 服務(wù)器和和護短,因為本來就是單純的互相發(fā)來發(fā)去。客戶端端口 一般是隨機的

以上是 *nix平臺下的標(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ù)個數(shù)以及功能也是一致,即使有一個例外,其參數(shù)以及使用方法也相同。

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

并不!

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

故而多加了加載操作。

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

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

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

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

那么首先會將套接字版本庫盡可能設(shè)置到平臺的 最高版本 ,可以通過結(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平臺*uix平臺 的區(qū)別在于,前者使用時需要 加載和清除 套接字庫 其余邏輯流程一致,畢竟只有統(tǒng)一才能越利于編程世界的發(fā)展。

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