中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

建站之星如何建網(wǎng)站sem推廣是什么

建站之星如何建網(wǎng)站,sem推廣是什么,學(xué)軟件技術(shù)需要什么基礎(chǔ),防火墻放行圖片域名一、操作函數(shù)簡(jiǎn)介 在 Linux 中,TCP(傳輸控制協(xié)議)操作涉及多種系統(tǒng)調(diào)用和函數(shù),通常用來(lái)創(chuàng)建套接字、連接、發(fā)送/接收數(shù)據(jù)、關(guān)閉連接等。以下是一些常用的 TCP 操作函數(shù)和它們的簡(jiǎn)要說(shuō)明: 1. socket() 函數(shù)原型: int…

一、操作函數(shù)簡(jiǎn)介

在 Linux 中,TCP(傳輸控制協(xié)議)操作涉及多種系統(tǒng)調(diào)用和函數(shù),通常用來(lái)創(chuàng)建套接字、連接、發(fā)送/接收數(shù)據(jù)、關(guān)閉連接等。以下是一些常用的 TCP 操作函數(shù)和它們的簡(jiǎn)要說(shuō)明:

1. socket()

  • 函數(shù)原型: int socket(int domain, int type, int protocol);
  • 功能: 創(chuàng)建一個(gè)新的套接字(socket),它是與網(wǎng)絡(luò)通信相關(guān)的基本對(duì)象。
  • 參數(shù):
    • domain: 協(xié)議族(如 AF_INET 用于 IPv4,AF_INET6 用于 IPv6)。
    • type: 套接字類型(如 SOCK_STREAM 表示 TCP,SOCK_DGRAM 表示 UDP)。
    • protocol: 使用的協(xié)議,通常設(shè)為 0,由系統(tǒng)自動(dòng)選擇合適的協(xié)議。
  • 返回值: 返回一個(gè)套接字描述符(文件描述符),失敗時(shí)返回 -1。

2. bind()

  • 函數(shù)原型: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 功能: 將套接字與本地地址(IP 地址和端口)綁定。
  • 參數(shù):
    • sockfd: 要綁定的套接字。
    • addr: 地址結(jié)構(gòu),通常是 struct sockaddr_in,指定 IP 和端口。
    • addrlen: 地址結(jié)構(gòu)的長(zhǎng)度。
  • 返回值: 成功返回 0,失敗返回 -1。

3. listen()

  • 函數(shù)原型: int listen(int sockfd, int backlog);
  • 功能: 將套接字設(shè)置為被動(dòng)模式,等待客戶端連接。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • backlog: 最多可連接的等待隊(duì)列的大小。
  • 返回值: 成功返回 0,失敗返回 -1。

4. accept()

  • 函數(shù)原型: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 功能: 接受來(lái)自客戶端的連接請(qǐng)求,并返回一個(gè)新的套接字描述符用于與客戶端通信。
  • 參數(shù):
    • sockfd: 已經(jīng)調(diào)用 listen() 的套接字。
    • addr: 客戶端的地址信息。
    • addrlen: 地址結(jié)構(gòu)的大小。
  • 返回值: 返回新的套接字描述符,用于與客戶端的通信,失敗時(shí)返回 -1。

5. connect()

  • 函數(shù)原型: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 功能: 客戶端發(fā)起與服務(wù)器的連接請(qǐng)求。
  • 參數(shù):
    • sockfd: 客戶端套接字描述符。
    • addr: 目標(biāo)服務(wù)器的地址信息。
    • addrlen: 地址結(jié)構(gòu)的長(zhǎng)度。
  • 返回值: 成功返回 0,失敗返回 -1。

6. send()

  • 函數(shù)原型: ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • 功能: 通過(guò)套接字發(fā)送數(shù)據(jù)。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • buf: 數(shù)據(jù)緩沖區(qū)。
    • len: 發(fā)送數(shù)據(jù)的長(zhǎng)度。
    • flags: 發(fā)送標(biāo)志(一般設(shè)為 0)。
  • 返回值: 返回實(shí)際發(fā)送的字節(jié)數(shù),失敗時(shí)返回 -1。

7. recv()

  • 函數(shù)原型: ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • 功能: 從套接字接收數(shù)據(jù)。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • buf: 存儲(chǔ)接收到數(shù)據(jù)的緩沖區(qū)。
    • len: 接收數(shù)據(jù)的最大長(zhǎng)度。
    • flags: 接收標(biāo)志(一般設(shè)為 0)。
  • 返回值: 返回實(shí)際接收的字節(jié)數(shù),失敗時(shí)返回 -1。

8. close()

  • 函數(shù)原型: int close(int fd);
  • 功能: 關(guān)閉套接字,釋放相關(guān)資源。
  • 參數(shù):
    • fd: 套接字描述符。
  • 返回值: 成功返回 0,失敗返回 -1。

9. shutdown()

  • 函數(shù)原型: int shutdown(int sockfd, int how);
  • 功能: 用于關(guān)閉套接字的讀、寫(xiě)或者雙向通信。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • how: 控制關(guān)閉的方式,常用值為:
      • SHUT_RD: 關(guān)閉讀取(不能再讀取數(shù)據(jù))。
      • SHUT_WR: 關(guān)閉寫(xiě)入(不能再發(fā)送數(shù)據(jù))。
      • SHUT_RDWR: 同時(shí)關(guān)閉讀寫(xiě)。
  • 返回值: 成功返回 0,失敗返回 -1。

10. getsockopt() 和 setsockopt()

  • 函數(shù)原型:
    • int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
    • int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • 功能: 用于獲取或設(shè)置套接字的選項(xiàng)(如 TCP 的各種參數(shù),如緩沖區(qū)大小、超時(shí)時(shí)間等)。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • level: 設(shè)置選項(xiàng)的協(xié)議層級(jí),通常為 SOL_SOCKET(套接字層)或 IPPROTO_TCP(TCP 層)。
    • optname: 選項(xiàng)名稱(如 SO_RCVBUFSO_RCVBUF 等)。
    • optval: 選項(xiàng)的值。
    • optlen: 選項(xiàng)值的長(zhǎng)度。
  • 返回值: 成功返回 0,失敗返回 -1。

11. select() 和 poll()

  • 函數(shù)原型:
    • int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    • int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • 功能: 允許程序監(jiān)聽(tīng)多個(gè)套接字,并在某些事件(如可讀、可寫(xiě)等)發(fā)生時(shí)進(jìn)行處理。

12. accept4()(Linux 特有)

  • 函數(shù)原型: int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
  • 功能: 與 accept() 類似,但支持額外的標(biāo)志(如 SOCK_NONBLOCK 等),在非阻塞模式下返回。
  • 返回值: 返回一個(gè)新的套接字描述符,失敗時(shí)返回 -1

小結(jié):

這些是常見(jiàn)的用于 TCP 通信的 Linux 系統(tǒng)調(diào)用和函數(shù)。它們?cè)试S應(yīng)用程序通過(guò)網(wǎng)絡(luò)進(jìn)行基本的連接管理、數(shù)據(jù)發(fā)送/接收等操作。通常情況下,服務(wù)器會(huì)使用 socket()、bind()、listen()accept() 來(lái)創(chuàng)建并處理客戶端連接,而客戶端則使用 socket()connect() 發(fā)起連接。數(shù)據(jù)的發(fā)送和接收使用 send()recv()。

二、socket/listen/accept與TCB的關(guān)系

下面將詳細(xì)解釋在socket()、listen()、accept()等函數(shù)調(diào)用過(guò)程中,TCP控制塊(TCB,struct tcp_sock)的創(chuàng)建和隊(duì)列的使用,以及它們與文件描述符(socket_fdclient_fd)的關(guān)系。

1. socket() 函數(shù)調(diào)用后的TCB關(guān)聯(lián)

  • 當(dāng)你調(diào)用socket()函數(shù)時(shí),操作系統(tǒng)會(huì)為這個(gè)套接字創(chuàng)建一個(gè)struct sock結(jié)構(gòu)體(具體來(lái)說(shuō),如果是TCP套接字,將創(chuàng)建一個(gè)struct tcp_sock,它是struct sock的子類)。這個(gè)結(jié)構(gòu)體就是TCP控制塊(TCB),負(fù)責(zé)管理該套接字的所有TCP連接狀態(tài)。
  • 創(chuàng)建的sock結(jié)構(gòu)體會(huì)與socket_fd綁定,socket_fd是應(yīng)用層與內(nèi)核層進(jìn)行通信的文件描述符。通過(guò)socket_fd,內(nèi)核可以找到與之關(guān)聯(lián)的sock結(jié)構(gòu)體。

