微網(wǎng)站模板建設(shè)編程培訓(xùn)機(jī)構(gòu)排名前十
再次用到socket編程,將socket相關(guān)的知識(shí)點(diǎn)做了簡單整理,根據(jù)網(wǎng)絡(luò)上大家的整理,又做了一些調(diào)整和匯總。
API列表
sokect常見的API大致有列表里面這么多,不同平臺(tái)的實(shí)現(xiàn)可能有些微的差別,下面對常用API的參數(shù)和用法做了逐個(gè)整理。
socket(int, int, int)
socketpair(int, int, int, int [2])
bind(int, const sockaddr *, socklen_t)
connect(int, const sockaddr *, socklen_t)
listen(int, int)
accept(int, sockaddr *, socklen_t *)
accept4(int, sockaddr *, socklen_t *, int)
send(int, const void *, size_t, int)
sendto(int, const void *, size_t, int, const sockaddr *, socklen_t)
recv(int, void *, size_t, int)
recvfrom(int, void *, size_t, int, sockaddr *, socklen_t *)
shutdown(int, int)
setsockopt(int, int, int, const void *, socklen_t)
getsockopt(int, int, int, void *, socklen_t *)
getsockname(int, sockaddr *, socklen_t *)
getpeername(int, sockaddr *, socklen_t *)
recvmsg(int, msghdr *, int)
sendmsg(int, msghdr *, int)
socket()函數(shù)
int socket(int domain, int type, int protocol);
-
socket
函數(shù)對應(yīng)于普通文件的打開操作。 -
普通文件的打開操作返回一個(gè)文件描述符,而**socket()**用于創(chuàng)建一個(gè)
socket
描述符(socket descriptor),它唯一標(biāo)識(shí)一個(gè)socket
。 -
socket
描述符跟文件描述符一樣,后續(xù)的操作都有用到它,把它作為參數(shù),通過它來進(jìn)行一些讀寫操作。
參數(shù):
-
domain
: 即協(xié)議域,又稱為協(xié)議族(family),常用的協(xié)議組有:- AF_INET(ipv4地址(32位的)與端口號(hào)(16位的)的組合)
- AF_INET6( IPv6 的地址族)
- AF_LOCAL(或稱AF_UNIX,Unix域socket)(用一個(gè)絕對路徑名作為地址)
- AF_ROUTE
-
type
: 指定socket類型,常用的socket類型有:- SOCK_STREAM
- SOCK_DGRAM
- SOCK_RAW
- SOCK_PACKET
- SOCK_SEQPACKET等等
-
protocol
:指定協(xié)議,常用的協(xié)議有:- IPPROTO_TCP(TCP傳輸協(xié)議)
- IPPTOTO_UDP(UDP傳輸協(xié)議)
- IPPROTO_SCTP(STCP傳輸協(xié)議)
- IPPROTO_TIPC等(TIPC傳輸協(xié)議)
📣 注意:并不是上面的type和protocol可以隨意組合的,如SOCK_STREAM不可以跟IPPROTO_UDP組合。當(dāng)protocol為0時(shí),會(huì)自動(dòng)選擇type類型對應(yīng)的默認(rèn)協(xié)議。
bind()函數(shù)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
-
在套接口中,一個(gè)套接字只是用戶程序與內(nèi)核交互信息的樞紐,它自身沒有太多的信息,也沒有網(wǎng)絡(luò)協(xié)議地址和端口號(hào)等信息,在進(jìn)行網(wǎng)絡(luò)通信的時(shí)候,必須把一個(gè)套接字與一個(gè)地址相關(guān)聯(lián),這個(gè)過程就是地址綁定的過程。
-
許多時(shí)候內(nèi)核會(huì)我們自動(dòng)綁定一個(gè)地址,調(diào)用connect()、listen()時(shí)系統(tǒng)會(huì)自動(dòng)隨機(jī)分配一個(gè)端口。
-
然而有時(shí)用戶可能需要自己來完成這個(gè)綁定的過程,以滿足實(shí)際應(yīng)用的需要,最典型的情況是一個(gè)服務(wù)器進(jìn)程需要綁定一個(gè)眾所周知的地址或端口以等待客戶來連接。這個(gè)事由
bind()
的函數(shù)完成。
參數(shù):
sockfd
: 即socket
描述符,它是通過socket()
函數(shù)創(chuàng)建了,唯一標(biāo)識(shí)一個(gè)socket
。bind()
函數(shù)就是將給這個(gè)描述符綁定一個(gè)名字。addr
: 一個(gè)const struct sockaddr *
指針,指向要綁定給sockfd
的協(xié)議地址。addrlen
:地址的長度。
比如alsa-lib/aserver/aserver.c中make_inet_socket函數(shù)的代碼:
struct sockaddr_in addr;int sock;sock = socket(PF_INET, SOCK_STREAM, 0);if (sock < 0) {int result = -errno;SYSERROR("socket failed");return result;}memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {return -errno;}
addr這個(gè)地址結(jié)構(gòu)是根據(jù)地址創(chuàng)建socket時(shí)的地址協(xié)議族的不同而不同,這里是sockaddr_in
,將addr初始化后轉(zhuǎn)化為struct sockaddr *
類型作為參數(shù)傳入。
struct sockaddr_in {short int sin_family; /* 通信類型 2bytes */unsigned short int sin_port; /* 端口 2bytes */struct in_addr sin_addr; /* Internet 地址 4bytes */unsigned char sin_zero[8];};
/* Internet address. */
struct in_addr {uint32_t s_addr; /* address in network byte order */
};
原來的sockaddr
的格式是(16個(gè)字節(jié)):
struct sockaddr {unsigned short sa_family; /* 地址家族, AF_xxx */char sa_data[14]; /* 14字節(jié)協(xié)議地址 */
};
可以看出sockaddr_in
和sockaddr
二者長度一樣,都是16個(gè)字節(jié),即占用的內(nèi)存大小是一致的,因此可以互相轉(zhuǎn)化。
sockaddr和sockaddr_in對比
-
sockaddr常用于bind、connect、recvfrom、sendto等函數(shù)的參數(shù),指明地址信息,是一種通用的套接字地址。
-
sockaddr_in是internet環(huán)境下套接字的地址形式。所以在網(wǎng)絡(luò)編程中我們會(huì)對
sockaddr_in
結(jié)構(gòu)體進(jìn)行操作,使用sockaddr_in來建立所需的信息,最后使用類型轉(zhuǎn)化就可以了。 -
一般先把sockaddr_in變量賦值后,強(qiáng)制類型轉(zhuǎn)換后傳入用sockaddr做參數(shù)的函數(shù)。
-
sockaddr_in用于socket定義和賦值。
-
sockaddr用于函數(shù)參數(shù)。
Unix域?qū)?yīng)的是sockaddr_un
,總共120個(gè)字節(jié):
#define UNIX_PATH_MAX 108struct sockaddr_un {sa_family_t sun_family; /* AF_UNIX */char sun_path[UNIX_PATH_MAX]; /* pathname */
};
📣
通常服務(wù)器在啟動(dòng)的時(shí)候都會(huì)綁定一個(gè)眾所周知的地址(如ip地址+端口號(hào)),用于提供服務(wù),客戶就可以通過它來接連服務(wù)器。
而客戶端就不用指定,有系統(tǒng)自動(dòng)分配一個(gè)端口號(hào)和自身的ip地址組合。這就是為什么通常服務(wù)器端在listen之前會(huì)調(diào)用bind(),而客戶端就不會(huì)調(diào) 用,而是在connect()時(shí)由系統(tǒng)隨機(jī)生成一個(gè)。
listen()函數(shù)
int listen(int sockfd, int backlog);
-
listen函數(shù)在一般在調(diào)用bind之后,調(diào)用accept之前調(diào)用。
-
作為一個(gè)服務(wù)器,在調(diào)用socket(),bind()之后就會(huì)調(diào)用
listen()
來監(jiān)聽這個(gè)socket。 -
listen函數(shù)使用主動(dòng)連接套接口變?yōu)楸贿B接套接口,使得一個(gè)進(jìn)程可以接受其它進(jìn)程的請求,從而成為一個(gè)服務(wù)器進(jìn)程。在TCP服務(wù)器編程中l(wèi)isten函數(shù)把進(jìn)程變?yōu)橐粋€(gè)服務(wù)器,并指定相應(yīng)的套接字變?yōu)楸粍?dòng)連接。
-
當(dāng)調(diào)用listen之后,服務(wù)器進(jìn)程就可以調(diào)用accept來接受一個(gè)外來的請求。
參數(shù):
sockfd
::socket描述符,唯一的id。backlog
:相應(yīng)socket可以排隊(duì)的最大連接個(gè)數(shù)。
connect()函數(shù)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
-
客戶端通過調(diào)用connect函數(shù)來建立與TCP服務(wù)器的連接。
-
客戶端調(diào)用connect()發(fā)出連接請求,服務(wù)器端就會(huì)接收到這個(gè)請求。
-
connect()函數(shù)返回后并不進(jìn)行數(shù)據(jù)交換。而是要等服務(wù)器端 accept 之后才能進(jìn)行數(shù)據(jù)交換。
-
成功返回0,失敗返回-1。當(dāng)客戶端調(diào)用 connect()函數(shù)之后,發(fā)生以下情況之一才會(huì)返回:
-
服務(wù)器端接收連接請求
-
發(fā)生斷網(wǎng)的異常情況而終端連接請求
-
參數(shù):
sockfd
:客戶端建立socket函數(shù)的返回值。addr
:指定所要連接的服務(wù)器的地址,服務(wù)器的socket地址(要和服務(wù)端的實(shí)際IP地址以及綁定的端口一致才可以)。addrlen
:socket協(xié)議地址的長度,可由sizeof()計(jì)算得出。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-I8aODEq6-1676817320838)(.images/1659149924677533.png)]
accept()函數(shù)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
-
TCP服務(wù)器端依次調(diào)用socket()、bind()、listen()之后,就會(huì)監(jiān)聽指定的socket地址了。
-
TCP客戶端依次調(diào)用socket()、connect()之后就向TCP服務(wù)器發(fā)送了一個(gè)連接請求。
-
TCP服務(wù)器監(jiān)聽到這個(gè)請求之后,就會(huì)調(diào)用accept()函數(shù)取接收請求,這樣連接就建立好了。
-
然后就可以開始網(wǎng)絡(luò)I/O操作了,即類同于普通文件的讀寫I/O操作。
參數(shù):
sockfd
:服務(wù)器的socket描述符。addr
:指向struct sockaddr *
的指針,用于返回給客戶端的協(xié)議地址。addrlen
:socket協(xié)議地址的長度。
send()函數(shù)
int send(int sockfd, const void *buf, int len, int flags);
參數(shù):
sockfd
:想要發(fā)送數(shù)據(jù)的套接字描述符。buf
:指向發(fā)送數(shù)據(jù)緩沖區(qū)的指針。flags
:一般設(shè)置為0.
-
send()返回實(shí)際發(fā)送的數(shù)據(jù)的字節(jié)數(shù),可能小于你要求發(fā)送的長度,在錯(cuò)誤的時(shí)候返回
SOCKET_ERROR(-1)
,并設(shè)置errno。 -
send()只是發(fā)送它可能發(fā)送的數(shù)據(jù),如果send()返回的數(shù)據(jù)和
len
不匹配,就需要將剩下的數(shù)據(jù)發(fā)送完。 -
send()先比較待發(fā)送數(shù)據(jù)的長度len和套接字sockfd的發(fā)送緩沖的長度, 如果len大于sockfd的發(fā)送緩沖區(qū)的長度,該函數(shù)返回SOCKET_ERROR。
-
如果len小于或者等于sockfd的發(fā)送緩沖區(qū)的長度,那么send()先檢查協(xié)議是否正在發(fā)送s的發(fā)送緩沖中的數(shù)據(jù),如果是就等待協(xié)議把數(shù)據(jù)發(fā)送完,如果協(xié)議還沒有開始發(fā)送sockfd的發(fā)送緩沖中的數(shù)據(jù)或者sockfd的發(fā)送緩沖中沒有數(shù)據(jù),那么send就比較sockfd的發(fā)送緩沖區(qū)的剩余空間和len:
-
如果len大于剩余空間大小,send()就一直等待協(xié)議把sockfd的發(fā)送緩沖中的數(shù)據(jù)發(fā)送完。
-
如果len小于剩余空間大小,send()就僅僅把buf中的數(shù)據(jù)copy到剩余空間里(注意并不是send()把sockfd的發(fā)送緩沖中的數(shù)據(jù)傳到連接的另一端的,而是協(xié)議傳的,send僅僅是把buf中的數(shù)據(jù)copy到s的發(fā)送緩沖區(qū)的剩余空間里)。
-
如果協(xié)議在后續(xù)的傳送過程中出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤的話,那么下一個(gè)Socket函數(shù)就會(huì)返回SOCKET_ERROR。
-
📣
每一個(gè)除send外的Socket函數(shù)在執(zhí)行的最開始總要先等待套接字的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢才能繼續(xù),如果在等待時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么該Socket函數(shù)就返回 SOCKET_ERROR。
在Unix系統(tǒng)下,如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開的話,調(diào)用send的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對該信號(hào)的默認(rèn)處理是進(jìn)程終止。
異步socket的send函數(shù)在網(wǎng)絡(luò)剛剛斷開時(shí)還能發(fā)送返回相應(yīng)的字節(jié)數(shù),同時(shí)使用select檢測也是可寫的,但是過幾秒鐘之后,再send就會(huì)出錯(cuò)了,返回-1。select也不能檢測出可寫了。
recv()函數(shù)
int recv(int sockfd, FAR void *buf, size_t len, int flags)
參數(shù):
sockfd
:要讀的套接字描述符。buf
:讀取數(shù)據(jù)的緩沖區(qū)。len
:緩沖區(qū)的最大長度。flags
:一般設(shè)置為0.
-
recv()返回實(shí)際讀入緩沖的數(shù)據(jù)的字節(jié)數(shù)?;蛘咴阱e(cuò)誤的時(shí)候返回
SOCKET_ERROR(-1)
,同時(shí)設(shè)置errno。 -
當(dāng)應(yīng)用程序調(diào)用recv函數(shù)時(shí),recv先等待sockfd的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢。
-
如果sockfd的發(fā)送緩沖中沒有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后,recv先檢查套接字sockfd的接收緩沖區(qū),如果sockfd接收緩沖區(qū)中沒有數(shù)據(jù)或者協(xié)議正在接收數(shù)據(jù),那么recv就一直等待,只到協(xié)議把數(shù)據(jù)接收完畢。
-
當(dāng)協(xié)議把數(shù)據(jù)接收完畢,recv函數(shù)就把sockfd的接收緩沖中的數(shù)據(jù)copy到buf中(注意協(xié)議接收到的數(shù)據(jù)可能大于buf的長度,所以在這種情況下要調(diào)用幾次recv()函數(shù)才能把sockfd的接收緩沖中的數(shù)據(jù)copy完。
-
recv()函數(shù)僅僅是copy數(shù)據(jù),真正的接收數(shù)據(jù)是協(xié)議來完成的。
-
如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)中斷了,那么它返回0。在Unix系統(tǒng)下,如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開了,那么調(diào)用recv的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對該信號(hào)的默認(rèn)處理是進(jìn)程終止。
shutdown()函數(shù)
int shutdown(int sockfd,int howto);
參數(shù):
sockfd
:socket文件描述符。howto
:如何控制斷連過程。- SHUT_RD:值為0,關(guān)閉連接的讀這一半。
- SHUT_WR:值為1,關(guān)閉連接的寫這一半。
- SHUT_RDWR:值為2,連接的讀和寫都關(guān)閉。
終止網(wǎng)絡(luò)連接的通用方法是調(diào)用close函數(shù),但使用shutdown能更好的控制斷連過程。
sendto()函數(shù)
int sendto(int sockfd,const void *buf,int len,unsigned int flags,const struct sockaddr *dest_addr,socklen_t addrlen
);
參數(shù):
sockfd
:socket文件描述符。buf
:發(fā)送的數(shù)據(jù)緩沖區(qū)。len
:發(fā)送的數(shù)據(jù)長度。flags
:該參數(shù)一般為0。dest_addr
:(可選)指明數(shù)據(jù)發(fā)送的目標(biāo)地址。addrlen
:(可選)目標(biāo)地址長度,一般為:sizeof(struct sockaddr_in)
。
對于sendto()函數(shù),成功則返回實(shí)際傳送出去的字符數(shù),失敗返回-1,錯(cuò)誤原因存于errno中。
一般情況下,send()、recv()在TCP協(xié)議下使用,sendto()、recvfrom()在UDP協(xié)議下使用,也可以在TCP協(xié)議下使用,不過用的很少。
在無連接的socket方式下,由于本地socket并沒有與遠(yuǎn)端機(jī)器建立連接,所以在發(fā)送數(shù)據(jù)時(shí)應(yīng)指明目的地址,所以該函數(shù)比send()函數(shù)多了兩個(gè)參數(shù)dest_addr和addrlen。
recvfrom()函數(shù)
int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *src_addr,socklen_t *addrlen
);
參數(shù):
sockfd
:socket文件描述符。buf
:接收數(shù)據(jù)緩沖區(qū)。len
:接收緩沖區(qū)長度。flags
:調(diào)用操作方式。src_addr
:(可選)如果src_addr不為NULL,并且底層協(xié)議提供了源地址,則會(huì)填充此源地址。當(dāng)src_addr為NULL時(shí),不填寫任何內(nèi)容。addrlen
:(可選)指向from緩沖區(qū)長度值。
對于recvfrom()
函數(shù),成功則返回接收到的字符數(shù),失敗則返回-1,錯(cuò)誤原因存于errno中。
recvfrom()
用于從套接字接收消息,并且可用于接收套接字上的數(shù)據(jù),無論它是否面向連接。
其他相關(guān)函數(shù)
poll()函數(shù)
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
參數(shù):
-
fdarray
:一個(gè)結(jié)構(gòu)體,用來保存各個(gè)描述符的相關(guān)狀態(tài)。 -
nfds
:fdarray數(shù)組的大小,即里面包含有效成員的數(shù)量。 -
timeout
:設(shè)定的超時(shí)時(shí)間(以毫秒為單位)。
select()函數(shù)和poll()函數(shù)均是主要用來處理多路I/O復(fù)用的情況。比如一個(gè)服務(wù)器既想等待輸入終端到來,又想等待若干個(gè)套接字有客戶請求到達(dá),這時(shí)候就需要借助select或者poll函數(shù)了。
select()函數(shù)
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
參數(shù):
nfds
:本參數(shù)忽略,僅起到兼容作用。readfds
:指針,指向一組等待可讀性檢查的套接口。writefds
:指針,指向一組等待可寫性檢查的套接口。exceptfds
:指針,指向一組等待錯(cuò)誤檢查的套接口。timeout
:select()
最多等待時(shí)間,對阻塞操作則為NULL。
Select系統(tǒng)調(diào)用是用來讓我們的程序監(jiān)視多個(gè)文件描述符的狀態(tài)變化的。
使用select就可以完成非阻塞方式工作的程序,它能夠監(jiān)視我們需要監(jiān)視的文件描述符的變化情況。
fcntl()函數(shù)
int fcntl(int sockfd, int cmd, ... /* arg */);
參數(shù):
sockfd
:套接字描述符。cmd
:操作命令。arg
:與cmd關(guān)聯(lián)的數(shù)據(jù)。
返回值:
如果成功,返回的值將取決于指定的 cmd。 如果不成功,fcntl() 返回-1并將errno設(shè)置為以下之一:
- EBADF:套接字參數(shù)不是有效的套接字描述符。
- EINVAL:arg參數(shù)不是有效標(biāo)志,或者cmd參數(shù)不是有效命令。
fcntl()
實(shí)際上是一個(gè)系統(tǒng)調(diào)用,不是socket的API,用于對打開的文件描述符執(zhí)行操作,比如獲取或設(shè)置文件描述符標(biāo)志(更改O_APPEND或O_NONBLOCK狀態(tài)標(biāo)志)。
有以下操作命令可供使用:
- F_DUPFD:復(fù)制文件描述符。
- FD_CLOEXEC:設(shè)置
close-on-exec
標(biāo)志,如果FD_CLOEXEC
位是 0,執(zhí)行exec
相關(guān)函數(shù)后文件句柄保持打開,反之則關(guān)閉。 - F_GETFD:讀取文件描述符標(biāo)志。
- F_SETFD:設(shè)置文件描述符標(biāo)志。
- F_GETFL:獲取套接字描述符套接字的狀態(tài)標(biāo)志.
- F_SETFL:設(shè)置套接字描述符套接字的狀態(tài)標(biāo)志。
如將sockfd設(shè)置為非阻塞方式:
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
將sockfd設(shè)置為非阻塞方式:
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
close()函數(shù)
int close(int sockfd);
close()
一個(gè)套接字的默認(rèn)行為是把套接字標(biāo)記為已關(guān)閉,然后立即返回到調(diào)用進(jìn)程,該套接字描述符不能再由調(diào)用進(jìn)程使用。
STREAM流模式和DGRAM數(shù)據(jù)報(bào)模式的區(qū)別
📣 “TCP是一種流模式的協(xié)議,UDP是一種數(shù)據(jù)報(bào)模式的協(xié)議”
為什么這樣說,這是基于TCP和UDP的工作方式定義的。
TCP的定義
- 傳輸控制協(xié)議(TCP,Transmission Control Protocol)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。
TCP的工作方式可以這樣理解:
大概就像輸油管道一樣,需要建立連接,數(shù)據(jù)大小不固定:
- 發(fā)送方和接收方建立連接(三次握手)
- 發(fā)送方和接收方通過TCP連接交換字節(jié)流。例如發(fā)送方藥發(fā)送80字節(jié)的數(shù)據(jù),如果發(fā)送端先傳10字節(jié),又傳20字節(jié),再傳50字節(jié),連接的另一方將無法了解發(fā)方每次發(fā)送了多少字節(jié)。接收方只要自己的接收緩存沒有塞滿,TCP接收方將有多少就收多少。發(fā)送方將字節(jié)流放到TCP連接上,接收方就接受多少信息。
- 發(fā)送方發(fā)送完消息會(huì)通知接收方關(guān)閉連接(四次揮手)
UDP的定義
- 用戶數(shù)據(jù)報(bào)協(xié)議(UDP,User Datagram Protocol)。是一種無需建立連接、不可靠的、基于數(shù)據(jù)包的傳輸層通信協(xié)議
UDP的工作方式可以這樣理解:
大概就像發(fā)短信一樣,不需要建立連接,數(shù)據(jù)大小固定:
發(fā)送方將要發(fā)送的數(shù)據(jù)封裝到數(shù)據(jù)包中,發(fā)送給接收方,接收方會(huì)接收這個(gè)數(shù)據(jù)包。
參考
手撕Linux Socket——Socket原理與實(shí)踐分析
秒懂流模式和數(shù)據(jù)報(bào)模式