socket學習

socket學習網(wǎng)站:
http://c.biancheng.net/view/2124.html

一.什么是socket

socket 的原意是“插座”,在計算機通信領域抖誉,socket 被翻譯為“套接字”,它是計算機之間進行通信的一種約定或一種方式。通過 socket 這種約定檩坚,一臺計算機可以接收其他計算機的數(shù)據(jù),也可以向其他計算機發(fā)送數(shù)據(jù)诅福。

socket 的典型應用就是 Web 服務器和瀏覽器:瀏覽器獲取用戶輸入的 URL匾委,向服務器發(fā)起請求,服務器分析接收到的 URL氓润,將對應的網(wǎng)頁內(nèi)容返回給瀏覽器赂乐,瀏覽器再經(jīng)過解析和渲染,就將文字咖气、圖片挨措、視頻等元素呈現(xiàn)給用戶。

二.套接字的類型

1.流格式套接字(SOCK_STREAM)

流格式套接字(Stream Sockets)也叫“面向連接的套接字”崩溪,在代碼中使用 SOCK_STREAM 表示浅役。

SOCK_STREAM 是一種可靠的、雙向的通信數(shù)據(jù)流伶唯,數(shù)據(jù)可以準確無誤地到達另一臺計算機觉既,如果損壞或丟失,可以重新發(fā)送乳幸。

特征:
數(shù)據(jù)在傳輸過程中不會消失瞪讼;
數(shù)據(jù)是按照順序傳輸?shù)模?/strong>
數(shù)據(jù)的發(fā)送和接收不是同步的(有的教程也稱“不存在數(shù)據(jù)邊界”)

使用了TCP協(xié)議,所以流格式套接字可以達到高質(zhì)量的數(shù)據(jù)傳輸

2.數(shù)據(jù)報格式套接字(SOCK_DGRAM)

數(shù)據(jù)報格式套接字(Datagram Sockets)也叫“無連接的套接字”粹断,在代碼中使用 SOCK_DGRAM 表示符欠。

數(shù)據(jù)報套接字是一種不可靠的、不按順序傳遞的瓶埋、以追求速度為目的的套接字希柿。

特征:
強調(diào)快速傳輸而非傳輸順序;
傳輸?shù)臄?shù)據(jù)可能丟失也可能損毀悬赏;
限制每次傳輸?shù)臄?shù)據(jù)大薪坪骸;
數(shù)據(jù)的發(fā)送和接收是同步的(有的教程也稱“存在數(shù)據(jù)邊界”)闽颇。

使用了UDP協(xié)議,所以傳輸速率很快

三.OSI 網(wǎng)絡7層模型

1.OSI模型 只是存在于概念和理論上的一種模型寄锐,它的缺點是分層太多兵多,增加了網(wǎng)絡工作的復雜性尖啡,所以沒有大規(guī)模應用

2.TCP/IP 模型 對OSI模型 進行了簡化,合并了一些層剩膘,最終只保留了 4 層衅斩,從下到上分別是接口層、網(wǎng)絡層怠褐、傳輸層和應用層

3.“TCP/IP”畏梆,TCP 用來確保數(shù)據(jù)的正確性,IP(Internet Protocol奈懒,網(wǎng)絡協(xié)議)用來控制數(shù)據(jù)如何從源頭到達目的地奠涌,也就是常說的“路由”

4.這個網(wǎng)絡模型是用來進行數(shù)據(jù)封裝的。

我們平常使用的程序(或者說軟件)一般都是通過應用層來訪問網(wǎng)絡的磷杏,程序產(chǎn)生的數(shù)據(jù)會一層一層地往下傳輸溜畅,直到最后的網(wǎng)絡接口層,就通過網(wǎng)線發(fā)送到互聯(lián)網(wǎng)上去了极祸。數(shù)據(jù)每往下走一層慈格,就會被這一層的協(xié)議增加一層包裝,等到發(fā)送到互聯(lián)網(wǎng)上時遥金,已經(jīng)比原始數(shù)據(jù)多了四層包裝浴捆。整個數(shù)據(jù)封裝的過程就像俄羅斯套娃。

