加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專(zhuān)業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 1. 什么是socket套接字
    • ?2. socket編程
    • ?3. 網(wǎng)絡(luò)字節(jié)序
    • ?4. IP地址轉(zhuǎn)換函數(shù)
    • 5. sockaddr數(shù)據(jù)結(jié)構(gòu)
    • 6. 網(wǎng)絡(luò)套接字函數(shù)
    • 7. TCP通信流程圖
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

socket套接字

11/18 14:40
504
閱讀需 13 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

1. 什么是socket套接字

套接字就像一個(gè)插座,插座需要一個(gè)插頭來(lái)連接雙方才能通電,而socket通信也需要兩個(gè)端,一個(gè)服務(wù)端一個(gè)客戶端。一般來(lái)說(shuō),服務(wù)端是被動(dòng)的,客戶端是主動(dòng)的,也就是說(shuō)服務(wù)端應(yīng)該先啟動(dòng),啟動(dòng)之后就被動(dòng)的去準(zhǔn)備被(客戶端)連接以提供服務(wù),而客戶端需要服務(wù)的時(shí)候就主動(dòng)去連接服務(wù)器端。

實(shí)際上,socket編程就是網(wǎng)絡(luò)IO編程,同樣也是讀寫(xiě)操作,只不過(guò)是對(duì)網(wǎng)絡(luò)進(jìn)行讀寫(xiě),通過(guò)read/write和文件描述符來(lái)完成讀寫(xiě)。我們?cè)趧?chuàng)建套接字的時(shí)候,會(huì)得到文件描述符,然后就可以通過(guò)這個(gè)文件描述符來(lái)完成讀寫(xiě)操作。

實(shí)際上,我們?cè)谶M(jìn)程間通信時(shí)用的管道也是在內(nèi)核中分配一塊緩沖區(qū),這個(gè)緩沖區(qū)是用一個(gè)環(huán)形隊(duì)列來(lái)維護(hù)的,本質(zhì)是內(nèi)存中的一塊存儲(chǔ)空間,在管道的讀寫(xiě)兩端分別對(duì)應(yīng)一個(gè)文件描述符,操作讀端的文件描述符fd就相當(dāng)于操作內(nèi)核緩沖區(qū)。

套接字創(chuàng)建成功后,也會(huì)得到一個(gè)文件描述符fd,通過(guò)fd來(lái)操作一塊內(nèi)核緩沖區(qū)。在服務(wù)器端創(chuàng)建一個(gè)套接字,就會(huì)得到一個(gè)內(nèi)核緩沖區(qū)和文件描述符,這個(gè)緩沖區(qū)分為讀寫(xiě)兩部分。在客戶端發(fā)數(shù)據(jù)使用的是write操作,當(dāng)我們執(zhí)行write(fd)的時(shí)候,數(shù)據(jù)并不是直接寫(xiě)到網(wǎng)上的,而是先寫(xiě)到文件描述符對(duì)應(yīng)的內(nèi)核緩沖區(qū)中的寫(xiě)緩沖區(qū)部分,寫(xiě)緩沖區(qū)中只要有數(shù)據(jù)就會(huì)自動(dòng)發(fā)送到服務(wù)器端的讀緩沖區(qū)中,服務(wù)器端通過(guò)read就可以把數(shù)據(jù)讀出。我們所做的只有read和write操作,其他操作都是由操作系統(tǒng)完成的。需要注意的一點(diǎn)是,讀緩沖區(qū)中的數(shù)據(jù)讀走了之后就沒(méi)有了,和管道一樣。

套接字對(duì)應(yīng)的文件描述符默認(rèn)也是阻塞的,實(shí)際上阻塞是文件描述符對(duì)應(yīng)的文件所擁有的性質(zhì),而不是read/write的屬性,這兩個(gè)函數(shù)只負(fù)責(zé)讀取或者寫(xiě)數(shù)據(jù),即阻塞性質(zhì)是對(duì)文件描述符所對(duì)應(yīng)的文件類(lèi)型而言的。

?2. socket編程

- socket是一套網(wǎng)絡(luò)通信的函數(shù)接口

- socket內(nèi)封裝傳輸層協(xié)議

- TCP

- UDP

socket編程就是使用別人提供的一套網(wǎng)絡(luò)通信接口進(jìn)行編程。比如說(shuō)我們使用瀏覽器搜索內(nèi)容,瀏覽器使用的是HTTP協(xié)議,而HTTP協(xié)議再往下封裝的就是TCP協(xié)議。

