国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

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

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

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

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

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

1. socket()

  • 函數(shù)原型: int socket(int domain, int type, int protocol);
  • 功能: 創(chuàng)建一個(gè)新的套接字(socket),它是與網(wǎng)絡(luò)通信相關(guān)的基本對象。
  • 參數(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)的長度。
  • 返回值: 成功返回 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);
  • 功能: 接受來自客戶端的連接請求,并返回一個(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ù)器的連接請求。
  • 參數(shù):
    • sockfd: 客戶端套接字描述符。
    • addr: 目標(biāo)服務(wù)器的地址信息。
    • addrlen: 地址結(jié)構(gòu)的長度。
  • 返回值: 成功返回 0,失敗返回 -1

6. send()

  • 函數(shù)原型: ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • 功能: 通過套接字發(fā)送數(shù)據(jù)。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • buf: 數(shù)據(jù)緩沖區(qū)。
    • len: 發(fā)送數(shù)據(jù)的長度。
    • 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ù)的最大長度。
    • 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)閉套接字的讀、寫或者雙向通信。
  • 參數(shù):
    • sockfd: 套接字描述符。
    • how: 控制關(guān)閉的方式,常用值為:
      • SHUT_RD: 關(guān)閉讀取(不能再讀取數(shù)據(jù))。
      • SHUT_WR: 關(guān)閉寫入(不能再發(fā)送數(shù)據(jù))。
      • SHUT_RDWR: 同時(shí)關(guān)閉讀寫。
  • 返回值: 成功返回 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)值的長度。
  • 返回值: 成功返回 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)聽多個(gè)套接字,并在某些事件(如可讀、可寫等)發(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é):

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

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

下面將詳細(xì)解釋在socket()、listen()、accept()等函數(shù)調(diào)用過程中,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)體(具體來說,如果是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)行通信的文件描述符。通過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ài),這時(shí)在與該監(jiān)聽套接字對應(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ǔ)連接請求的狀態(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)行綁定。

    換句話說,client_fd與新連接的struct tcp_sock關(guān)聯(lián)起來,使得通過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)用程序通過client_fd來處理這個(gè)TCP連接。


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

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

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

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

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

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

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

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

  • 雖然應(yīng)用程序可以指定backlog的大小,但內(nèi)核實(shí)際上會(huì)對該值進(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ì)列的長度最大為10,即最多允許10個(gè)已經(jīng)完成三次握手的連接排隊(duì)等待accept()。
  • 如果第11個(gè)連接嘗試建立,服務(wù)器將返回TCP RST包,拒絕該連接。

5. 總結(jié)

  • backlog參數(shù)用于指定服務(wù)器上全連接隊(duì)列的最大長度,即等待應(yīng)用層accept()調(diào)用的已建立連接數(shù)的最大值。
  • 過小的backlog值會(huì)導(dǎo)致高并發(fā)時(shí)連接被拒絕,而過大的值會(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 請求時(shí),服務(wù)器將這個(gè)連接請求放入 半連接隊(duì)列(也稱為 SYN 隊(duì)列)。此隊(duì)列用于存儲(chǔ)尚未完成三次握手的連接。
  • 一旦握手完成并且服務(wù)器準(zhǔn)備好接受數(shù)據(jù),連接就會(huì)移入 全連接隊(duì)列(Accept Queue)。

2. tcp_max_syn_backlog 參數(shù):

  • 作用: 控制半連接隊(duì)列的最大長度,即可以緩存的未完成三次握手的連接數(shù)。
  • 默認(rèn)值: 在大多數(shù) Linux 系統(tǒng)中,默認(rèn)值通常為 128,意味著最多可以緩存 128 個(gè)尚未完成三次握手的連接。
  • 調(diào)整: 可以通過修改 /proc/sys/net/ipv4/tcp_max_syn_backlog 文件來調(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 請求到達(dá),內(nèi)核會(huì)丟棄這些連接請求,通常客戶端會(huì)收到一個(gè) TCP RST(重置) 消息,或者如果客戶端重試,可能會(huì)延遲連接。
  • 為了避免此情況,通常需要根據(jù)實(shí)際的網(wǎng)絡(luò)負(fù)載來調(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ì)列的長度由不同的參數(shù)控制,而服務(wù)器需要根據(jù)實(shí)際的負(fù)載情況合理配置這些參數(shù),以確保高并發(fā)時(shí)的連接性能和穩(wěn)定性。

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

send()函數(shù)的第四個(gè)參數(shù)是**flags**,用于指定發(fā)送操作的行為。通過設(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ù)長度。
  • 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)的特殊場景。
  3. MSG_NOSIGNAL

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

    • 僅適用于基于某些協(xié)議(如UDP)的發(fā)送,表示希望確認(rèn)對端的存在,通常用于實(shí)現(xiàn)鏈路層的鄰居確認(rèn)。
    • 僅用于某些低層協(xié)議的特定場景,在常規(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ò)通信的場景。
    • 在大多數(shù)普通應(yīng)用場景中很少使用。
  6. MSG_EOR(End of Record):

    • 僅用于某些基于記錄的協(xié)議,表示本次send()調(diào)用發(fā)送的數(shù)據(jù)是一個(gè)邏輯記錄的結(jié)束。
    • 對于常見的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ò)開銷的場景。