當另一臺計算機接收到數(shù)據(jù)包時稿械,會從網(wǎng)絡接口層再一層一層往上傳輸汤功,每傳輸一層就拆開一層包裝,直到最后的應用層溜哮,就得到了最原始的數(shù)據(jù)滔金,這才是程序要使用的數(shù)據(jù)。

給數(shù)據(jù)加包裝的過程茂嗓,實際上就是在數(shù)據(jù)的頭部增加一個標志(一個數(shù)據(jù)塊)餐茵,表示數(shù)據(jù)經(jīng)過了這一層,我已經(jīng)處理過了述吸。給數(shù)據(jù)拆包裝的過程正好相反忿族,就是去掉數(shù)據(jù)頭部的標志,讓它逐漸現(xiàn)出原形蝌矛。

5.我們所說的 socket 編程道批,是站在傳輸層的基礎上,所以可以使用 TCP/UDP 協(xié)議入撒,但是不能干「訪問網(wǎng)頁」這樣的事情隆豹,因為訪問網(wǎng)頁所需要的 http 協(xié)議位于應用層。

四.TCP/IP協(xié)議族

1.協(xié)議(Protocol)就是網(wǎng)絡通信過程中的約定或者合同茅逮,通信的雙方必須都遵守才能正常收發(fā)數(shù)據(jù)璃赡。協(xié)議有很多種判哥,例如 TCP、UDP碉考、IP 等塌计,通信的雙方必須使用同一協(xié)議才能通信。協(xié)議是一種規(guī)范侯谁,由計算機組織制定锌仅,規(guī)定了很多細節(jié),例如墙贱,如何建立連接热芹,如何相互識別等。

2.TCP/IP 模型包含了 TCP嫩痰、IP剿吻、UDP、Telnet串纺、FTP丽旅、SMTP 等上百個互為關聯(lián)的協(xié)議,其中 TCP 和 IP 是最常用的兩種底層協(xié)議纺棺,所以把它們統(tǒng)稱為“TCP/IP 協(xié)議族”榄笙。也就是說,“TCP/IP模型”中所涉及到的協(xié)議稱為“TCP/IP協(xié)議族”祷蝌。

TCP 和 UDP 協(xié)議的層級關系

五.IP茅撞,MAC,端口號

1.IP地址

  • IP地址是 Internet Protocol Address 的縮寫巨朦,譯為“網(wǎng)際協(xié)議地址”米丘。
    目前大部分軟件使用 IPv4 地址,但 IPv6 也正在被人們接受

  • 1.2一臺計算機可以擁有一個獨立的 IP 地址糊啡,一個局域網(wǎng)也可以擁有一個獨立的 IP 地址拄查,所以一個IP地址不一定能標識一臺計算機

  • 1.3在因特網(wǎng)上進行通信時,必須要知道對方的 IP 地址棚蓄。實際上數(shù)據(jù)包中已經(jīng)附帶了 IP 地址堕扶,把數(shù)據(jù)包發(fā)送給路由器以后,路由器會根據(jù) IP 地址找到對方的地里位置梭依,完成一次數(shù)據(jù)的傳遞稍算。路由器有非常高效和智能的算法,很快就會找到目標計算機役拴。