在套接字編程時(shí)需要IP和Port:

- IP地址:在網(wǎng)絡(luò)環(huán)境中,需要IP來(lái)定位一臺(tái)主機(jī)

- 端口號(hào)Port:在一臺(tái)主機(jī)上,需要Port來(lái)定位一個(gè)進(jìn)程

- IP:Port

?3. 網(wǎng)絡(luò)字節(jié)序

- 大端:網(wǎng)絡(luò)字節(jié)序,數(shù)據(jù)的高位字節(jié)存儲(chǔ)在內(nèi)存的低地址。

- 小端:主機(jī)字節(jié)序,數(shù)據(jù)的高位字節(jié)存儲(chǔ)在內(nèi)存的高位地址。常見(jiàn)的主機(jī)數(shù)據(jù)都是小端存儲(chǔ)。

函數(shù)介紹:

#include <arpa/inet.h>

(1) 主機(jī)字節(jié)序轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序

uint16_t htons(uint16_t hostshort); //端口uint32_t htonl(uint32_t hostlong); //IP

(2) 網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)主機(jī)字節(jié)序

uint16_t ntohs(uint16_t netshort); //端口uint32_t ntohl(uint32_t netlong); //IP

假如說(shuō)我們要將小端字節(jié)序轉(zhuǎn)換為大端字節(jié)序,如果主機(jī)是小端字節(jié)序,這些函數(shù)將參數(shù)做相應(yīng)的大小端轉(zhuǎn)換后返回,如果主機(jī)是大端字節(jié)序,這些函數(shù)將不做任何變換,將參數(shù)原封不動(dòng)的返回。

常見(jiàn)的文件字節(jié)序:

- Adobe PS  ---  Big Endian- BMP             ---    Little Endian- GIF               ---    Little Endian- JPEG             ---    Big Endian- MacPaint     ---    Big Endian- RTF               ---     Little Endian

注:在Java以及所有的網(wǎng)絡(luò)通訊協(xié)議都是使用Big-Endian編碼。

?4. IP地址轉(zhuǎn)換函數(shù)

指定IP轉(zhuǎn)換為點(diǎn)分十進(jìn)制字符串

- 本地IP轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序:字符串 ---> int(大端方式存儲(chǔ))

  int inet_pton(int af, const char* src, void* dst);

- af:地址簇協(xié)議

- src:點(diǎn)分十進(jìn)制IP

- dest:傳出參數(shù),轉(zhuǎn)換后的int整形的存放地址

- 網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)本地IP:int ---> 字符串

  const char *inet_ntop(int af, const void* src, char* dst, socklen_t size);

5. sockaddr數(shù)據(jù)結(jié)構(gòu)

- sockaddr

- sockaddrin

- sockaddrun

struct sockaddr {/* address family, AF_xxx */sa_family_t sa_family; /* 14 bytes of protocol address */char sa_data[14];};struct sockaddr_in {__kernel_sa_family_t sin_family;   // 地址族協(xié)議     __be16 sin_port;  // 端口struct in_addr sin_addr;   // IP地址unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];};struct in_addr {__be32 s_addr;};

IPv4地址用socketaddr_in結(jié)構(gòu)體表示,包括16位端口號(hào)和32位IP地址,IPv6地址用socketaddr_in6結(jié)構(gòu)體表示,包括16位端口號(hào)、128位IP地址和一些控制字段。

6. 網(wǎng)絡(luò)套接字函數(shù)

(1) 創(chuàng)建套接字

int?socket(int?domain,?int?type,?int?protocol);

- 創(chuàng)建一個(gè)套接字

- domin

- AF_INET:這是大多數(shù)用來(lái)產(chǎn)生socket的協(xié)議,使用TCP或UDP來(lái)傳輸,使用IPv4的地址;

- AF_INET6:使用IPv6的地址;

- AF_UNIX:本地協(xié)議,使用在Unix和Linux系統(tǒng)上,一般都是當(dāng)客戶端和服務(wù)器端都在同一臺(tái)機(jī)器上的時(shí)候使用;

- type

- SOCK_STREAM:流式協(xié)議,這個(gè)協(xié)議是按照順序的、可靠的、數(shù)據(jù)類(lèi)型完整的、基于字節(jié)流的連接。這是一個(gè)使用最多的socket類(lèi)型,這個(gè)socket是使用TCP來(lái)進(jìn)行傳輸?shù)模?/p>