示例:使用 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ā)送操作的行為。常見的標(biāo)志包括MSG_DONTWAIT(非阻塞發(fā)送)、MSG_OOB(發(fā)送緊急數(shù)據(jù))、MSG_NOSIGNAL(避免SIGPIPE信號(hào))等。你可以根據(jù)具體應(yīng)用場景使用不同的標(biāo)志來改變send()的默認(rèn)行為。


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

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

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

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

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

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

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

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

  • 第一次揮手:客戶端發(fā)送一個(gè)FIN包,表示它要關(guān)閉連接(數(shù)據(jù)傳輸結(jié)束)。
  • 第二次揮手:服務(wù)器收到后,回復(fù)一個(gè)ACK,表示收到了客戶端的FIN請求,但服務(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): 在四次揮手過程中,TCP協(xié)議允許連接進(jìn)入半關(guān)閉狀態(tài),即:

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

    在這個(gè)階段,服務(wù)器回復(fù)的ACK只是表明收到了客戶端的FIN,但服務(wù)器還沒有準(zhǔn)備好關(guān)閉連接,因?yàn)榭赡苋匀挥袛?shù)據(jù)需要發(fā)送。如果此時(shí)合并ACKFIN,就意味著服務(wù)器已經(jīng)準(zhǔn)備好關(guān)閉連接了,但實(shí)際上它可能還沒有完成數(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來關(guān)閉連接。這兩個(gè)操作通常在不同的時(shí)間點(diǎn)發(fā)生,無法合并。
  3. 確保數(shù)據(jù)完整性: 在四次揮手中,分開ACKFIN的發(fā)送有助于確保所有數(shù)據(jù)都能成功傳輸完畢。服務(wù)器通過先發(fā)送ACK確認(rèn)收到客戶端的關(guān)閉請求,并在數(shù)據(jù)發(fā)送完畢后才發(fā)送FIN,可以避免數(shù)據(jù)丟失或中途終止傳輸。

3. 總結(jié)

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

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

但是用shutdown(sockfd, SHUT_RD)后只會(huì)發(fā)出 ACK,不會(huì)給發(fā)出FIN,還可以接著給對方發(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)文,告訴對方主機(jī)發(fā)送方已經(jīng)關(guān)閉,數(shù)據(jù)發(fā)送已完成,表明不會(huì)再有更多的數(shù)據(jù)從該端發(fā)送。

詳細(xì)說明:
  • 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ù)。之后,這一端仍然可以接收對方的數(shù)據(jù),但不能再發(fā)送任何數(shù)據(jù)。

  • SHUT_RDWR (2):同時(shí)關(guān)閉發(fā)送和接收方向。調(diào)用shutdown(sockfd, SHUT_RDWR)時(shí),發(fā)送FIN,且無法再接收對方的數(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ù)沒有發(fā)送完,TCP協(xié)議棧會(huì)繼續(xù)嘗試發(fā)送剩余數(shù)據(jù),并最終發(fā)送FIN報(bào)文,完成四次揮手流程。

4. 典型使用場景

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

  • 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é)議中起著非常重要的作用,它們被用來標(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é)議。
  • 注冊端口(Registered Ports): 范圍為 1024 到 49151,供用戶和應(yīng)用程序使用。
  • 動(dòng)態(tài)或私有端口(Dynamic or Private Ports): 范圍為 49152 到 65535,通常用于臨時(shí)分配給客戶端應(yīng)用。

知名端口(0 - 1023)

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

端口號(hào)協(xié)議 / 服務(wù)說明
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)頁瀏覽)
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)程桌面訪問

注冊端口(1024 - 49151)

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