2.MAC地址

  • 一個局域網(wǎng)往往才能擁有一個獨立的 IP糊探,IP 地址只能定位到一個局域網(wǎng),無法定位到具體的一臺計算機

  • MAC 地址是 Media Access Control Address 的縮寫,直譯為“媒體訪問控制地址”侧到,也稱為局域網(wǎng)地址(LAN Address)勃教,以太網(wǎng)地址(Ethernet Address)或物理地址(Physical Address)淤击。

  • 真正能唯一標識一臺計算機的是 MAC 地址匠抗,每個網(wǎng)卡的 MAC 地址在全世界都是獨一無二的。計算機出廠時污抬,MAC 地址已經(jīng)被寫死到網(wǎng)卡里面了汞贸。局域網(wǎng)中的路由器/交換機會記錄每臺計算機的 MAC 地址

  • 數(shù)據(jù)包中除了會附帶對方的 IP 地址,還會附帶對方的 MAC 地址印机,當數(shù)據(jù)包達到局域網(wǎng)以后矢腻,路由器/交換機會根據(jù)數(shù)據(jù)包中的 MAC 地址找到對應的計算機,然后把數(shù)據(jù)包轉(zhuǎn)交給它射赛,這樣就完成了數(shù)據(jù)的傳遞多柑。

3.端口號

  • 有了 IP 地址和 MAC 地址,雖然可以找到目標計算機漱挚,但是一臺計算機可以同時提供多種網(wǎng)絡服務兜叨,例如 Web 服務(網(wǎng)站)秆剪、FTP 服務(文件傳輸服務)、SMTP 服務(郵箱服務)等

  • 僅有 IP 地址和 MAC 地址初嘹,計算機雖然可以正確接收到數(shù)據(jù)包,但是卻不知道要將數(shù)據(jù)包交給哪個網(wǎng)絡程序來處理沮趣,所以通信失敗

  • 為了區(qū)分不同的網(wǎng)絡程序屯烦,計算機會為每個網(wǎng)絡程序分配一個獨一無二的端口號(Port Number),例如房铭,Web 服務的端口號是 80驻龟,F(xiàn)TP 服務的端口號是 21,SMTP 服務的端口號是 25缸匪。

  • 端口(Port)是一個虛擬的翁狐、邏輯上的概念『浪裕可以將端口理解為一道門谴蔑,數(shù)據(jù)通過這道門流入流出,每道門有不同的編號龟梦,就是端口號隐锭。

六.socket() 函數(shù)創(chuàng)建套接字

int socket(int af, int type, int protocol);

1.af參數(shù)-IP 地址類型

  • af 為地址族(Address Family),也就是 IP 地址類型计贰,常用的有 AF_INET 和 AF_INET6钦睡。

  • AF_INET 表示 IPv4 地址,例如 127.0.0.1(本機地址)躁倒;AF_INET6 表示 IPv6 地址荞怒,例如 1030::C9B4:FF12:48AA:1A2B洒琢。

  • AF 是“Address Family”的簡寫,INET是“Inetnet”的簡寫褐桌。

  • 你也可以使用 PF 前綴衰抑,PF 是“Protocol Family”的簡寫,它和 AF 是一樣的荧嵌。

2.type參數(shù)- 數(shù)據(jù)傳輸方式/套接字類型

  • SOCK_STREAM(流格式套接字/面向連接的套接字)
  • SOCK_DGRAM(數(shù)據(jù)報套接字/無連接的套接字)

3.protocol參數(shù)-傳輸協(xié)議

  • IPPROTO_TCP 表示 TCP 傳輸協(xié)議
  • IPPTOTO_UDP 表示 UDP 傳輸協(xié)議

4.第三個參數(shù)的意義

  • 一般情況下有了 af 和 type 兩個參數(shù)就可以創(chuàng)建套接字了呛踊,操作系統(tǒng)會自動推演出協(xié)議類型,除非遇到這樣的情況:有兩種不同的協(xié)議支持同一種地址類型和數(shù)據(jù)傳輸類型啦撮。如果我們不指明使用哪種協(xié)議谭网,操作系統(tǒng)是沒辦法自動推演的。

  • 參數(shù) af 的值為 PF_INET赃春。如果使用 SOCK_STREAM 傳輸數(shù)據(jù)愉择,那么滿足這兩個條件的協(xié)議只有 TCP,這種套接字稱為 TCP 套接字