2. listen() 函數(shù)調(diào)用后的隊(duì)列創(chuàng)建

  • 當(dāng)調(diào)用listen()函數(shù)時(shí),TCP進(jìn)入監(jiān)聽(tīng)狀態(tài),這時(shí)在與該監(jiān)聽(tīng)套接字對(duì)應(yīng)的TCB上會(huì)創(chuàng)建兩個(gè)隊(duì)列:
    • 半連接隊(duì)列(Syn Queue):存放正在進(jìn)行三次握手的連接。
    • 全連接隊(duì)列(Accept Queue):存放已經(jīng)完成三次握手的連接。

這些隊(duì)列用于管理TCP連接的不同狀態(tài),但隊(duì)列中的成員并不是直接的TCB(struct tcp_sock)類型

  • 半連接隊(duì)列中的成員:是struct request_sock類型。request_sock是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu),用于在三次握手未完成時(shí)存儲(chǔ)連接請(qǐng)求的狀態(tài)信息。在接收到客戶端的SYN之后,服務(wù)端在半連接隊(duì)列中分配一個(gè)request_sock,并等待三次握手完成。

  • 全連接隊(duì)列中的成員:在三次握手完成后,內(nèi)核會(huì)從半連接隊(duì)列移除request_sock并創(chuàng)建一個(gè)完整的struct tcp_sock(也稱作TCB),然后將其移入全連接隊(duì)列中,表示該連接已經(jīng)建立。

3. accept() 函數(shù)調(diào)用后

  • 當(dāng)應(yīng)用程序調(diào)用accept()函數(shù)時(shí),內(nèi)核會(huì)從全連接隊(duì)列中取出一個(gè)已經(jīng)完成三次握手的TCP連接。

  • 在全連接隊(duì)列中的成員是一個(gè)完整的struct tcp_sock(即TCB),它記錄了該連接的所有TCP狀態(tài)。

  • 內(nèi)核會(huì)為這個(gè)新的TCP連接創(chuàng)建一個(gè)新的文件描述符,稱為client_fd,并將該文件描述符與這個(gè)TCP連接的TCB(struct tcp_sock)進(jìn)行綁定。

    換句話說(shuō),client_fd與新連接的struct tcp_sock關(guān)聯(lián)起來(lái),使得通過(guò)client_fd可以操作該TCP連接(如發(fā)送或接收數(shù)據(jù))。

總結(jié)流程

  1. socket(): 創(chuàng)建一個(gè)struct sock(具體為struct tcp_sock),并與socket_fd關(guān)聯(lián)。

  2. listen(): 在tcp_sock上創(chuàng)建半連接隊(duì)列和全連接隊(duì)列:

    • 半連接隊(duì)列存放struct request_sock,用于管理三次握手中的連接。
    • 全連接隊(duì)列存放已建立連接的struct tcp_sock。
  3. accept(): 從全連接隊(duì)列中取出一個(gè)struct tcp_sock,為它分配一個(gè)新的文件描述符client_fd,并將client_fd與這個(gè)TCP連接的TCB(struct tcp_sock)綁定。

因此,調(diào)用accept()后,全連接隊(duì)列中的TCP連接會(huì)與新的client_fd關(guān)聯(lián),應(yīng)用程序通過(guò)client_fd來(lái)處理這個(gè)TCP連接。


三、listen函數(shù)backlog的作用

listen()函數(shù)的backlog參數(shù)在TCP服務(wù)器中用于指定全連接隊(duì)列(Accept Queue)的最大長(zhǎng)度,即允許在服務(wù)器上排隊(duì)等待accept()的已建立連接的最大數(shù)量。

1. listen() 函數(shù)及 backlog 參數(shù)的作用

當(dāng)你調(diào)用listen()函數(shù)時(shí),服務(wù)器的套接字進(jìn)入監(jiān)聽(tīng)狀態(tài),開(kāi)始等待客戶端的連接請(qǐng)求。backlog參數(shù)定義了以下內(nèi)容:

  • 最大已完成連接數(shù)backlog參數(shù)指定全連接隊(duì)列的最大長(zhǎng)度,即已經(jīng)完成三次握手但尚未被應(yīng)用程序accept()取走的連接數(shù)。
  • 當(dāng)客戶端發(fā)起連接請(qǐng)求并完成了三次握手,連接會(huì)被放入全連接隊(duì)列。如果隊(duì)列已滿,新完成的連接將被拒絕,客戶端會(huì)收到TCP RST(復(fù)位)信號(hào),表示連接無(wú)法建立。

2. backlog 參數(shù)的工作機(jī)制

listen(sockfd, backlog)中:
  • 全連接隊(duì)列(Accept Queue) 存放的是已經(jīng)完成三次握手、處于ESTABLISHED狀態(tài)的連接,這些連接等待應(yīng)用程序調(diào)用accept()來(lái)處理。
  • 半連接隊(duì)列(Syn Queue) 管理尚未完全建立的連接(正在三次握手中的連接),它與backlog關(guān)系較小,主要受tcp_max_syn_backlog內(nèi)核參數(shù)的影響。
具體行為:
  • 當(dāng)全連接隊(duì)列中的連接數(shù)達(dá)到backlog限制時(shí),新完成的連接將無(wú)法進(jìn)入隊(duì)列,導(dǎo)致客戶端收到RST包,連接被拒絕。
  • 如果設(shè)置的backlog值太小,服務(wù)器可能無(wú)法處理高并發(fā)連接,導(dǎo)致連接請(qǐng)求頻繁被拒絕。
  • 如果設(shè)置的backlog值過(guò)大,可能會(huì)增加系統(tǒng)負(fù)擔(dān),尤其是在沒(méi)有足夠的資源或處理能力時(shí)。

3. backlog 參數(shù)的實(shí)際值

  • 雖然應(yīng)用程序可以指定backlog的大小,但內(nèi)核實(shí)際上會(huì)對(duì)該值進(jìn)行限制。

  • Linux內(nèi)核中有一個(gè)參數(shù)somaxconn,它定義了允許的最大backlog值。如果你在listen()中傳入的backlog值大于/proc/sys/net/core/somaxconn中設(shè)定的值,系統(tǒng)會(huì)將backlog限制為somaxconn的值。

    • 查看和調(diào)整 somaxconn 參數(shù)
      cat /proc/sys/net/core/somaxconn
      echo 1024 > /proc/sys/net/core/somaxconn
      

4. 實(shí)際例子

假設(shè)你調(diào)用了如下的listen()函數(shù):

listen(sockfd, 10);
  • 這意味著全連接隊(duì)列的長(zhǎng)度最大為10,即最多允許10個(gè)已經(jīng)完成三次握手的連接排隊(duì)等待accept()。
  • 如果第11個(gè)連接嘗試建立,服務(wù)器將返回TCP RST包,拒絕該連接。

5. 總結(jié)

  • backlog參數(shù)用于指定服務(wù)器上全連接隊(duì)列的最大長(zhǎng)度,即等待應(yīng)用層accept()調(diào)用的已建立連接數(shù)的最大值。
  • 過(guò)小的backlog值會(huì)導(dǎo)致高并發(fā)時(shí)連接被拒絕,而過(guò)大的值會(huì)增加系統(tǒng)資源占用,需根據(jù)系統(tǒng)處理能力合理設(shè)置。

四、半連接隊(duì)列的限制

在 TCP 服務(wù)器中,半連接隊(duì)列的數(shù)量(即 SYN 隊(duì)列)由內(nèi)核的 tcp_max_syn_backlog 參數(shù)控制。

1. 半連接隊(duì)列(SYN隊(duì)列):

  • 當(dāng)客戶端向服務(wù)器發(fā)送 SYN 請(qǐng)求時(shí),服務(wù)器將這個(gè)連接請(qǐng)求放入 半連接隊(duì)列(也稱為 SYN 隊(duì)列)。此隊(duì)列用于存儲(chǔ)尚未完成三次握手的連接。
  • 一旦握手完成并且服務(wù)器準(zhǔn)備好接受數(shù)據(jù),連接就會(huì)移入 全連接隊(duì)列(Accept Queue)。

