網路程式設計學習筆記(七)優雅的斷開套接字連線

2021-08-21 16:50:10 字數 4456 閱讀 9131

tcp中的斷開連線過程更重要,因為連線過程中一般不會出現大的變數,但斷開過程有可能發生意想不到的情況,因此應準確掌控。只有明白了下面講的半關閉(half-close),才能明確斷開過程。

linux的close函式和windows的closesocket函式意味著完全斷開連線。完全斷開不僅指無法傳輸資料,而且也不能接收資料。因此,在某些情況下,通訊一方呼叫close或closesocket函式就顯得不太優雅。

為了解決這類問題,「只關閉一部分資料交換中使用的流」(half-close)的方法應運而生。斷開一部分連線是指,可以傳輸資料但無法接收,或可以接收資料但無法傳輸。顧名思義就是只關閉流的一半。

兩台主機通過套接字建立連線後進入可交換資料的狀態,又稱「流形成的狀態」。也就是把建立套接字後可交換資料的狀態看作一種流。

套接字的流像水流一樣,資料只能向乙個方向移動。因此,為了進行雙向通訊,需要兩個流——輸入輸出流。

本章所說的「優雅的斷開連線方式」只斷開其中1個流,而非同時斷開兩個流。linux的close和windows的closesocket函式將同時斷開這兩個流,因此與「優雅」二字還有一段距離。

介紹半關閉的函式:

#include 

int shutdown(int sock, int howto);

sock:需要斷開的套接字檔案描述符

howto:傳遞斷開方式資訊

成功時返回0,失敗時返回-1

第二個引數決定斷開接連的方式,其可能值如下所示:

1. shut_rd:斷開輸入流

2. shut_wr:斷開輸出流

3. shut_rdwr:同時斷開i/o流

斷開輸入流,套接字無法接收資料。及時輸入緩衝收到資料也會抹去,而且無法呼叫輸入相關函式。如果是斷開輸出流,也就無法傳輸資料。但如果輸出緩衝還留有未傳輸的資料,則將傳遞至目標主機。最後,若傳入第三個引數,則同時中斷i/o流。這相當於分兩次呼叫shutdown,第一次關閉輸入流,第二次關閉輸出流。

「究竟為什麼需要半關閉?是否只要留出足夠長的連線時間,保證完成資料交換即可?只要不急於斷開連線,好像也沒必要使用半關閉」

這句話也不完全是錯的,如果保持足夠的時間間隔,完成資料交換後再斷開連線,這時就沒有必要使用半關閉。但要考慮如下情況:

「一旦客戶端連線到伺服器端,伺服器端將約定的檔案傳給客戶端,客戶端收到後傳送字串『thank you』給伺服器端」。

此處字串「thank you」的傳遞是多餘的,這只是用來模擬客戶端斷開連線前還有資料需要傳遞的情況。此時程式實現的難度並不小,因為傳輸檔案的伺服器端只需連續傳輸檔案資料即可,而客戶端則無法知道需要接收資料到何時。客戶端也沒辦法無休止地呼叫輸入函式,因為這有可能導致程式阻塞(呼叫的函式未返回)

「是否可以讓伺服器端和客戶端約定乙個代表檔案結尾的字元?」

這種方式也有問題,因為這意味著檔案中不能有與約定字元相同的內容。為解決該問題,伺服器端應最後向客戶端傳遞eof表示檔案傳輸結束。客戶端通過函式返回值接收eof,這樣可以避免與檔案內容衝突。剩下最後乙個問題:伺服器如何傳遞eof?

「斷開輸出流時向對方主機傳輸eof」

當然,呼叫close函式的同時關閉i/o流,這樣會向對方傳送eof。但此時無法再接收對方傳輸的資料。這時就需要shutdown函式,只關閉伺服器的輸出流(半關閉)。這樣既可以傳送eof,同時又保留了輸入流,可以接收對方資料。

首先介紹伺服器端。肯呢個該示例與之前的示例不同,省略了一些錯誤處理**。(實際編寫中不應省略)

file_server.c

#include 

#include

#include

#include

#include

#include

#define buf_size 30

void error_handling(char *message);

int main(int argc, char *argv)

fp = fopen("file_server.c", "rb");

serv_sock = socket(pf_inet,sock_stream,0);

if(serv_sock == -1)

error_handling("socket() error");

memset(&serv_addr, 0, sizeof(serv_addr));

serv_addr.sin_family = af_inet;

serv_addr.sin_addr.s_addr = htonl(inaddr_any);

serv_addr.sin_port = htons(atoi(argv[1]));

if(bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)

error_handling("bind() error");

if(listen(serv_sock,5) == -1)

error_handling("listen() error");

clnt_addr_size = sizeof(clnt_addr);

clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);

if(clnt_sock == -1)

error_handling("accept() error");

else

printf("connect client\n");

while(1)

write(clnt_sock, buf, buf_size);

}shutdown(clnt_sock, shut_wr);

read(clnt_sock, buf, buf_size);

printf("message from client: %s \n",buf);

fclose(fp);

close(clnt_sock);

close(serv_sock);

return0;}

void error_handling(char *message)

file_client.c

#include 

#include

#include

#include

#include

#include

#define buf_size 30

void error_handling(char *message);

int main(int argc, char *argv)

fp = fopen("receive.dat", "wb");

sock = socket(pf_inet,sock_stream,0);

if(sock == -1)

error_handling("socket() error");

memset(&serv_addr, 0, sizeof(serv_addr));

serv_addr.sin_family = af_inet;

serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

serv_addr.sin_port = htons(atoi(argv[2]));

if(connect(sock,(struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)

error_handling("connect() error");

else

puts("connected.........");

while((read_cnt = read(sock, buf, buf_size)))

fwrite((void*)buf, 1, read_cnt, fp);

puts("received file data");

write(sock, "thank you", 10);

fclose(fp);

close(sock);

return0;}

void error_handling(char *message)

windows平台同樣通過呼叫shutdown函式完成半關閉,只是向其傳遞的引數名略有不同,要確認。

#include 

int shutdown(socket sock, int howto);

sock:要斷開的套接字控制代碼

howto:斷開方式的資訊

成功時返回0,失敗時返回socket_error

第二個引數:

1. sd_receive 斷開輸入流

2. sd_send 斷開輸出流

3. sd_both 同時斷開i/o流

雖然常量名不同於linux中的名稱,但其值完全相同。

示例就不再多說。

socket程式設計 優雅的斷開連線shutdown

呼叫 close closesocket 函式意味著完全斷開連線,即不能傳送資料也不能接收資料,這種 生硬 的方式有時候會顯得不太 優雅 上圖演示了兩台正在進行雙向通訊的主機。主機a傳送完資料後,單方面呼叫 close closesocket 斷開連線,之後主機a b都不能再接受對方傳輸的資料。實際...

《網路程式設計》 第7章 優雅地斷開套接字連線

思想 只關閉一部分資料交換中使用的流 half close 斷開一部分連線是指,可以傳輸資料但無法接收,或可以接收資料但無法傳輸。顧名思義就是只關閉流的一半。include int shutdown int sock,int howto 功能 關閉一部分資料交換中使用的流。引數 sock 需要斷開的...

Linux網路程式設計(七) 套接字選項

1 include2 int getsockopt int sockfd,int level,int optname,void optval,socklen t optlen 3 int setsockopt int sockfd,int level,int optname,const void o...