int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
  • 參數(shù) af 的值為 PF_INET织中。如果使用 SOCK_DGRAM 傳輸方式锥涕,那么滿足這兩個條件的協(xié)議只有 UDP,這種套接字稱為 UDP 套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  
  • 簡化寫法:上面兩種情況都只有一種協(xié)議滿足條件抠璃,可以將 protocol 的值設為 0站楚,系統(tǒng)會自動推演出應該使用什么協(xié)議
//創(chuàng)建TCP套接字
nt tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  

//創(chuàng)建UDP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  

七.bind() 函數(shù)-將套接字與特定的 IP 地址和端口綁定

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);  
  • 參數(shù)解釋:sock 為 socket 文件描述符也就是我們的套接字,addr 為 sockaddr 結(jié)構(gòu)體變量的指針搏嗡,addrlen 為 addr 變量的大小窿春,可由 sizeof() 計算得出。
//創(chuàng)建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

//創(chuàng)建sockaddr_in結(jié)構(gòu)體變量
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));  //每個字節(jié)都用0填充
serv_addr.sin_family = AF_INET;  //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//綁定IP地址為任意
serv_addr.sin_port = htons(1234);  //端口

//將套接字和IP采盒、端口綁定
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  • 例子:創(chuàng)建的套接字與IP地址 127.0.0.1旧乞、端口 1234 綁定

1.為什么bind() 第二個參數(shù)的類型為 sockaddr,而代碼中卻使用 sockaddr_in磅氨,然后再強制轉(zhuǎn)換為 sockaddr

//sockaddr 結(jié)構(gòu)體
struct sockaddr{
    sa_family_t  sin_family;   //地址族(Address Family)尺栖,也就是地址類型
    char         sa_data[14];  //IP地址和端口號
};

//sockaddr_in結(jié)構(gòu)體
struct sockaddr_in{
    sa_family_t     sin_family;   //地址族(Address Family),也就是地址類型
    uint16_t        sin_port;     //16位的端口號
    struct in_addr  sin_addr;     //32位IP地址
    char            sin_zero[8];  //不使用烦租,一般用0填充
};

//sockaddr_in6結(jié)構(gòu)體
struct sockaddr_in6 { 
    sa_family_t sin6_family;  //(2)地址類型延赌,取值為AF_INET6
    in_port_t sin6_port;  //(2)16位端口號
    uint32_t sin6_flowinfo;  //(4)IPv6流信息
    struct in6_addr sin6_addr;  //(4)具體的IPv6地址
    uint32_t sin6_scope_id;  //(4)接口范圍ID
};

sockaddr 和 sockaddr_in 的長度相同,都是16字節(jié)叉橱,只是將IP地址和端口號合并到一起挫以,用一個成員 sa_data 表示

想給 sa_data 賦值,必須同時指明IP地址和端口號窃祝,例如”127.0.0.1:80“掐松,遺憾的是,沒有相關函數(shù)將這個字符串轉(zhuǎn)換成需要的形式,也就很難給 sockaddr 類型的變量賦值大磺,所以使用 sockaddr_in 來代替

這兩個結(jié)構(gòu)體的長度相同抡句,強制轉(zhuǎn)換類型時不會丟失字節(jié),也沒有多余的字節(jié)杠愧。

可以認為待榔,sockaddr 是一種通用的結(jié)構(gòu)體,可以用來保存多種類型的IP地址和端口號殴蹄,而 sockaddr_in 是專門用來保存 IPv4 地址的結(jié)構(gòu)體究抓。另外還有 sockaddr_in6猾担,用來保存 IPv6 地址

2.sockaddr_in結(jié)構(gòu)體參數(shù)解釋

struct sockaddr_in{
    sa_family_t     sin_family;   //地址族(Address Family)袭灯,也就是地址類型
    uint16_t        sin_port;     //16位的端口號
    struct in_addr  sin_addr;     //32位IP地址
    char            sin_zero[8];  //不使用,一般用0填充
};

sin_family: 和 socket() 的第一個參數(shù)的含義相同绑嘹,取值也要保持一致稽荧。