2. tcp_max_syn_backlog 參數(shù):

  • 作用: 控制半連接隊(duì)列的最大長(zhǎng)度,即可以緩存的未完成三次握手的連接數(shù)。
  • 默認(rèn)值: 在大多數(shù) Linux 系統(tǒng)中,默認(rèn)值通常為 128,意味著最多可以緩存 128 個(gè)尚未完成三次握手的連接。
  • 調(diào)整: 可以通過(guò)修改 /proc/sys/net/ipv4/tcp_max_syn_backlog 文件來(lái)調(diào)整此值。例如:
    echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog
    
    或者在 sysctl.conf 中添加:
    net.ipv4.tcp_max_syn_backlog=2048
    

3. SYN 隊(duì)列溢出:

  • 如果半連接隊(duì)列已滿并且有新的 SYN 請(qǐng)求到達(dá),內(nèi)核會(huì)丟棄這些連接請(qǐng)求,通??蛻舳藭?huì)收到一個(gè) TCP RST(重置) 消息,或者如果客戶端重試,可能會(huì)延遲連接。
  • 為了避免此情況,通常需要根據(jù)實(shí)際的網(wǎng)絡(luò)負(fù)載來(lái)調(diào)整該參數(shù),尤其是在高并發(fā)的服務(wù)器上。

4. 全連接隊(duì)列:

  • 在調(diào)用 listen() 函數(shù)時(shí),backlog 參數(shù)設(shè)置的是 全連接隊(duì)列 的大小,即已完成三次握手的連接的最大數(shù)量。它并不直接影響半連接隊(duì)列的大小。
  • 如果 全連接隊(duì)列 已滿,accept() 會(huì)阻塞,直到隊(duì)列中有空間為止。

總結(jié):

  • 半連接隊(duì)列(SYN 隊(duì)列)的大小是由 tcp_max_syn_backlog 參數(shù)控制。
  • 全連接隊(duì)列(Accept Queue)的大小是由 listen() 函數(shù)的 backlog 參數(shù)控制。

因此,半連接隊(duì)列和全連接隊(duì)列的長(zhǎng)度由不同的參數(shù)控制,而服務(wù)器需要根據(jù)實(shí)際的負(fù)載情況合理配置這些參數(shù),以確保高并發(fā)時(shí)的連接性能和穩(wěn)定性。

五、send函數(shù)的第四個(gè)參數(shù)是什么作用

send()函數(shù)的第四個(gè)參數(shù)是**flags**,用于指定發(fā)送操作的行為。通過(guò)設(shè)置不同的標(biāo)志,應(yīng)用程序可以控制send()函數(shù)的具體行為。

send() 函數(shù)的原型

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd:目標(biāo)套接字的文件描述符。
  • buf:要發(fā)送的數(shù)據(jù)的緩沖區(qū)。
  • len:要發(fā)送的數(shù)據(jù)長(zhǎng)度。
  • flags:控制發(fā)送行為的標(biāo)志位(即第四個(gè)參數(shù))。

常用的 flags

以下是一些常用的標(biāo)志及其作用,它們可以組合使用(使用按位或操作符 |):

  1. MSG_DONTWAIT

    • 使send()成為非阻塞操作。如果套接字的發(fā)送緩沖區(qū)已滿,send()不會(huì)等待緩沖區(qū)空閑,而是立即返回,返回值為-1,并設(shè)置errnoEAGAINEWOULDBLOCK
    • 適用于非阻塞套接字,也可以臨時(shí)使阻塞套接字表現(xiàn)為非阻塞模式。
  2. MSG_OOB(Out-of-Band Data):

    • 發(fā)送緊急數(shù)據(jù)(帶外數(shù)據(jù)),僅適用于TCP協(xié)議。緊急數(shù)據(jù)會(huì)優(yōu)先于普通數(shù)據(jù)處理,但在實(shí)際應(yīng)用中,帶外數(shù)據(jù)的使用較少。
    • 常用于一些需要快速響應(yīng)的特殊場(chǎng)景。
  3. MSG_NOSIGNAL

    • 如果向已斷開(kāi)的連接發(fā)送數(shù)據(jù),通常會(huì)觸發(fā)SIGPIPE信號(hào),導(dǎo)致程序終止。使用該標(biāo)志可以抑制SIGPIPE信號(hào),防止程序崩潰。
    • 適用于需要處理網(wǎng)絡(luò)中斷且不希望信號(hào)干擾的場(chǎng)景。
  4. MSG_CONFIRM

    • 僅適用于基于某些協(xié)議(如UDP)的發(fā)送,表示希望確認(rèn)對(duì)端的存在,通常用于實(shí)現(xiàn)鏈路層的鄰居確認(rèn)。
    • 僅用于某些低層協(xié)議的特定場(chǎng)景,在常規(guī)TCP/UDP應(yīng)用中較少使用。
  5. MSG_DONTROUTE

    • 發(fā)送數(shù)據(jù)時(shí),不查找路由表,直接將數(shù)據(jù)發(fā)送到與目標(biāo)網(wǎng)絡(luò)直接相連的接口。通常用于網(wǎng)絡(luò)診斷和本地網(wǎng)絡(luò)通信的場(chǎng)景。
    • 在大多數(shù)普通應(yīng)用場(chǎng)景中很少使用。
  6. MSG_EOR(End of Record):

    • 僅用于某些基于記錄的協(xié)議,表示本次send()調(diào)用發(fā)送的數(shù)據(jù)是一個(gè)邏輯記錄的結(jié)束。
    • 對(duì)于常見(jiàn)的TCP或UDP通信,這個(gè)標(biāo)志不常用。
  7. MSG_MORE

    • 表示應(yīng)用程序還有更多的數(shù)據(jù)要發(fā)送。在某些協(xié)議(如TCP)中,使用該標(biāo)志時(shí),內(nèi)核會(huì)暫時(shí)將數(shù)據(jù)保留在緩沖區(qū)中,而不是立即發(fā)送,以減少網(wǎng)絡(luò)上的包數(shù)。
    • 適合分多次發(fā)送數(shù)據(jù),但希望減少網(wǎng)絡(luò)開(kāi)銷的場(chǎng)景。

示例:使用 MSG_DONTWAITMSG_NOSIGNAL

char message[] = "Hello, World!";
int result = send(sockfd, message, sizeof(message), MSG_DONTWAIT | MSG_NOSIGNAL);if (result == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {// 緩沖區(qū)已滿,發(fā)送失敗printf("Send would block, try again later.\n");} else {// 處理其他錯(cuò)誤perror("send");}
}

總結(jié)

send()函數(shù)的第四個(gè)參數(shù)flags用于控制發(fā)送操作的行為。常見(jiàn)的標(biāo)志包括MSG_DONTWAIT(非阻塞發(fā)送)、MSG_OOB(發(fā)送緊急數(shù)據(jù))、MSG_NOSIGNAL(避免SIGPIPE信號(hào))等。你可以根據(jù)具體應(yīng)用場(chǎng)景使用不同的標(biāo)志來(lái)改變send()的默認(rèn)行為。


六、為什么握手要三次,揮手要四次,揮手中間的兩次不能像握手那樣合并在一起嗎?

在TCP協(xié)議的三次握手四次揮手過(guò)程中,雖然在三次握手時(shí)可以將SYNACK合并到一個(gè)數(shù)據(jù)包發(fā)送,但在四次揮手過(guò)程中,FINACK通常不能合并到同一個(gè)數(shù)據(jù)包發(fā)送。這主要與TCP的連接狀態(tài)和雙方通信的半關(guān)閉狀態(tài)有關(guān)。

1. 三次握手(SYN 和 ACK 合并的原因)

在三次握手中,通信雙方需要同步序列號(hào),建立可靠的連接。具體過(guò)程是:

  • 第一次握手:客戶端發(fā)送一個(gè)SYN包,表示請(qǐng)求建立連接,并傳遞初始序列號(hào)。
  • 第二次握手:服務(wù)器收到SYN后,回復(fù)一個(gè)包含SYNACK的包。這里的ACK是對(duì)客戶端SYN的確認(rèn),而SYN則是服務(wù)器請(qǐng)求建立連接的信號(hào)。因?yàn)?code>SYN和ACK是針對(duì)不同的動(dòng)作(SYN是服務(wù)器發(fā)起的,而ACK是對(duì)客戶端請(qǐng)求的確認(rèn)),可以一起合并發(fā)送。
  • 第三次握手:客戶端收到后,發(fā)送ACK確認(rèn),連接建立。

這里之所以可以合并,是因?yàn)殡p方的狀態(tài)在邏輯上是同步的,服務(wù)器既要發(fā)出自己的SYN,又要確認(rèn)客戶端的SYN,可以一起處理。

2. 四次揮手(ACK 和 FIN 通常不能合并的原因)

