一、源IP和目的IP
如果我們的臺(tái)式機(jī)或者筆記本沒(méi)有IP地址就無(wú)法上網(wǎng),而因?yàn)槊颗_(tái)主機(jī)都有IP地址,所以注定了數(shù)據(jù)從一臺(tái)主機(jī)傳輸?shù)搅硪慌_(tái)主機(jī)一定有源IP和目的IP。
所以在報(bào)頭中就會(huì)包含源IP和目的IP。
而我們把數(shù)據(jù)從一臺(tái)主機(jī)傳遞到另一臺(tái)主機(jī)并不是目的,真正通信的其實(shí)是應(yīng)用層上的軟件。
而我們知道應(yīng)用層可不止一個(gè)軟件。
那么我們現(xiàn)在就有了問(wèn)題:
既然有了公網(wǎng)IP標(biāo)識(shí)了一臺(tái)唯一的主機(jī),那么數(shù)據(jù)就可以由一臺(tái)主機(jī)傳遞到另一臺(tái)主機(jī)。但是有這么多的軟件(進(jìn)程),怎么保證軟件A發(fā)送的被軟件B接收呢?
換句話說(shuō):用什么來(lái)標(biāo)識(shí)主機(jī)上的進(jìn)程的唯一性呢?
二、端口號(hào)port
為了更好的表示一臺(tái)主機(jī)上服務(wù)進(jìn)程的唯一性,規(guī)定用端口號(hào)port標(biāo)識(shí)服務(wù)進(jìn)程、客戶(hù)端進(jìn)程的唯一性。
端口號(hào):
1?? 端口號(hào)是一個(gè)2字節(jié)16位的整數(shù)
2?? 端口號(hào)用來(lái)標(biāo)識(shí)一個(gè)進(jìn)程,告訴操作系統(tǒng)要把數(shù)據(jù)交給哪一個(gè)進(jìn)程
3?? 一個(gè)端口號(hào)只能被一個(gè)進(jìn)程占用(同一個(gè)主機(jī))
由上面可以知道:
IP地址(標(biāo)識(shí)唯一主機(jī))+ 端口號(hào)(標(biāo)識(shí)唯一進(jìn)程)能夠標(biāo)識(shí)網(wǎng)絡(luò)上的某一臺(tái)主機(jī)的某一個(gè)進(jìn)程(全網(wǎng)唯一進(jìn)程)。
那么網(wǎng)絡(luò)通信的本質(zhì)就是進(jìn)程間通信。
而我們之前說(shuō)過(guò)進(jìn)程間通信的本質(zhì)是看到同一份資源,現(xiàn)在這個(gè)資源就是網(wǎng)絡(luò)。
而通信的本質(zhì)就是IO,因?yàn)槲覀兩暇W(wǎng)的行為就兩種情況:1.把數(shù)據(jù)發(fā)送出去 2.接收到數(shù)據(jù)。
這里再思考一個(gè)問(wèn)題,我們標(biāo)識(shí)一個(gè)進(jìn)程有pid,那么為什么還需要端口號(hào)呢?
1?? 首先pid是系統(tǒng)規(guī)定的,而port是網(wǎng)絡(luò)規(guī)定的,這樣就可以把系統(tǒng)和網(wǎng)絡(luò)解耦。
2?? port標(biāo)識(shí)服務(wù)器的唯一性不能做任何改變,要讓客戶(hù)端能找到服務(wù)器,就像110,120樣不能被改變。而pid每次啟動(dòng)進(jìn)程pid就會(huì)改變。
3?? 不是所有的進(jìn)程都需要提供網(wǎng)絡(luò)服務(wù)或請(qǐng)求(不需要port),但每個(gè)進(jìn)程都需要pid。
雖然一個(gè)端口號(hào)只能綁定一個(gè)進(jìn)程,但是一個(gè)進(jìn)程可以綁定多個(gè)端口號(hào)。
上面我們說(shuō)了IP有源IP和目的IP,而這里的port也有源端口號(hào)和目的端口號(hào)。我們?cè)诎l(fā)送數(shù)據(jù)的時(shí)候也要把自己的IP和端口號(hào)發(fā)送過(guò)去,因?yàn)閿?shù)據(jù)還要被發(fā)送回來(lái)。所以發(fā)送數(shù)據(jù)的時(shí)候一定會(huì)多出一部分?jǐn)?shù)據(jù)(以協(xié)議的形式呈現(xiàn))。
有人可能會(huì)問(wèn)那么第一次怎么知道給哪個(gè)IP和port發(fā)送,這個(gè)不用擔(dān)心,因?yàn)榉?wù)器內(nèi)部已經(jīng)內(nèi)置好了。
三、TCP/UDP協(xié)議
我們用的套接字接口一定會(huì)使用傳輸層協(xié)議,我們不會(huì)繞過(guò)傳輸層去調(diào)用下面的協(xié)議。
而傳輸層的協(xié)議分為T(mén)CP協(xié)議和UDP協(xié)議。
對(duì)于TCP協(xié)議有幾個(gè)特點(diǎn):
1?? 傳輸層協(xié)議
2?? 有連接(正式通信前要先建立連接)
3?? 可靠傳輸(在內(nèi)部幫我們做可靠傳輸工作)
4?? 面向字節(jié)流
對(duì)于UDP協(xié)議有幾個(gè)特點(diǎn):
1?? 傳輸層協(xié)議
2?? 無(wú)連接
3?? 不可靠傳輸
4?? 面向數(shù)據(jù)報(bào)
可靠和不可靠傳輸:
不可靠傳輸:比如說(shuō)發(fā)送數(shù)據(jù)時(shí)出現(xiàn)了丟包的情況、或者數(shù)據(jù)被重復(fù)傳遞了(傳遞了多份)、或者網(wǎng)絡(luò)出現(xiàn)了問(wèn)題等等造成的后果就叫做不可靠。
所以傳輸層就是用來(lái)解決可靠性的一個(gè)協(xié)議。
但是為什么UDP不可靠傳輸我們還要有這個(gè)協(xié)調(diào)呢?
這里要注意可不可靠是一個(gè)中性詞。可靠是需要成本的,往往在維護(hù)和編碼上都比較復(fù)雜;
而不可靠沒(méi)有成本,使用起來(lái)也簡(jiǎn)單。
所以要分場(chǎng)景使用。
3.1 網(wǎng)絡(luò)字節(jié)流
我們知道多字節(jié)數(shù)據(jù)存儲(chǔ)的時(shí)候會(huì)有大小端問(wèn)題。
小端:低權(quán)值的數(shù)放入低地址。
大端:低權(quán)值的數(shù)放入高地址。
所以現(xiàn)在就出現(xiàn)了這么一種情況:可能一個(gè)大端機(jī)用大端的方式發(fā)送數(shù)據(jù)到一個(gè)小端機(jī)。
現(xiàn)在跨網(wǎng)絡(luò)我們也不知道數(shù)據(jù)到底是大端和小端。
所以就有了規(guī)定:
網(wǎng)絡(luò)中的數(shù)據(jù)都是大端。
發(fā)送數(shù)據(jù)的主機(jī)如果是大端機(jī)就不用管,如果是小端機(jī)就把小端轉(zhuǎn)成大端再發(fā)送。
接收數(shù)據(jù)同理。
那么如何定義網(wǎng)絡(luò)數(shù)據(jù)流的地址呢?
發(fā)送主機(jī)把發(fā)送緩沖區(qū)的數(shù)據(jù)按內(nèi)存的地址從低到高進(jìn)行發(fā)送。
接收主機(jī)把接收緩沖區(qū)的數(shù)據(jù)按內(nèi)存的地址從低到高進(jìn)行接收。
也就是先發(fā)送的數(shù)據(jù)是低地址,后發(fā)送的數(shù)據(jù)是高地址。
TCP/IP協(xié)議規(guī)定,網(wǎng)絡(luò)數(shù)據(jù)流應(yīng)采用大端字節(jié)序,即低地址高字節(jié),不管這臺(tái)主機(jī)是大端機(jī)還是小端機(jī), 都會(huì)按照這個(gè)TCP/IP規(guī)定的網(wǎng)絡(luò)字節(jié)序來(lái)發(fā)送/接收數(shù)據(jù),如果當(dāng)前發(fā)送主機(jī)是小端, 就需要先將數(shù)據(jù)轉(zhuǎn)成大端; 否則就忽略, 直接發(fā)送即可。
而把數(shù)據(jù)轉(zhuǎn)化成大端的工作不用我們自己來(lái)做,可以調(diào)用以下庫(kù)函數(shù)做網(wǎng)絡(luò)字節(jié)序和主機(jī)字節(jié)序的轉(zhuǎn)換。
#include <arpa/inet.h>
// 主機(jī)序列轉(zhuǎn)網(wǎng)絡(luò)序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
// 網(wǎng)絡(luò)序列轉(zhuǎn)主機(jī)序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示host,n表示network,l表示32位長(zhǎng)整數(shù),s表示16位短整數(shù)。
主機(jī)是大端還是小端在函數(shù)內(nèi)部會(huì)自己進(jìn)行判斷。
如果主機(jī)是小端字節(jié)序,這些函數(shù)將參數(shù)做相應(yīng)的大小端轉(zhuǎn)換然后返回。
如果主機(jī)是大端字節(jié)序,這些 函數(shù)不做轉(zhuǎn)換,將參數(shù)原封不動(dòng)地返回。
四、socket套接字
前面我們說(shuō)過(guò)IP+端口號(hào)port標(biāo)識(shí)了全網(wǎng)唯一的進(jìn)程,我們把IP+port
就叫做套接字socket
4.1 socket常見(jiàn)接口
// 創(chuàng)建 socket 文件描述符 (TCP/UDP, 客戶(hù)端 + 服務(wù)器)
int socket(int domain, int type, int protocol);
// 綁定端口號(hào) (TCP/UDP, 服務(wù)器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 開(kāi)始監(jiān)聽(tīng)socket (TCP, 服務(wù)器)
int listen(int socket, int backlog);
// 接收請(qǐng)求 (TCP, 服務(wù)器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立連接 (TCP, 客戶(hù)端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
可以發(fā)現(xiàn)函數(shù)參數(shù)里面有個(gè)叫sockaddr的結(jié)構(gòu)體類(lèi)型,這是什么呢?
4.2 sockaddr結(jié)構(gòu)體
其實(shí)套接字有不少種類(lèi)型。
常見(jiàn)的有三種:
網(wǎng)絡(luò)套接字
原始套接字
域間套接字
網(wǎng)絡(luò)套接字主要運(yùn)用于跨主機(jī)之間的通信,也能支持本地通信,而域間套接字只能在本地通信。而原始套接字可以跨過(guò)傳輸層(TCP/IP協(xié)議)訪問(wèn)底層的數(shù)據(jù)。
這些套接字應(yīng)用場(chǎng)景完全不同,所以我們想用就得用三套不同的接口。
而為了方便,設(shè)計(jì)者只使用了一套接口,就可以通過(guò)不同的參數(shù),解決所有的通信場(chǎng)景。
這里舉兩個(gè)具體的套接字類(lèi)型:sockaddr_in
與sockaddr_un
struct sockaddr_in {
short int sin_family; // 地址族,一般為AF_INET
unsigned short int sin_port; // 端口號(hào),網(wǎng)絡(luò)字節(jié)序
struct in_addr sin_addr; // IP地址
unsigned char sin_zero[8]; // 用于填充,使sizeof(sockaddr_in)等于16
};
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* 帶有路徑的文件名 */
};
可以看到sockaddr_in
和sockaddr_un
是兩個(gè)不同的通信場(chǎng)景。區(qū)分它們就用16地址類(lèi)型協(xié)議家族的標(biāo)識(shí)符。 我們兩個(gè)結(jié)構(gòu)體都不用,用sockaddr
。
比方說(shuō)我們想用網(wǎng)絡(luò)通信,雖然參數(shù)是
const struct sockaddr *addr
,但實(shí)際傳遞進(jìn)去的卻是sockaddr_in
結(jié)構(gòu)體(注意要強(qiáng)制類(lèi)型轉(zhuǎn)換)。
在函數(shù)內(nèi)部一視同仁,全部看成sockaddr類(lèi)型,然后根據(jù)前兩個(gè)字節(jié)判斷到底是什么通信類(lèi)型然后再?gòu)?qiáng)轉(zhuǎn)回去。
我們可以把sockaddr
看成基類(lèi),把sockaddr_in
和sockaddr_un
看成派生類(lèi),構(gòu)成了多態(tài)體系。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-431396.html
五、總結(jié)
1?? IP+port
可以標(biāo)志全網(wǎng)唯一進(jìn)程。
2?? 套接字是一種通信機(jī)制(通信的兩方的一種約定),而套接字的表示形式:IP+port
。
3?? TCP和UDP的主要區(qū)別就是可靠傳輸和不可靠傳輸,而可不可靠是中性詞。
4?? 網(wǎng)絡(luò)字節(jié)序列簡(jiǎn)單粗暴的規(guī)定為大端。
5?? sockaddr用統(tǒng)一的接口方式來(lái)解決網(wǎng)絡(luò)通信的大部分問(wèn)題。
Socket可以看成在兩個(gè)程序進(jìn)行通訊連接中的一個(gè)端點(diǎn),一個(gè)程序?qū)⒁欢涡畔?xiě)入Socket中,該Socket將這段信息發(fā)送給另外一個(gè)Socket中,使這段信息能傳送到其他程序中。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-431396.html
到了這里,關(guān)于【網(wǎng)絡(luò)編程】socket套接字的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!