sin_prot :端口號。uint16_t 的長度為兩個字節(jié)工腋,理論上端口號的取值范圍為 0~65536姨丈,但 0~1023 的端口一般由系統(tǒng)分配給特定的服務程序,例如 Web 服務的端口號為 80擅腰,F(xiàn)TP 服務的端口號為 21蟋恬,所以我們的程序要盡量在 1024~65536 之間分配端口號。端口號需要用 htons() 函數(shù)轉(zhuǎn)換趁冈。

sin_addr : struct in_addr 結(jié)構(gòu)體類型的變量

sin_zero[8] :是多余的8個字節(jié)歼争,沒有用,一般使用 memset() 函數(shù)填充為 0渗勘。上面的代碼中沐绒,先用 memset() 將結(jié)構(gòu)體的全部字節(jié)填充為 0,再給前3個成員賦值旺坠,剩下的 sin_zero 自然就是 0 了乔遮。

3.in_addr結(jié)構(gòu)體的參數(shù)解釋

struct in_addr{
    in_addr_t  s_addr;  //32位的IP地址
};

in_addr_t 在頭文件 <netinet/in.h> 中定義,等價于 unsigned long取刃,長度為4個字節(jié)蹋肮。也就是說,s_addr 是一個整數(shù)璧疗,而IP地址是一個字符串坯辩,所以需要 inet_addr() 函數(shù)進行轉(zhuǎn)換

八.connect() 函數(shù)-客戶端程序發(fā)起連接

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);

各個參數(shù)和bind()函數(shù)相同

九.listen(),accept()函數(shù)-服務端程序被動監(jiān)聽病毡,響應服務端請求

對于服務器端程序濒翻,使用 bind() 綁定套接字后,還需要使用 listen() 函數(shù)讓套接字進入被動監(jiān)聽狀態(tài),再調(diào)用 accept() 函數(shù)有送,就可以隨時響應客戶端的請求了淌喻。

1.listen()函數(shù)-讓套接字進入被動監(jiān)聽狀態(tài)

int listen(int sock, int backlog);
  • sock :需要進入監(jiān)聽狀態(tài)的套接字,backlog :請求隊列的最大長度雀摘。

  • 被動監(jiān)聽:是指當沒有客戶端請求時裸删,套接字處于“睡眠”狀態(tài),只有當接收到客戶端請求時阵赠,套接字才會被“喚醒”來響應請求涯塔。

  • 請求隊列:當套接字正在處理客戶端請求時,如果有新的請求進來清蚀,套接字是沒法處理的匕荸,只能把它放進緩沖區(qū),待當前請求處理完畢后枷邪,再從緩沖區(qū)中讀取出來處理榛搔。如果不斷有新的請求進來,它們就按照先后順序在緩沖區(qū)中排隊东揣,直到緩沖區(qū)滿践惑。這個緩沖區(qū),就稱為請求隊列

  • 緩沖區(qū)的長度(能存放多少個客戶端請求):可以通過 listen() 函數(shù)的 backlog 參數(shù)指定嘶卧,但究竟為多少并沒有什么標準尔觉,可以根據(jù)你的需求來定,并發(fā)量小的話可以是10或者20芥吟。如果將 backlog 的值設置為 SOMAXCONN侦铜,就由系統(tǒng)來決定請求隊列長度,這個值一般比較大运沦,可能是幾百泵额,或者更多。

  • 請求隊列滿時:不再接收新的請求携添,對于 Linux嫁盲,客戶端會收到 ECONNREFUSED 錯誤,對于 Windows烈掠,客戶端會收到 WSAECONNREFUSED 錯誤羞秤。

  • listen() 只是讓套接字處于監(jiān)聽狀態(tài),并沒有接收請求左敌。接收請求需要使用 accept() 函數(shù)瘾蛋。