四次揮手過(guò)程用于關(guān)閉TCP連接,具體如下:

  • 第一次揮手:客戶端發(fā)送一個(gè)FIN包,表示它要關(guān)閉連接(數(shù)據(jù)傳輸結(jié)束)。
  • 第二次揮手:服務(wù)器收到后,回復(fù)一個(gè)ACK,表示收到了客戶端的FIN請(qǐng)求,但服務(wù)器可能還在發(fā)送數(shù)據(jù)。
  • 第三次揮手:服務(wù)器發(fā)送完數(shù)據(jù)后,再發(fā)送一個(gè)FIN包,表示它也同意關(guān)閉連接。
  • 第四次揮手:客戶端收到服務(wù)器的FIN后,發(fā)送一個(gè)ACK包,確認(rèn)關(guān)閉。
原因:
  1. 連接的半關(guān)閉狀態(tài): 在四次揮手過(guò)程中,TCP協(xié)議允許連接進(jìn)入半關(guān)閉狀態(tài),即:

    • 當(dāng)客戶端發(fā)送FIN請(qǐng)求時(shí),意味著客戶端已經(jīng)不再發(fā)送數(shù)據(jù),但服務(wù)器還可以繼續(xù)發(fā)送未完成的數(shù)據(jù)。
    • 客戶端發(fā)送的FIN和服務(wù)器接收的ACK是兩個(gè)不同的操作,它們代表了不同的狀態(tài)。

    在這個(gè)階段,服務(wù)器回復(fù)的ACK只是表明收到了客戶端的FIN,但服務(wù)器還沒(méi)有準(zhǔn)備好關(guān)閉連接,因?yàn)榭赡苋匀挥袛?shù)據(jù)需要發(fā)送。如果此時(shí)合并ACKFIN,就意味著服務(wù)器已經(jīng)準(zhǔn)備好關(guān)閉連接了,但實(shí)際上它可能還沒(méi)有完成數(shù)據(jù)發(fā)送。

  2. 不同的時(shí)間點(diǎn)ACKFIN通常不會(huì)在同一時(shí)刻發(fā)生:

    • 客戶端發(fā)FIN后,服務(wù)器需要立即回復(fù)一個(gè)ACK,但是服務(wù)器可能還在發(fā)送數(shù)據(jù),并未準(zhǔn)備好關(guān)閉連接。
    • 只有當(dāng)服務(wù)器確認(rèn)所有數(shù)據(jù)發(fā)送完畢后,它才會(huì)發(fā)送FIN來(lái)關(guān)閉連接。這兩個(gè)操作通常在不同的時(shí)間點(diǎn)發(fā)生,無(wú)法合并。
  3. 確保數(shù)據(jù)完整性: 在四次揮手中,分開(kāi)ACKFIN的發(fā)送有助于確保所有數(shù)據(jù)都能成功傳輸完畢。服務(wù)器通過(guò)先發(fā)送ACK確認(rèn)收到客戶端的關(guān)閉請(qǐng)求,并在數(shù)據(jù)發(fā)送完畢后才發(fā)送FIN,可以避免數(shù)據(jù)丟失或中途終止傳輸。

3. 總結(jié)

  • 在三次握手中,SYNACK可以合并到一個(gè)數(shù)據(jù)包中發(fā)送,因?yàn)樗鼈冊(cè)谶壿嬌鲜遣⑿械牟僮?#xff0c;且是在同一時(shí)刻發(fā)送的。
  • 在四次揮手中,ACKFIN不能合并發(fā)送,因?yàn)樗鼈兺ǔ0l(fā)生在不同的時(shí)間點(diǎn),表示不同的狀態(tài)轉(zhuǎn)換。ACK是對(duì)接收方收到FIN的確認(rèn),而FIN是表示發(fā)送方準(zhǔn)備完全關(guān)閉連接,這兩者之間可能存在數(shù)據(jù)傳輸?shù)难舆t,因此分開(kāi)發(fā)送有助于確保傳輸?shù)目煽啃院屯暾浴?/li>
4. close/shutdown與揮手報(bào)文的關(guān)系:

? ? ? ? 當(dāng)recv函數(shù)返回0時(shí)表示收到了對(duì)方的FIN報(bào)文,此時(shí)close()調(diào)用后,會(huì)直接發(fā)出ACK + FIN。

但是用shutdown(sockfd, SHUT_RD)后只會(huì)發(fā)出 ACK,不會(huì)給發(fā)出FIN,還可以接著給對(duì)方發(fā)送數(shù)據(jù)。

七、shutdown函數(shù) 與 FIN 報(bào)文

是的,調(diào)用shutdown()函數(shù)時(shí),根據(jù)調(diào)用參數(shù),TCP連接可以發(fā)送FIN報(bào)文,但這取決于shutdown()的具體使用方式。

1. shutdown()函數(shù)的作用

shutdown()函數(shù)用于部分或完全關(guān)閉一個(gè)已經(jīng)建立的TCP連接。它不同于close()函數(shù),close()不僅會(huì)關(guān)閉連接,還會(huì)釋放文件描述符,而shutdown()允許程序在不關(guān)閉文件描述符的情況下關(guān)閉連接的某一方向(發(fā)送或接收)。

shutdown()函數(shù)的原型:
int shutdown(int sockfd, int how);

其中:

  • sockfd:要關(guān)閉的套接字描述符。
  • how:決定關(guān)閉連接的方式。其值可以是以下之一:
    • SHUT_RD (0):關(guān)閉接收方向,該套接字不再能接收數(shù)據(jù)。
    • SHUT_WR (1):關(guān)閉發(fā)送方向,該套接字不再能發(fā)送數(shù)據(jù),并發(fā)送FIN包。
    • SHUT_RDWR (2):同時(shí)關(guān)閉發(fā)送和接收方向,等同于分別調(diào)用SHUT_RDSHUT_WR。

2. FIN報(bào)文的發(fā)送

當(dāng)shutdown()函數(shù)的how參數(shù)為SHUT_WRSHUT_RDWR時(shí),TCP協(xié)議會(huì)發(fā)送一個(gè)FIN報(bào)文,告訴對(duì)方主機(jī)發(fā)送方已經(jīng)關(guān)閉,數(shù)據(jù)發(fā)送已完成,表明不會(huì)再有更多的數(shù)據(jù)從該端發(fā)送。

詳細(xì)說(shuō)明:
  • SHUT_WR (1):關(guān)閉發(fā)送方向。當(dāng)調(diào)用shutdown(sockfd, SHUT_WR)時(shí),TCP協(xié)議棧會(huì)發(fā)送一個(gè)FIN報(bào)文,表示發(fā)送端不再發(fā)送數(shù)據(jù)。之后,這一端仍然可以接收對(duì)方的數(shù)據(jù),但不能再發(fā)送任何數(shù)據(jù)。

  • SHUT_RDWR (2):同時(shí)關(guān)閉發(fā)送和接收方向。調(diào)用shutdown(sockfd, SHUT_RDWR)時(shí),發(fā)送FIN,且無(wú)法再接收對(duì)方的數(shù)據(jù)。此時(shí),連接相當(dāng)于完全關(guān)閉,但文件描述符不會(huì)被釋放,應(yīng)用程序仍然可以繼續(xù)使用文件描述符做其他操作。

3. shutdown()close()的區(qū)別

  • shutdown()函數(shù)可以只關(guān)閉連接的一部分(如只關(guān)閉發(fā)送而保留接收),而close()會(huì)完全關(guān)閉連接并釋放套接字文件描述符。
  • 在調(diào)用close()時(shí),如果還有數(shù)據(jù)沒(méi)有發(fā)送完,TCP協(xié)議棧會(huì)繼續(xù)嘗試發(fā)送剩余數(shù)據(jù),并最終發(fā)送FIN報(bào)文,完成四次揮手流程。

4. 典型使用場(chǎng)景

  • shutdown(sockfd, SHUT_WR):當(dāng)一個(gè)應(yīng)用程序完成了發(fā)送數(shù)據(jù),但仍然希望接收對(duì)方的數(shù)據(jù)時(shí),通常會(huì)調(diào)用這個(gè)函數(shù)。例如,HTTP協(xié)議中,服務(wù)器發(fā)送響應(yīng)數(shù)據(jù)后可能會(huì)調(diào)用shutdown()來(lái)關(guān)閉發(fā)送方向,但仍然保留接收方向以讀取客戶端的請(qǐng)求。

  • shutdown(sockfd, SHUT_RDWR):用于完全關(guān)閉連接,類似于close(),但不釋放文件描述符。