端口號(hào)協(xié)議 / 服務(wù)說明
1080SOCKS(SOCKS Proxy Protocol)用于代理服務(wù)
1433Microsoft SQL Server用于 Microsoft SQL 數(shù)據(jù)庫服務(wù)
3306MySQL用于 MySQL 數(shù)據(jù)庫服務(wù)
3389Microsoft RDP用于遠(yuǎn)程桌面協(xié)議
5432PostgreSQL用于 PostgreSQL 數(shù)據(jù)庫服務(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ì)話中,客戶端通過使用這些端口號(hào)連接到遠(yuǎn)程服務(wù)器的服務(wù)端口。

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

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

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

    • 例如: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ù)。許多常見的協(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 請求中,客戶端使用隨機(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ì)對端口號(hào)進(jìn)行過濾,阻止不安全的端口。
  • 端口掃描:攻擊者通常通過端口掃描來查找開放的端口和運(yùn)行的服務(wù),進(jìn)而尋找潛在的攻擊入口。

總結(jié)

  • 端口號(hào)是網(wǎng)絡(luò)通信中的重要組成部分,允許不同的服務(wù)和應(yīng)用程序在同一臺(tái)機(jī)器上并行運(yùn)行。
  • 端口號(hào)分為知名端口、注冊端口和動(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ù)在沒有完成操作之前會(huì)等待特定條件的發(fā)生。這些函數(shù)通常用于執(zhí)行需要等待數(shù)據(jù)到達(dá)、連接建立、或者連接關(guān)閉等操作的任務(wù)。阻塞行為通常與網(wǎng)絡(luò)狀態(tài)、系統(tǒng)資源、以及協(xié)議本身的特性相關(guān)。

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

1. accept()

  • 阻塞原因: accept() 用于在服務(wù)器端接受一個(gè)已經(jīng)完成三次握手的連接請求。如果沒有等待的連接,它會(huì)阻塞,直到有客戶端發(fā)起連接請求。
  • 何時(shí)阻塞: 當(dāng)沒有客戶端連接請求到達(dá)時(shí),accept() 會(huì)阻塞,直到有連接請求到來。
  • 代碼示例:
    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);  // 阻塞直到有連接到來
    if (client_sock == -1) {perror("accept");
    }
    

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

  • 阻塞原因: 這些函數(shù)用于從套接字中接收數(shù)據(jù)。如果沒有數(shù)據(jù)可讀,它們會(huì)阻塞,直到有數(shù)據(jù)可用。recv() 在默認(rèn)情況下會(huì)阻塞,直到接收到至少一個(gè)字節(jié)的數(shù)據(jù)。
  • 何時(shí)阻塞: 如果緩沖區(qū)中沒有數(shù)據(jù)(例如,客戶端沒有發(fā)送數(shù)據(jù)),則會(huì)阻塞等待數(shù)據(jù)的到來。
  • 代碼示例:
    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ū)有足夠的空間來存儲(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ù)器沒有響應(yīng)或不可達(dá),connect() 會(huì)阻塞,直到連接成功建立或者超時(shí)。
  • 何時(shí)阻塞: 如果沒有可用的遠(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)聽模式,等待客戶端的連接請求。它本身不會(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)閉套接字的某些操作(如讀、寫),并等待數(shù)據(jù)的完全傳輸或清理。如果你調(diào)用 shutdown() 來關(guān)閉寫操作,它可能會(huì)阻塞,直到 TCP 將所有待發(fā)送的數(shù)據(jù)發(fā)送完畢。
  • 何時(shí)阻塞: 如果套接字有未發(fā)送的數(shù)據(jù)需要傳輸,shutdown() 會(huì)阻塞,直到數(shù)據(jù)傳輸完畢。
  • 代碼示例:
    int result = shutdown(sockfd, SHUT_WR);  // 關(guān)閉寫端,阻塞直到所有數(shù)據(jù)被發(fā)送
    if (result == -1) {perror("shutdown");
    }
    

為什么阻塞?

TCP 是一種面向連接、可靠的數(shù)據(jù)傳輸協(xié)議,它保證數(shù)據(jù)的可靠交付,確保所有數(shù)據(jù)包按照順序到達(dá)目的地,并通過流量控制、擁塞控制等機(jī)制避免網(wǎng)絡(luò)過載。為確保這些特性,某些操作需要等待特定事件的發(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)必須等待對方準(zhǔn)備好接收或發(fā)起連接。
  3. 緩沖區(qū)未滿:如在發(fā)送數(shù)據(jù)時(shí),如果發(fā)送緩沖區(qū)已滿,系統(tǒng)會(huì)等待緩沖區(qū)騰出空間來進(jìn)行數(shù)據(jù)傳輸。

如何避免阻塞?

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

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

  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ù)在沒有數(shù)據(jù)或連接不可用時(shí)會(huì)阻塞,直到特定條件滿足。為了避免阻塞,開發(fā)者可以使用非阻塞模式、超時(shí)機(jī)制或多線程來處理阻塞操作。

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

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