2.accept() 函數(shù)-通過 accept() 函數(shù)來接收客戶端請求

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
  • 參數(shù)與 bind() 和 connect() 是相同的,sock 為服務器端套接字矫限,addr 為 sockaddr_in 結(jié)構(gòu)體變量哺哼,addrlen 為參數(shù) addr 的長度佩抹,可由 sizeof() 求得。

  • accept() 返回一個新的套接字來和客戶端通信取董,addr 保存了客戶端的IP地址和端口號棍苹,而 sock 是服務器端的套接字。后面和客戶端通信時茵汰,要使用這個新生成的套接字枢里,而不是原來服務器端的套接字。

  • listen() 只是讓套接字進入監(jiān)聽狀態(tài)蹂午,并沒有真正接收客戶端請求栏豺,listen() 后面的代碼會繼續(xù)執(zhí)行,直到遇到 accept()豆胸。accept() 會阻塞程序執(zhí)行(后面代碼不能被執(zhí)行)奥洼,直到有新的請求到來

十.write() , read()函數(shù)-發(fā)送和接受數(shù)據(jù)

兩臺計算機之間的通信相當于兩個套接字之間的通信配乱,在服務器端用 write() 向套接字寫入數(shù)據(jù)溉卓,客戶端就能收到,然后再使用 read() 從套接字中讀取出來搬泥,就完成了一次通信。

ssize_t:size_t 是通過 typedef 聲明的 unsigned int 類型伏尼;ssize_t 在 "size_t" 前面加了一個"s"忿檩,代表 signed,即 ssize_t 是通過 typedef 聲明的 signed int 類型爆阶。

1.write()函數(shù)(send)

ssize_t write(int fd, const void *buf, size_t nbytes);
  • fd 為要寫入的文件的描述符即連接的socket燥透,buf 為要寫入的數(shù)據(jù)的緩沖區(qū)地址,nbytes 為要寫入的數(shù)據(jù)的字節(jié)數(shù)辨图。

  • write() 函數(shù)會將緩沖區(qū) buf 中的 nbytes 個字節(jié)寫入文件 fd班套,成功則返回寫入的字節(jié)數(shù),失敗則返回 -1故河。

2.read()函數(shù)(recv)

ssize_t read(int fd, void *buf, size_t nbytes);
  • fd 為要讀取的文件的描述符即連接的socket吱韭,buf 為要接收數(shù)據(jù)的緩沖區(qū)地址,nbytes 為要讀取的數(shù)據(jù)的字節(jié)數(shù)鱼的。

  • read() 函數(shù)會從 fd 文件中讀取 nbytes 個字節(jié)并保存到緩沖區(qū) buf理盆,成功則返回讀取到的字節(jié)數(shù)(但遇到文件結(jié)尾則返回0),失敗則返回 -1凑阶。

十一.TCP三次握手建立連接

TCP建立連接時要傳輸三個數(shù)據(jù)包猿规,俗稱三次握手(Three-way Handshaking)≈娉鳎可以形象的比喻為下面的對話:
[Shake 1] 套接字A:“你好姨俩,套接字B蘸拔,我這里有數(shù)據(jù)要傳送給你,建立連接吧环葵《嘉保”
[Shake 2] 套接字B:“好的,我這邊已準備就緒积担≡删В”
[Shake 3] 套接字A:“謝謝你受理我的請求〉坭担”

十二.TCP四次握手斷開連接

建立連接需要三次握手先誉,斷開連接需要四次握手,可以形象的比喻為下面的對話:
[Shake 1] 套接字A:“任務處理完畢的烁,我希望斷開連接褐耳。”
[Shake 2] 套接字B:“哦渴庆,是嗎铃芦?請稍等,我準備一下襟雷∪凶遥”
等待片刻后……
[Shake 3] 套接字B:“我準備好了,可以斷開連接了耸弄∵只ⅲ”
[Shake 4] 套接字A:“好的,謝謝合作计呈∨樗校”

十三.類型轉(zhuǎn)換

1.htonl()