5. 總結(jié)

當(dāng)調(diào)用shutdown(sockfd, SHUT_WR)shutdown(sockfd, SHUT_RDWR)時(shí),TCP會(huì)發(fā)送FIN報(bào)文,表示發(fā)送方已經(jīng)完成數(shù)據(jù)傳輸,關(guān)閉了發(fā)送方向。


八、bind函數(shù)端口號(hào)的設(shè)置

端口號(hào)在網(wǎng)絡(luò)協(xié)議中起著非常重要的作用,它們被用來(lái)標(biāo)識(shí)不同的服務(wù)或應(yīng)用程序。端口號(hào)可以分為兩大類:知名端口(Well-Known Ports)和動(dòng)態(tài)或私有端口(Dynamic or Private Ports)。這些端口號(hào)由 Internet Assigned Numbers Authority (IANA) 管理,確保網(wǎng)絡(luò)中的每個(gè)服務(wù)都能有唯一的端口標(biāo)識(shí)。

端口號(hào)的分類

  • 知名端口(Well-Known Ports): 范圍為 0 到 1023,通常分配給操作系統(tǒng)和知名的服務(wù)協(xié)議。
  • 注冊(cè)端口(Registered Ports): 范圍為 1024 到 49151,供用戶和應(yīng)用程序使用。
  • 動(dòng)態(tài)或私有端口(Dynamic or Private Ports): 范圍為 49152 到 65535,通常用于臨時(shí)分配給客戶端應(yīng)用。

知名端口(0 - 1023)

這些端口通常由 IANA 分配給常用服務(wù)和協(xié)議,以下是一些常見(jiàn)的協(xié)議和對(duì)應(yīng)的端口號(hào):

端口號(hào)協(xié)議 / 服務(wù)說(shuō)明
20FTP 數(shù)據(jù)傳輸(File Transfer Protocol)用于 FTP 數(shù)據(jù)傳輸
21FTP 控制(File Transfer Protocol)用于 FTP 控制連接
22SSH(Secure Shell)用于安全遠(yuǎn)程登錄
23Telnet用于非加密的遠(yuǎn)程登錄
25SMTP(Simple Mail Transfer Protocol)用于郵件傳輸
53DNS(Domain Name System)用于域名解析
67DHCP 服務(wù)器端(Dynamic Host Configuration Protocol)用于 DHCP 服務(wù)器
68DHCP 客戶端(Dynamic Host Configuration Protocol)用于 DHCP 客戶端
69TFTP(Trivial File Transfer Protocol)用于輕量級(jí)的文件傳輸
80HTTP(HyperText Transfer Protocol)用于 Web 服務(wù)(網(wǎng)頁(yè)瀏覽)
110POP3(Post Office Protocol version 3)用于郵件接收
119NNTP(Network News Transfer Protocol)用于新聞組協(xié)議
123NTP(Network Time Protocol)用于網(wǎng)絡(luò)時(shí)間同步
143IMAP(Internet Message Access Protocol)用于郵件接收(替代 POP3)
161SNMP(Simple Network Management Protocol)用于網(wǎng)絡(luò)設(shè)備管理
194IRC(Internet Relay Chat)用于即時(shí)聊天
443HTTPS(HyperText Transfer Protocol Secure)用于加密的 Web 服務(wù)(HTTPS)
514Syslog用于網(wǎng)絡(luò)設(shè)備和操作系統(tǒng)的日志記錄
520RIP(Routing Information Protocol)用于路由協(xié)議
3389RDP(Remote Desktop Protocol)用于遠(yuǎn)程桌面訪問(wèn)

注冊(cè)端口(1024 - 49151)

這些端口主要由軟件供應(yīng)商和開(kāi)發(fā)者為其應(yīng)用程序所使用。IANA 對(duì)這些端口進(jìn)行注冊(cè),但它們通常不屬于標(biāo)準(zhǔn)化的、固定的協(xié)議。以下是一些常見(jiàn)的服務(wù)和對(duì)應(yīng)的端口號(hào):

端口號(hào)協(xié)議 / 服務(wù)說(shuō)明
1080SOCKS(SOCKS Proxy Protocol)用于代理服務(wù)
1433Microsoft SQL Server用于 Microsoft SQL 數(shù)據(jù)庫(kù)服務(wù)
3306MySQL用于 MySQL 數(shù)據(jù)庫(kù)服務(wù)
3389Microsoft RDP用于遠(yuǎn)程桌面協(xié)議
5432PostgreSQL用于 PostgreSQL 數(shù)據(jù)庫(kù)服務(wù)
5900VNC(Virtual Network Computing)用于虛擬網(wǎng)絡(luò)計(jì)算(遠(yuǎn)程桌面控制)
8080HTTP(Alternative Port)用于 Web 服務(wù)的備用端口(HTTP)
8888HTTP(Alternative Port)用于 Web 服務(wù)的備用端口(HTTP)

動(dòng)態(tài)或私有端口(49152 - 65535)

這些端口通常由操作系統(tǒng)或應(yīng)用程序動(dòng)態(tài)分配給客戶端程序使用,尤其是在進(jìn)行臨時(shí)連接時(shí)。它們不固定分配給任何特定服務(wù)。通常在 TCP/IP 會(huì)話中,客戶端通過(guò)使用這些端口號(hào)連接到遠(yuǎn)程服務(wù)器的服務(wù)端口。

端口號(hào)范圍說(shuō)明
49152 - 65535動(dòng)態(tài)端口范圍,用于臨時(shí)分配給客戶端

端口號(hào)的使用說(shuō)明

  1. 給用戶的端口號(hào):這些端口號(hào)由操作系統(tǒng)和服務(wù)程序?yàn)橛脩籼峁?#xff0c;用來(lái)執(zhí)行應(yīng)用程序或服務(wù)的訪問(wèn)。這些端口號(hào)一般需要符合特定協(xié)議,使用時(shí)需要確保沒(méi)有沖突。

    • 例如:Web 服務(wù)使用 80 或 443 端口,郵件服務(wù)使用 25、110 或 143 端口。
  2. 給協(xié)議的端口號(hào):協(xié)議端口號(hào)由 IANA(Internet Assigned Numbers Authority)分配,用于區(qū)分不同的網(wǎng)絡(luò)協(xié)議和服務(wù)。許多常見(jiàn)的協(xié)議和服務(wù)有固定的端口號(hào),比如 HTTP(80)、FTP(21)、SSH(22)等。

  3. 特定協(xié)議的端口號(hào):許多協(xié)議和應(yīng)用程序會(huì)規(guī)定固定的端口號(hào),用于指定特定的服務(wù)。例如:

    • HTTP/HTTPS 協(xié)議默認(rèn)使用端口 80 和 443。
    • FTP 使用端口 21 進(jìn)行控制連接,端口 20 用于數(shù)據(jù)連接。
    • SMTP 使用端口 25 發(fā)送郵件,POP3 使用端口 110 接收郵件。
  4. 動(dòng)態(tài)分配端口:客戶端與服務(wù)器建立連接時(shí),通常會(huì)使用動(dòng)態(tài)端口(范圍 49152 到 65535)。例如,在 HTTP 請(qǐng)求中,客戶端使用隨機(jī)分配的端口號(hào)連接服務(wù)器的端口 80 或 443。

端口號(hào)的重要性

  • 網(wǎng)絡(luò)服務(wù)和協(xié)議標(biāo)識(shí):端口號(hào)幫助操作系統(tǒng)區(qū)分不同的網(wǎng)絡(luò)協(xié)議和服務(wù),使得同一臺(tái)機(jī)器可以同時(shí)提供多個(gè)不同的服務(wù)。
  • 安全性考慮:某些服務(wù)使用的端口號(hào)可能存在安全漏洞,因此安全防護(hù)設(shè)備(如防火墻)通常會(huì)對(duì)端口號(hào)進(jìn)行過(guò)濾,阻止不安全的端口。
  • 端口掃描:攻擊者通常通過(guò)端口掃描來(lái)查找開(kāi)放的端口和運(yùn)行的服務(wù),進(jìn)而尋找潛在的攻擊入口。

總結(jié)

  • 端口號(hào)是網(wǎng)絡(luò)通信中的重要組成部分,允許不同的服務(wù)和應(yīng)用程序在同一臺(tái)機(jī)器上并行運(yùn)行。
  • 端口號(hào)分為知名端口、注冊(cè)端口和動(dòng)態(tài)端口,分別用于系統(tǒng)服務(wù)、應(yīng)用程序服務(wù)和臨時(shí)連接。
  • 各種協(xié)議和服務(wù)使用不同的端口號(hào),IANA 負(fù)責(zé)管理這些端口號(hào)的分配。