1. accept()

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

  • EINVAL: 如果套接字未正確綁定(如 bind() 未調(diào)用)或者套接字類型不支持 accept()(例如,UDP 套接字),則返回此錯(cuò)誤。
  • ECONNABORTED: 如果先前的連接被中止,accept() 返回此錯(cuò)誤。
  • EFAULT: 傳遞給 accept() 的地址指針無效。
  • 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í)表示對方已經(jīng)斷開連接。

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

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

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

  • EAGAINEWOULDBLOCK: 套接字被設(shè)置為非阻塞模式,且發(fā)送緩沖區(qū)已滿,無法繼續(xù)發(fā)送數(shù)據(jù)。
  • EBADF: 套接字無效,可能是已經(jīng)關(guān)閉或未正確初始化。
  • EINTR: 系統(tǒng)調(diào)用被信號(hào)中斷,導(dǎo)致 send() 被中斷。
  • ENOTCONN: 套接字未連接時(shí)調(diào)用 send() 會(huì)失敗。
  • ECONNRESET: 對方主機(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ò)誤,表示對方已經(jīng)關(guān)閉了連接,寫入操作失敗。

4. connect()

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

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

5. listen()

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

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

6. shutdown()

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

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

8. bind()

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

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

總結(jié)

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

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

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

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

具體情況:

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

    • 在正常的 TCP 連接關(guān)閉過程中,通信雙方會(huì)經(jīng)過四次揮手(Four-way Handshake),具體來說:
      • 一方(通常是主動(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)閉過程。

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

  2. recv() 返回 0 的例子: 下面是一個(gè)簡單的代碼示例,演示如何使用 recv() 判斷對方關(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");// 對方關(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,表示對方已經(jīng)正常關(guān)閉了連接。此時(shí),通常需要關(guān)閉自己的套接字并清理相關(guān)資源。
    • 如果 recv() 返回負(fù)值(< 0),表示發(fā)生了錯(cuò)誤,可以通過 errno 獲取錯(cuò)誤原因。

其他返回情況:

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

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

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

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

這是因?yàn)樵?TCP 協(xié)議中,連接關(guān)閉是通過發(fā)送 FIN 包來實(shí)現(xiàn)的。此時(shí),連接的另一端會(huì)通知接收端自己已經(jīng)沒有數(shù)據(jù)發(fā)送,并且希望關(guān)閉連接。當(dāng)接收端收到這個(gè) FIN 包后,recv() 返回 0,表示沒有更多的數(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)閉請求。此時(shí),接收方的接收緩沖區(qū)為空,不再有數(shù)據(jù)傳輸,recv() 將返回 0,表示對方已關(guān)閉連接。

總結(jié):

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

0voice · GitHub

http://aloenet.com.cn/news/41172.html

相關(guān)文章:

  • 哪里做企業(yè)網(wǎng)站上海seo服務(wù)
  • 天津市做網(wǎng)站的公司查淘寶關(guān)鍵詞排名軟件
  • 香港一卡通app下載鄭州seo外包顧問熱狗
  • 去哪里找做網(wǎng)站的百度競價(jià)ocpc投放策略
  • 設(shè)計(jì)導(dǎo)航網(wǎng)站 左側(cè)菜單欄網(wǎng)絡(luò)營銷課程論文
  • 農(nóng)產(chǎn)品網(wǎng)站如何做地推網(wǎng)易企業(yè)郵箱
  • 網(wǎng)站開發(fā)語言 排行榜關(guān)鍵詞seo公司真實(shí)推薦
  • 醫(yī)藥網(wǎng)站建設(shè)客戶的需求廈門關(guān)鍵詞排名推廣
  • 西部數(shù)碼 空間做2個(gè)網(wǎng)站什么是新媒體運(yùn)營
  • 自適應(yīng)手機(jī)網(wǎng)站 css愛站網(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ǎng)站優(yōu)化價(jià)格福鼎網(wǎng)站優(yōu)化公司
  • 廈門網(wǎng)站建設(shè)xm37網(wǎng)站的營銷推廣
  • 靜態(tài)網(wǎng)站建設(shè)課程設(shè)計(jì)百度一下生活更好
  • 網(wǎng)站404怎么做搜索排名提升
  • 網(wǎng)站怎么做直通車鄭州厲害的seo優(yōu)化顧問
  • 做攻略的網(wǎng)站好企業(yè)中層管理人員培訓(xùn)課程
  • 廣州網(wǎng)站排名優(yōu)化費(fèi)用招聘網(wǎng)絡(luò)營銷推廣人員
  • wordpress各部分功能百度seo關(guān)鍵詞優(yōu)化費(fèi)用
  • 自己做網(wǎng)站自己做推廣教程視頻教程網(wǎng)絡(luò)運(yùn)營培訓(xùn)
  • 水果b2b電商平臺(tái)有哪些seo技術(shù)員
  • 蘇州營銷型網(wǎng)站南寧企業(yè)官網(wǎng)seo
  • 上海做網(wǎng)站比較有名的公司湖南疫情最新消息今天
  • 我網(wǎng)站關(guān)鍵詞太多公司做網(wǎng)站推廣