簡述:
  將主機的無符號長整形數(shù)轉(zhuǎn)換成網(wǎng)絡字節(jié)順序。
  #include <winsock.h>
  u_long PASCAL FAR htonl( u_long hostlong);
  hostlong:主機字節(jié)順序表達的32位數(shù)捌显。
  注釋:
  本函數(shù)將一個32位數(shù)從主機字節(jié)順序轉(zhuǎn)換成網(wǎng)絡字節(jié)順序茁彭。
  返回值:
  htonl()返回一個網(wǎng)絡字節(jié)順序的值。
  參見:
  htons(), ntohl(), ntohs().

2.htons()

簡述:
  將主機的無符號短整形數(shù)轉(zhuǎn)換成網(wǎng)絡字節(jié)順序扶歪。
  #include <winsock.h>
  u_short PASCAL FAR htons( u_short hostshort);
  hostshort:主機字節(jié)順序表達的16位數(shù)理肺。
  注釋:
  本函數(shù)將一個16位數(shù)從主機字節(jié)順序轉(zhuǎn)換成網(wǎng)絡字節(jié)順序。
  返回值:
  htons()返回一個網(wǎng)絡字節(jié)順序的值击罪。
  參見:
  htonl(), ntohl(), ntohs().
  ---------------------------------------------
  簡單地說,htons()就是將一個數(shù)的高低位互換(如:12 34 --> 34 12)
VB表示:
  MsgBox Hex(htons(&H1234))
  顯示值為 3412

3.inet_addr()

簡述:將一個點間隔地址轉(zhuǎn)換成一個in_addr哲嘲。
  #include <winsock.h>
  unsigned long PASCAL FAR inet_addr( const struct FAR* cp);
  cp:一個以Internet標準“.”間隔的字符串。
  注釋:
  本函數(shù)解釋cp參數(shù)中的字符串媳禁,這個字符串用Internet的“.”間隔格式表示一個數(shù)字的Internet地址眠副。返回值可用作Internet地址。所有Internet地址以網(wǎng)絡字節(jié)順序返回(字節(jié)從左到右排列)竣稽。
  Internet地址用“.”間隔的地址可有下列幾種表達方式:
  a.b.c.d囱怕,a.b.c霍弹,a.b,a
  當四個部分都有定值時娃弓,每個都解釋成一個字節(jié)數(shù)據(jù)典格,從左到右組成Internet四字節(jié)地址。請注意台丛,當一個Internet地址在Intel機器上表示成一個32位整型數(shù)時耍缴,則上述的字節(jié)為“d.c.b.a”。這是因為Intel處理器的字節(jié)是從右向左排列的挽霉。
  請注意:只有Berkeley支持下述表達法防嗡,Internet其余各處均不支持∠揽玻考慮到與軟件的兼容性蚁趁,應按規(guī)定進行使用。
  對一個三部分地址实胸,最后一部分解釋成16位數(shù)據(jù)并作為網(wǎng)絡地址的最右兩個字節(jié)他嫡。這樣,三部分地址便很容易表示B組網(wǎng)絡地址庐完,如“128.net.host”.
  對一個兩部分地址钢属,最后一部分解釋成24位數(shù)據(jù)并作為網(wǎng)絡地址的最右三個字節(jié),這樣假褪,兩部分地址便很容易表示C組網(wǎng)絡地址署咽,如“net.host”。
  對僅有一個部分的地址生音,則將它的值直接存入網(wǎng)絡地址不作任何字節(jié)的重組。
  返回值:
  若無錯誤發(fā)生窒升,inet_addr()返回一個無符號長整型數(shù)缀遍,其中以適當字節(jié)順序存放Internet地址。如果傳入的字符串不是一個合法的Internet地址饱须,如“a.b.c.d”地址中任一項超過255域醇,那么inet_addr()返回INADDR_NONE。
  參見:
  inet_ntoa().