九、TCP系統(tǒng)調(diào)用函數(shù)的 -- 阻塞性

在 TCP 編程中,某些網(wǎng)絡(luò)操作可能會(huì)阻塞,即函數(shù)在沒(méi)有完成操作之前會(huì)等待特定條件的發(fā)生。這些函數(shù)通常用于執(zhí)行需要等待數(shù)據(jù)到達(dá)、連接建立、或者連接關(guān)閉等操作的任務(wù)。阻塞行為通常與網(wǎng)絡(luò)狀態(tài)、系統(tǒng)資源、以及協(xié)議本身的特性相關(guān)。

以下是一些常見(jiàn)的會(huì)阻塞的 TCP 相關(guān)函數(shù),以及它們?yōu)槭裁磿?huì)阻塞:

1. accept()

  • 阻塞原因: accept() 用于在服務(wù)器端接受一個(gè)已經(jīng)完成三次握手的連接請(qǐng)求。如果沒(méi)有等待的連接,它會(huì)阻塞,直到有客戶端發(fā)起連接請(qǐng)求。
  • 何時(shí)阻塞: 當(dāng)沒(méi)有客戶端連接請(qǐng)求到達(dá)時(shí),accept() 會(huì)阻塞,直到有連接請(qǐng)求到來(lái)。
  • 代碼示例:
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8080);bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    listen(sockfd, 5);int client_sock = accept(sockfd, NULL, NULL);  // 阻塞直到有連接到來(lái)
    if (client_sock == -1) {perror("accept");
    }
    

2. recv() / recvfrom() / read()

  • 阻塞原因: 這些函數(shù)用于從套接字中接收數(shù)據(jù)。如果沒(méi)有數(shù)據(jù)可讀,它們會(huì)阻塞,直到有數(shù)據(jù)可用。recv() 在默認(rèn)情況下會(huì)阻塞,直到接收到至少一個(gè)字節(jié)的數(shù)據(jù)。
  • 何時(shí)阻塞: 如果緩沖區(qū)中沒(méi)有數(shù)據(jù)(例如,客戶端沒(méi)有發(fā)送數(shù)據(jù)),則會(huì)阻塞等待數(shù)據(jù)的到來(lái)。
  • 代碼示例:
    char buffer[1024];
    int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);  // 阻塞直到數(shù)據(jù)到達(dá)
    if (bytes_received == -1) {perror("recv");
    }
    

3. send() / sendto() / write()

  • 阻塞原因: 如果發(fā)送緩沖區(qū)已滿,send()write() 可能會(huì)阻塞,直到發(fā)送緩沖區(qū)有足夠的空間來(lái)存儲(chǔ)數(shù)據(jù)。特別是在網(wǎng)絡(luò)擁堵或者接收方的速度跟不上發(fā)送速度時(shí),發(fā)送函數(shù)可能會(huì)阻塞。
  • 何時(shí)阻塞: 當(dāng)套接字處于阻塞模式且發(fā)送緩沖區(qū)已滿時(shí),send()write() 會(huì)阻塞,直到緩沖區(qū)有空間。
  • 代碼示例:
    const char *msg = "Hello, Client!";
    int bytes_sent = send(client_sock, msg, strlen(msg), 0);  // 阻塞直到數(shù)據(jù)被發(fā)送
    if (bytes_sent == -1) {perror("send");
    }
    

4. connect()

  • 阻塞原因: connect() 用于客戶端與服務(wù)器建立 TCP 連接。如果服務(wù)器沒(méi)有響應(yīng)或不可達(dá),connect() 會(huì)阻塞,直到連接成功建立或者超時(shí)。
  • 何時(shí)阻塞: 如果沒(méi)有可用的遠(yuǎn)程服務(wù)器響應(yīng)或服務(wù)器未準(zhǔn)備好接收連接,connect() 會(huì)阻塞,直到連接成功或失敗。
  • 代碼示例:
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.100");int result = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));  // 阻塞直到連接成功
    if (result == -1) {perror("connect");
    }
    

5. listen()

  • 阻塞原因: listen() 是在 TCP 服務(wù)器端調(diào)用的,用于將套接字設(shè)為監(jiān)聽(tīng)模式,等待客戶端的連接請(qǐng)求。它本身不會(huì)阻塞,但會(huì)準(zhǔn)備好接收連接。在后續(xù)調(diào)用 accept() 時(shí),才會(huì)阻塞。
  • 何時(shí)阻塞: listen() 本身不會(huì)阻塞,但它為 accept() 阻塞操作做好準(zhǔn)備。
  • 代碼示例:
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8080);bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    listen(sockfd, 5);  // 為后續(xù)的accept準(zhǔn)備
    

6. shutdown()

  • 阻塞原因: shutdown() 可以關(guān)閉套接字的某些操作(如讀、寫(xiě)),并等待數(shù)據(jù)的完全傳輸或清理。如果你調(diào)用 shutdown() 來(lái)關(guān)閉寫(xiě)操作,它可能會(huì)阻塞,直到 TCP 將所有待發(fā)送的數(shù)據(jù)發(fā)送完畢。
  • 何時(shí)阻塞: 如果套接字有未發(fā)送的數(shù)據(jù)需要傳輸,shutdown() 會(huì)阻塞,直到數(shù)據(jù)傳輸完畢。
  • 代碼示例:
    int result = shutdown(sockfd, SHUT_WR);  // 關(guān)閉寫(xiě)端,阻塞直到所有數(shù)據(jù)被發(fā)送
    if (result == -1) {perror("shutdown");
    }
    

為什么阻塞?

TCP 是一種面向連接、可靠的數(shù)據(jù)傳輸協(xié)議,它保證數(shù)據(jù)的可靠交付,確保所有數(shù)據(jù)包按照順序到達(dá)目的地,并通過(guò)流量控制、擁塞控制等機(jī)制避免網(wǎng)絡(luò)過(guò)載。為確保這些特性,某些操作需要等待特定事件的發(fā)生,導(dǎo)致阻塞:

  1. 等待數(shù)據(jù)到達(dá):如在調(diào)用 recv() 時(shí),系統(tǒng)必須等待數(shù)據(jù)從遠(yuǎn)程主機(jī)傳輸?shù)奖镜亍?/li>
  2. 等待連接建立:如在 accept()connect() 中,系統(tǒng)必須等待對(duì)方準(zhǔn)備好接收或發(fā)起連接。
  3. 緩沖區(qū)未滿:如在發(fā)送數(shù)據(jù)時(shí),如果發(fā)送緩沖區(qū)已滿,系統(tǒng)會(huì)等待緩沖區(qū)騰出空間來(lái)進(jìn)行數(shù)據(jù)傳輸。

如何避免阻塞?

  1. 非阻塞模式:可以將套接字設(shè)置為非阻塞模式,在這種模式下,調(diào)用函數(shù)不會(huì)阻塞。如果操作無(wú)法立即完成,它會(huì)返回 EAGAINEWOULDBLOCK 錯(cuò)誤,應(yīng)用程序可以做其他事情或稍后再試。

    • 代碼示例
      int flags = fcntl(sockfd, F_GETFL, 0);
      fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);  // 設(shè)置套接字為非阻塞模式
      
  2. 超時(shí)設(shè)置:對(duì)于 connect()、recv() 等函數(shù),可以設(shè)置超時(shí)時(shí)間,避免長(zhǎng)時(shí)間阻塞。比如可以使用 select()poll() 來(lái)實(shí)現(xiàn)超時(shí)檢測(cè)。

  3. 多線程或異步 I/O:在多線程程序中,阻塞的操作可以放在單獨(dú)的線程中執(zhí)行,主線程繼續(xù)進(jìn)行其他任務(wù)。使用異步 I/O 也是一種避免阻塞的方式,特別是在高性能網(wǎng)絡(luò)應(yīng)用中。

總結(jié)

TCP 編程中,許多函數(shù)會(huì)阻塞,尤其是與連接、數(shù)據(jù)接收和發(fā)送相關(guān)的操作。accept()、recv()send()connect() 等函數(shù)在沒(méi)有數(shù)據(jù)或連接不可用時(shí)會(huì)阻塞,直到特定條件滿足。為了避免阻塞,開(kāi)發(fā)者可以使用非阻塞模式、超時(shí)機(jī)制或多線程來(lái)處理阻塞操作。