- SOCK_DGRAM:報(bào)式協(xié)議,這個(gè)協(xié)議式無(wú)連接的、固定長(zhǎng)度的傳輸調(diào)用,該協(xié)議是不可靠的,使用UDP進(jìn)行傳輸;

- SOCK_SEQPACKET:該協(xié)議是雙線路的、可靠的連接,發(fā)送固定長(zhǎng)度的數(shù)據(jù)包進(jìn)行傳輸,必須把這個(gè)包完整的接收才能進(jìn)行讀??;

- SOCK_RAW:socket類(lèi)型提供單一的網(wǎng)絡(luò)訪問(wèn),這個(gè)socket類(lèi)型使用ICMP公共協(xié)議,ping以及traceroute都使用該協(xié)議;

- SOCK_RDM:這個(gè)類(lèi)型使用較少,在大部分操作系統(tǒng)上沒(méi)有實(shí)現(xiàn),它提供給數(shù)據(jù)鏈路層使用,不保證數(shù)據(jù)包的順序;

- protocol:設(shè)置0表示使用默認(rèn)協(xié)議;協(xié)議,常見(jiàn)的協(xié)議有IPPROTO_TCP、IPPTOTO_UDP、 IPPROTO_SCTP、IPPROTO_TIPC他們分別對(duì)應(yīng)這TCP傳輸協(xié)議、UDP傳輸協(xié)議、STCP傳輸協(xié)議、TIPC傳輸協(xié)議。當(dāng)protocol為0時(shí),會(huì)自動(dòng)選擇type類(lèi)型對(duì)應(yīng)的默認(rèn)協(xié)議;

- 返回值為文件描述符(套接字),即創(chuàng)建好的socket套接字的文件描述符。On success, a file descriptor for ?the ?new ?socket ?is ?returned. ? On error, -1 is returned, and errno is set appropriately.

(2) 綁定

int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

- 將本地的IP和端口號(hào)與創(chuàng)建出來(lái)的套接字綁定,將參數(shù)sockfd和addr綁定在一起,使sockfd這個(gè)用于網(wǎng)絡(luò)通訊的文件描述符監(jiān)聽(tīng)addr所描述的地址和端口號(hào)。

- sockfd:創(chuàng)建出的文件描述符

- addr:端口和IP

- addrlen:addr結(jié)構(gòu)體的長(zhǎng)度,sizeof(addr)

(3) 監(jiān)聽(tīng)

int listen(int sockfd, int backlog);

- 設(shè)置同時(shí)連接到服務(wù)器的客戶端的個(gè)數(shù),listen()聲明sockfd處于監(jiān)聽(tīng)狀態(tài),并且最多允許有backlog個(gè)客戶端處于連接等待狀態(tài),如果接收到更多的連接請(qǐng)求就忽略。

- sockfd:socket函數(shù)創(chuàng)建出來(lái)的文件描述符;

- backlog:同時(shí)能連接的最大數(shù)量,最大值為128;

(4) 接受連接

int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);

- 阻塞等待客戶端連接請(qǐng)求,并接受連接。

- sockfd:文件描述符,使用socket創(chuàng)建出來(lái)的文件描述符;

- 監(jiān)聽(tīng)的文件描述符;

- addr:存儲(chǔ)客戶端的端口和IP,是一個(gè)傳出參數(shù);

- addrlen:傳入傳出參數(shù)(值 - 結(jié)果),傳入sizeof(addr)的大小,函數(shù)返回時(shí)返回真正接收到地址結(jié)構(gòu)體的大?。?/p>

- 函數(shù)返回值是一個(gè)套接字,對(duì)應(yīng)客戶端,服務(wù)器端與客戶端進(jìn)程通信使用accept的返回值對(duì)應(yīng)的套接字。

(5) 連接

int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

- 客戶端需要調(diào)用connect()函數(shù)連接服務(wù)器,connect和bind的參數(shù)形式一致,區(qū)別在于bind的參數(shù)是自己的地址,而connect的參數(shù)是對(duì)方的地址。

- sockfd:套接字;

- addr:傳入?yún)?shù),指定服務(wù)器端地址信息,服務(wù)器端的IP和端口;

- addrlen:第二個(gè)參數(shù)addr的長(zhǎng)度;

7. TCP通信流程圖

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

Linux、C、C++、Python、Matlab,機(jī)器人運(yùn)動(dòng)控制、多機(jī)器人協(xié)作,智能優(yōu)化算法,貝葉斯濾波與卡爾曼濾波估計(jì)、多傳感器信息融合,機(jī)器學(xué)習(xí),人工智能。