inet_addr()函數(shù)的實現(xiàn)
輸入是點分的IP地址格式(如A.B.C.D)的字符串蓉媳,從該字符串中提取出每一部分譬挚,轉(zhuǎn)換為ULONG,假設得到4個ULONG型的A,B,C,D,
ulAddress(ULONG型)是轉(zhuǎn)換后的結(jié)果,
ulAddress = D<<24 + C<<16 + B<<8 + A(網(wǎng)絡字節(jié)序)酪呻,即inet_addr(const char *)的返回結(jié)果
另外减宣,我們也可以得到把該IP轉(zhuǎn)換為主機序的結(jié)果,轉(zhuǎn)換方法一樣
A<<24 + B<<16 + C<<8 + D

4.inet_ntoa()

簡述:
  將網(wǎng)絡地址轉(zhuǎn)換成“.”點隔的字符串格式玩荠。
  #include <winsock.h>
  char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
  in:一個表示Internet主機地址的結(jié)構(gòu)漆腌。
  注釋:
  本函數(shù)將一個用in參數(shù)所表示的Internet地址結(jié)構(gòu)轉(zhuǎn)換成以“.” 間隔的諸如“a.b.c.d”的字符串形式贼邓。請注意inet_ntoa()返回的字符串存放在WINDOWS套接口實現(xiàn)所分配的內(nèi)存中。應用程序不應假設該內(nèi)存是如何分配的闷尿。在同一個線程的下一個WINDOWS套接口調(diào)用前塑径,數(shù)據(jù)將保證是有效。
  返回值:
  若無錯誤發(fā)生填具,inet_ntoa()返回一個字符指針统舀。否則的話,返回NULL劳景。其中的數(shù)據(jù)應在下一個WINDOWS套接口調(diào)用前復制出來誉简。
  測試代碼如下
  include <stdio.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <string.h>
  int main(int aargc, char* argv[])
  {
  struct in_addr addr1,addr2;
  ulong l1,l2;
  l1= inet_addr("192.168.0.74");
  l2 = inet_addr("211.100.21.179");
  memcpy(&addr1, &l1, 4);
  memcpy(&addr2, &l2, 4);
  printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意這一句的運行結(jié)果
  printf("%s\n", inet_ntoa(addr1));
  printf("%s\n", inet_ntoa(addr2));
  return 0;
  }
  實際運行結(jié)果如下:
  192.168.0.74 : 192.168.0.74 //從這里可以看出,printf里的inet_ntoa只運行了一次。
  192.168.0.74
  211.100.21.179
  inet_ntoa返回一個char *,而這個char *的空間是在inet_ntoa里面靜態(tài)分配的枢泰,所以inet_ntoa后面的調(diào)用會覆蓋上一次的調(diào)用描融。第一句printf的結(jié)果只能說明在printf里面的可變參數(shù)的求值是從右到左的,僅此而已衡蚂。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窿克,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子毛甲,更是在濱河造成了極大的恐慌年叮,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玻募,死亡現(xiàn)場離奇詭異只损,居然都是意外死亡,警方通過查閱死者的電腦和手機七咧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門跃惫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人艾栋,你說我怎么就攤上這事爆存。” “怎么了蝗砾?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵先较,是天一觀的道長。 經(jīng)常有香客問我悼粮,道長闲勺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任扣猫,我火速辦了婚禮菜循,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苞笨。我一直安慰自己债朵,他們只是感情好子眶,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著序芦,像睡著了一般臭杰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谚中,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天渴杆,我揣著相機與錄音,去河邊找鬼宪塔。 笑死磁奖,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的某筐。 我是一名探鬼主播比搭,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼南誊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抄囚,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤霉赡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幔托,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體穴亏,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年重挑,在試婚紗的時候發(fā)現(xiàn)自己被綠了嗓化。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡谬哀,死狀恐怖蟆湖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玻粪,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布诬垂,位于F島的核電站劲室,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏结窘。R本人自食惡果不足惜很洋,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隧枫。 院中可真熱鬧喉磁,春花似錦谓苟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孕暇,卻和暖如春仑撞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妖滔。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工隧哮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人座舍。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓沮翔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親曲秉。 傳聞我的和親對象是個殘疾皇子采蚀,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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