十、調(diào)用返回失敗的情況分析

在 TCP 編程中,許多常用的函數(shù)會(huì)返回失敗的情況,尤其是在網(wǎng)絡(luò)條件不理想或系統(tǒng)資源不足時(shí)。了解每個(gè)函數(shù)返回失敗時(shí)的具體錯(cuò)誤原因非常重要,這有助于調(diào)試和錯(cuò)誤處理。以下是對(duì)常見(jiàn)的 TCP 函數(shù)返回失敗時(shí)的錯(cuò)誤情況的詳細(xì)說(shuō)明:

1. accept()

accept() 用于接受客戶端連接。當(dāng)調(diào)用失敗時(shí),返回值為 -1,并設(shè)置 errno 以指示具體的錯(cuò)誤原因。

  • EINVAL: 如果套接字未正確綁定(如 bind() 未調(diào)用)或者套接字類型不支持 accept()(例如,UDP 套接字),則返回此錯(cuò)誤。
  • ECONNABORTED: 如果先前的連接被中止,accept() 返回此錯(cuò)誤。
  • EFAULT: 傳遞給 accept() 的地址指針無(wú)效。
  • EINTR: 系統(tǒng)調(diào)用被信號(hào)中斷。accept() 被中斷時(shí)返回該錯(cuò)誤。

2. recv() / recvfrom() / read()

這些函數(shù)用于從 TCP 套接字接收數(shù)據(jù)。當(dāng)返回 -1 時(shí),表示出現(xiàn)錯(cuò)誤,errno 將設(shè)置為相應(yīng)的錯(cuò)誤代碼。0時(shí)表示對(duì)方已經(jīng)斷開(kāi)連接。

  • EAGAINEWOULDBLOCK: 套接字被設(shè)置為非阻塞模式,且沒(méi)有數(shù)據(jù)可用時(shí)返回該錯(cuò)誤。
  • EBADF: 套接字無(wú)效,可能是已經(jīng)關(guān)閉或未正確初始化。
  • EINTR: 系統(tǒng)調(diào)用被信號(hào)中斷。操作在信號(hào)處理程序執(zhí)行后被中斷,導(dǎo)致 recv() 返回失敗。
  • ENOTCONN: 套接字未連接,調(diào)用 recv() 時(shí),TCP 套接字未完成連接。
  • ECONNRESET: 對(duì)方主機(jī)強(qiáng)制關(guān)閉連接,TCP 連接被重置,導(dǎo)致接收操作失敗。
  • ENOTSOCK: 目標(biāo)文件描述符不是一個(gè)套接字。
  • EFAULT: 提供的緩沖區(qū)地址無(wú)效。

3. send() / sendto() / write()

這些函數(shù)用于向 TCP 套接字發(fā)送數(shù)據(jù),失敗時(shí)返回 -1,并設(shè)置 errno

  • EAGAINEWOULDBLOCK: 套接字被設(shè)置為非阻塞模式,且發(fā)送緩沖區(qū)已滿,無(wú)法繼續(xù)發(fā)送數(shù)據(jù)。
  • EBADF: 套接字無(wú)效,可能是已經(jīng)關(guān)閉或未正確初始化。
  • EINTR: 系統(tǒng)調(diào)用被信號(hào)中斷,導(dǎo)致 send() 被中斷。
  • ENOTCONN: 套接字未連接時(shí)調(diào)用 send() 會(huì)失敗。
  • ECONNRESET: 對(duì)方主機(jī)強(qiáng)制關(guān)閉連接,導(dǎo)致連接重置,發(fā)送操作失敗。
  • ENOTSOCK: 目標(biāo)文件描述符不是一個(gè)套接字。
  • EPIPE: 當(dāng)發(fā)送數(shù)據(jù)到一個(gè)已經(jīng)關(guān)閉的連接時(shí)返回此錯(cuò)誤,表示對(duì)方已經(jīng)關(guān)閉了連接,寫(xiě)入操作失敗。

4. connect()

connect() 用于客戶端建立與服務(wù)器的連接。如果返回值是 -1,則表示連接失敗,errno 會(huì)被設(shè)置為特定錯(cuò)誤值。

  • ECONNREFUSED: 目標(biāo)服務(wù)器拒絕連接。通常是目標(biāo)服務(wù)器未啟動(dòng)或未監(jiān)聽(tīng)指定的端口。
  • ETIMEDOUT: 連接請(qǐng)求超時(shí)。在指定時(shí)間內(nèi)沒(méi)有完成連接。
  • EINPROGRESS: 如果套接字是非阻塞模式且連接正在進(jìn)行中,這個(gè)錯(cuò)誤會(huì)發(fā)生。不是錯(cuò)誤,表示連接正在進(jìn)行。
  • EAGAIN: 套接字設(shè)置為非阻塞模式時(shí),連接嘗試會(huì)立即返回 EAGAIN 錯(cuò)誤,表示無(wú)法立即連接。
  • EADDRINUSE: 本地地址已在使用中,無(wú)法為新連接分配。
  • ENETUNREACH: 網(wǎng)絡(luò)不可達(dá),可能是由于路由或網(wǎng)絡(luò)配置問(wèn)題。
  • EHOSTUNREACH: 主機(jī)不可達(dá),通常由于目標(biāo)主機(jī)未開(kāi)機(jī)或網(wǎng)絡(luò)不可達(dá)。
  • ENOTSOCK: 目標(biāo)文件描述符不是一個(gè)套接字。

5. listen()

listen() 用于在服務(wù)器端啟動(dòng)監(jiān)聽(tīng)。失敗時(shí)返回 -1,并設(shè)置 errno。

  • EADDRINUSE: 如果指定的端口已被其他應(yīng)用程序占用,listen() 會(huì)失敗并返回此錯(cuò)誤。
  • EINVAL: 如果套接字不是流式套接字(例如 UDP 套接字),則會(huì)發(fā)生此錯(cuò)誤。
  • ENOTSOCK: 傳入的文件描述符不是套接字。

6. shutdown()

shutdown() 用于關(guān)閉套接字的讀寫(xiě)操作。如果返回 -1,則表示操作失敗,errno 被設(shè)置為錯(cuò)誤值。

  • EBADF: 套接字無(wú)效,可能是已經(jīng)關(guān)閉或者未正確初始化。
  • EINTR: 系統(tǒng)調(diào)用被信號(hào)中斷,shutdown() 被中斷。
  • ENOTSOCK: 目標(biāo)文件描述符不是一個(gè)套接字。

7. fcntl()

fcntl() 用于獲取或設(shè)置套接字的屬性,如設(shè)置非阻塞模式等。如果返回 -1,表示操作失敗,errno 被設(shè)置為錯(cuò)誤碼。

  • EBADF: 套接字無(wú)效,可能是已經(jīng)關(guān)閉或未正確初始化。
  • EINVAL: 無(wú)效的命令或參數(shù)。
  • ENOTTY: 非法的文件描述符類型,不支持該操作。

8. bind()

bind() 用于將套接字與本地地址(IP 和端口)綁定。如果返回 -1,表示綁定失敗,errno 被設(shè)置為特定錯(cuò)誤碼。

  • EADDRINUSE: 地址已被使用,無(wú)法綁定。
  • EADDRNOTAVAIL: 本地地址不可用,可能由于沒(méi)有該網(wǎng)絡(luò)接口或地址配置問(wèn)題。
  • EBADF: 套接字無(wú)效。
  • EINVAL: 無(wú)效的套接字類型,通常是由于套接字類型和協(xié)議不匹配。

總結(jié)

了解這些 TCP 函數(shù)返回失敗時(shí)的錯(cuò)誤原因非常重要,有助于調(diào)試和錯(cuò)誤處理。一般情況下,當(dāng)函數(shù)返回 -1 時(shí),errno 會(huì)提供失敗的詳細(xì)信息。開(kāi)發(fā)者應(yīng)該根據(jù)不同的錯(cuò)誤代碼進(jìn)行適當(dāng)?shù)腻e(cuò)誤處理,例如通過(guò)重試、記錄日志、關(guān)閉套接字等方式來(lái)恢復(fù)網(wǎng)絡(luò)操作,確保程序的健壯性。

十一、recv返回0時(shí)的詳細(xì)說(shuō)明

在 TCP 編程中,recv() 函數(shù)的返回值為 0 是一個(gè)非常重要的情況,它表示 對(duì)方關(guān)閉了連接。這個(gè)情況常常被用來(lái)判斷連接是否已經(jīng)正常關(guān)閉。

  • recv() 返回 0:
    • 當(dāng)調(diào)用 recv() 時(shí),如果返回值是 0,這并不表示錯(cuò)誤,而是表示連接已經(jīng)被對(duì)方關(guān)閉(也就是對(duì)方發(fā)送了一個(gè) TCP FIN 包 來(lái)終止連接),并且沒(méi)有更多的數(shù)據(jù)可接收。
    • 這個(gè)返回值表示對(duì)方已經(jīng)優(yōu)雅地關(guān)閉了連接,并且沒(méi)有數(shù)據(jù)需要讀取。

具體情況:

  1. TCP 連接正常關(guān)閉

    • 在正常的 TCP 連接關(guān)閉過(guò)程中,通信雙方會(huì)經(jīng)過(guò)四次揮手(Four-way Handshake),具體來(lái)說(shuō):
      • 一方(通常是主動(dòng)關(guān)閉的那方)發(fā)送一個(gè) FIN 包,表示希望關(guān)閉連接。
      • 接收方確認(rèn)收到 FIN 包,并發(fā)送一個(gè) ACK 包
      • 接收方也會(huì)發(fā)送自己的 FIN 包,表示自己也準(zhǔn)備關(guān)閉連接。
      • 主動(dòng)關(guān)閉的一方確認(rèn)收到接收方的 FIN 包,完成連接關(guān)閉過(guò)程。

    在這個(gè)過(guò)程中,當(dāng) recv() 讀取到接收到的 FIN 包 后,表示對(duì)方已經(jīng)關(guān)閉了連接,函數(shù)返回 0。

  2. recv() 返回 0 的例子: 下面是一個(gè)簡(jiǎn)單的代碼示例,演示如何使用 recv() 判斷對(duì)方關(guān)閉連接:

    char buffer[1024];
    int bytes_received;// 假設(shè)客戶端已經(jīng)連接到服務(wù)器
    bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);if (bytes_received == 0) {printf("The remote side has closed the connection gracefully.\n");// 對(duì)方關(guān)閉了連接,進(jìn)行相應(yīng)的清理工作close(client_sock);
    } else if (bytes_received < 0) {perror("recv failed");
    } else {// 處理接收到的數(shù)據(jù)printf("Received %d bytes: %s\n", bytes_received, buffer);
    }
    

    在上面的代碼中:

    • 如果 recv() 返回 0,表示對(duì)方已經(jīng)正常關(guān)閉了連接。此時(shí),通常需要關(guān)閉自己的套接字并清理相關(guān)資源。
    • 如果 recv() 返回負(fù)值(< 0),表示發(fā)生了錯(cuò)誤,可以通過(guò) errno 獲取錯(cuò)誤原因。

其他返回情況:

  • 返回負(fù)值(< 0

    • 如果 recv() 返回一個(gè)負(fù)值,通常表示發(fā)生了錯(cuò)誤。常見(jiàn)的錯(cuò)誤包括:
      • EINTR: 系統(tǒng)調(diào)用被信號(hào)中斷。
      • EAGAINEWOULDBLOCK: 套接字處于非阻塞模式,且沒(méi)有數(shù)據(jù)可讀取。
      • ECONNRESET: 連接被對(duì)方重置(例如,遠(yuǎn)程主機(jī)強(qiáng)制關(guān)閉了連接)。
  • 返回大于零的正數(shù)

    • 如果返回一個(gè)大于 0 的值,表示成功接收到數(shù)據(jù),值表示接收到的數(shù)據(jù)字節(jié)數(shù)。開(kāi)發(fā)者可以處理這些數(shù)據(jù)。

為什么 recv() 返回 0 代表對(duì)方關(guān)閉了連接?

這是因?yàn)樵?TCP 協(xié)議中,連接關(guān)閉是通過(guò)發(fā)送 FIN 包來(lái)實(shí)現(xiàn)的。此時(shí),連接的另一端會(huì)通知接收端自己已經(jīng)沒(méi)有數(shù)據(jù)發(fā)送,并且希望關(guān)閉連接。當(dāng)接收端收到這個(gè) FIN 包后,recv() 返回 0,表示沒(méi)有更多的數(shù)據(jù)可讀。

  • TCP FIN 包:當(dāng)連接的某一方發(fā)送 FIN 包時(shí),它表示已經(jīng)發(fā)送完所有數(shù)據(jù)并且希望關(guān)閉連接。接收方接收到 FIN 包后,會(huì)回復(fù)一個(gè) ACK 包,表示已經(jīng)收到關(guān)閉請(qǐng)求。此時(shí),接收方的接收緩沖區(qū)為空,不再有數(shù)據(jù)傳輸,recv() 將返回 0,表示對(duì)方已關(guān)閉連接。

總結(jié):

  • recv() 返回 0 表示對(duì)方已經(jīng)關(guān)閉了連接,通常是正常的連接關(guān)閉過(guò)程。
  • 該返回值是用于 優(yōu)雅地關(guān)閉連接 的指示,表明沒(méi)有更多數(shù)據(jù)可讀,開(kāi)發(fā)者可以清理資源并關(guān)閉自己的套接字。
  • 當(dāng)遇到 0 時(shí),通常需要進(jìn)行關(guān)閉套接字、清理資源等操作。

0voice · GitHub

http://www.risenshineclean.com/news/41172.html

相關(guān)文章:

  • 哪里做企業(yè)網(wǎng)站上海seo服務(wù)
  • 天津市做網(wǎng)站的公司查淘寶關(guān)鍵詞排名軟件
  • 香港一卡通app下載鄭州seo外包顧問(wèn)熱狗
  • 去哪里找做網(wǎng)站的百度競(jìng)價(jià)ocpc投放策略
  • 設(shè)計(jì)導(dǎo)航網(wǎng)站 左側(cè)菜單欄網(wǎng)絡(luò)營(yíng)銷課程論文
  • 農(nóng)產(chǎn)品網(wǎng)站如何做地推網(wǎng)易企業(yè)郵箱
  • 網(wǎng)站開(kāi)發(fā)語(yǔ)言 排行榜關(guān)鍵詞seo公司真實(shí)推薦
  • 醫(yī)藥網(wǎng)站建設(shè)客戶的需求廈門(mén)關(guān)鍵詞排名推廣
  • 西部數(shù)碼 空間做2個(gè)網(wǎng)站什么是新媒體運(yùn)營(yíng)
  • 自適應(yīng)手機(jī)網(wǎng)站 css愛(ài)站網(wǎng)是什么
  • 博物館文化網(wǎng)站建設(shè)青島排名推廣
  • 投訴做網(wǎng)站的電話服務(wù)器域名查詢
  • 室內(nèi)設(shè)計(jì)網(wǎng)站大全網(wǎng)seo新手教程
  • 響應(yīng)式網(wǎng)站弊端互聯(lián)網(wǎng)公司
  • 池州市住房和城鄉(xiāng)建設(shè)委員會(huì)網(wǎng)站百度推廣聯(lián)系人
  • 山東安康建設(shè)項(xiàng)目管理有限公司網(wǎng)站北京谷歌優(yōu)化
  • 大宗商品現(xiàn)貨交易app天津seo優(yōu)化公司哪家好
  • 無(wú)錫網(wǎng)站優(yōu)化價(jià)格福鼎網(wǎng)站優(yōu)化公司
  • 廈門(mén)網(wǎng)站建設(shè)xm37網(wǎng)站的營(yíng)銷推廣
  • 靜態(tài)網(wǎng)站建設(shè)課程設(shè)計(jì)百度一下生活更好
  • 網(wǎng)站404怎么做搜索排名提升
  • 網(wǎng)站怎么做直通車鄭州厲害的seo優(yōu)化顧問(wèn)
  • 做攻略的網(wǎng)站好企業(yè)中層管理人員培訓(xùn)課程
  • 廣州網(wǎng)站排名優(yōu)化費(fèi)用招聘網(wǎng)絡(luò)營(yíng)銷推廣人員
  • wordpress各部分功能百度seo關(guān)鍵詞優(yōu)化費(fèi)用
  • 自己做網(wǎng)站自己做推廣教程視頻教程網(wǎng)絡(luò)運(yùn)營(yíng)培訓(xùn)
  • 水果b2b電商平臺(tái)有哪些seo技術(shù)員
  • 蘇州營(yíng)銷型網(wǎng)站南寧企業(yè)官網(wǎng)seo
  • 上海做網(wǎng)站比較有名的公司湖南疫情最新消息今天
  • 我網(wǎng)站關(guān)鍵詞太多公司做網(wǎng)站推廣