<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 0.TCP連接的建立和終止 ### 1) 三次握手 ?? 建立一個TCP連接時會發生下述情形: (1)服務器必須準備好接受外來的連接。這通常通過調用socket,bind和listen這三個函數來完成,我們稱之為被動打開。 (2)客戶通過調用connect發起主動打開。這導致客戶TCP發送一個SYN(同步)分節,它告訴服務器客戶將在(待建立的)連接中發送的數據的初始序列號。通常SYN分節不攜帶數據,其所在IP數據報只含有一個IP首部,一個TCP首部及可能有的TCP選項。 (2-1)TCP選項之MSS選項: 發送SYN的TCP一端使用本選項通告對端它的最大分節大小(maximum segment size)即MSS,也就是它在本連接的每個TCP分節中愿意接受的最大數據量。發送端TCP使用接收端的MSS值作為所發送分節的最大大小。 (3)服務器必須確認(ACK)客戶的SYN,同時自己也得發送一個SYN分節,它含有服務器將在同一連接中發送的數據的初始序列號。服務器在單個分節中發送SYN和對客戶SYN的ACK。 (4)客戶必須確認服務器的SYN。 ?? 這種交換至少需要3個分組,因此稱之為TCP的三路握手。(這里SYN為1字節,所以ACK時候只要在K上簡單加1即可) ![](https://box.kancloud.cn/2016-06-20_57678b2f0b396.jpg) ### 2) TCP連接終止 ?? TCP終止一個連接則需4個分節: (1)某個應用進程首先調用close,我們稱該端執行主動關閉。該端的TCP于是發送一個FIN分節,表示數據發送完畢。 (2)接收到這個FIN的對端執行被動關閉。這個FIN由TCP確認。它的接收也作為一個文件結束符(EOF)傳遞給接收端應用進程(放在已排隊等候該應用進程接收的任何其他數據之后),因為FIN的接收意味著接收端應用進程在相應連接上再無額外數據可接收。 (3)一段時間后,接收到這個文件結束符的應用進程將調用close關閉它的套接字。這導致它的TCP也發送一個FIN。 (4)接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。 備注:從執行被動關閉到執行主動關閉(步驟2和步驟3之間)一端流動數據是可能的,這稱為半關閉,畢竟當時接收端的套接字并未close掉。(FIN和SYN一樣為1字節,所以ACK也是簡單的N+1) ![](https://box.kancloud.cn/2016-06-20_57678b2f2d781.jpg) ### 3) TCP狀態轉換圖 ![](https://box.kancloud.cn/2016-06-20_57678b2f55bb3.jpg) ### 4) 觀察分組 ![](https://box.kancloud.cn/2016-06-20_57678b2f81d9a.jpg) ## 1. socket函數 ?? 為了執行網絡I/O,一個進程必須做的第一件事就是調用socket函數,指定期望的通信協議類型。 ~~~ #include <sys/socket.h> int socket( int family, int type, int protocol ); 返回:若成功則為非負描述符,若出錯則為-1 ~~~ ### 1) socket函數的family常值 | family | 說明 | |-----|-----| | AF_INET | IPv4協議 | | AF_INET6 | IPv6協議 | | AF_LOCAL | unix域協議 | | AF_ROUTE | 路由套接字 | | AF_KEY | 密鑰套接字 | ### 2) socket函數的type常值 | type | 說明 | |-----|-----| | SOCK_STREAM | 字節流套接字 | | SOCK_DGRAM | 數據報套接字 | | SOCK_SEQPACKET | 有序分組套接字 | | SOCK_RAW | 原始套接字 | ### 3) socket函數AF_INET或AF_INET6的protocol常值 | protocol | 說明 | |-----|-----| | IPPROTO_TCP | TCP傳輸協議 | | IPPROTO_UDP | UDP傳輸協議 | | IPPROTO_SCTP | SCTP傳輸協議 | ?? socket函數在成功時返回一個小的非負整數值,它與文件描述符類似,我們把它稱為套接字描述符,簡稱sockfd. ?? 對于unix一切皆文件,則套接字描述符為網絡通信中的文件描述符。程序可以通過套接字描述符進行通信。 ## 2. connect函數 ?? TCP客戶用connect函數來建立與TCP服務器的連接 ~~~ #include <sys/socket.h> int connect( int sockfd, const struct sockaddr *servaddr, socklen_t addrlen ); 返回:若成功則為0,若出錯則為-1 ~~~ ?? sockfd是由socket函數返回的套接字描述符,第二個,第三個參數分別是一個指向套接字地址結構的指針和該結構的大小。 ?? 客戶在調用函數connect前不必非得調用bind函數,因為如果需要的話,內核會確定源IP地址,并選擇一個臨時端口作為源端口。 ?? 如果是TCP套接字,調用connect函數將激發TCP的三路握手過程,而且僅在連接建立成功或出錯時才返回,其中錯誤返回可能有以下幾種情況: 1)若TCP客戶沒有收到SYN分節的響應,則返回ETIMEDOUT錯誤(超時) 2) 若對客戶的SYN的響應是RST(表示復位),則表明該服務器主機在我們指定的端口上沒有進程在等待與之連接(例如服務器進程也許沒在運行,畢竟端口用于標識一個進程)。這是一種硬件錯誤(hard error),客戶一接收到RST就馬上返回ECONNREFUSED錯誤。 ?? RST是TCP在發生錯誤時發送的一種TCP分節。產生RST的三個條件是:目的地為某端口的SYN到達,然而該端口上沒有正在監聽的服務器;TCP想取消一個已有連接;TCP接收到一個根本不存在的連接上的分節。 3)若客戶發出的SYN在中間的某個路由器上引發了一個“destination unreadchable”ICMP錯誤,則認為是一種軟件錯誤(soft error)。客戶主機內核保存該消息,并繼續發送SYN。若超時,則將ICMP錯誤作為EHOSTUNREACH或ENETUNREACH錯誤返回給進程。 ?? 若connect失敗則該套接字不再可用,必須關閉,我們不能對這樣的套接字再次調用connect函數。當循環調用函數connect為給定主機嘗試各個IP地址直到有一個成功時,在每次connect失敗后,都必須close當前的套接字描述符并重新調用socket。 ## 3. bind函數 ?? bind函數把一個本地協議地址賦予一個套接字。 ~~~ #include <sys/socket.h> int bind( int sockfd, const struct sockaddr *myaddr, socklen_t addrlen ); 返回:若成功則為0,若出錯則為-1 ~~~ ?? 第二個參數是一個指向特定與協議的地址結構的指針,第三個參數是該地址結構的長度。 1) 服務器在啟動時捆綁它們的總所周知端口(端口用于標識一個進程,如果端口為0,則由內核選擇端口,而且必須使用getsockname來返回協議地址來得到內核所選擇的這個端口號) 2) 進程可以把一個特定的IP地址捆綁到它的套接字上(一般都是通配地址,用常量值INADDR_ANY來指定,如htonl( INADDR_ANY)) ## 4. listen函數 ?? listen函數僅由TCP服務器調用,它做兩件事情: 1) 當socket函數創建一個套接字時,它被假設為一個主動套接字,也就是說,它是一個將調用connect發起連接的客戶套接字。listen函數把一個未連接的套接字轉換成一個被動套接字,指示內核應接受指向該套接字的連接請求。 2) 第二個參數規定了內核應該為相應套接字排隊的最大連接個數。 ~~~ #include <sys/socket.h> int listen( int sockfd, int backlog ); 返回:若成功則為0,若出錯則為-1 ~~~ ### (1) 理解backlog 內核為任何一個給定的監聽套接字維護兩個隊列: 未完成連接隊列:每個這個的SYN分節對應其中一項:已由某個客戶發出并到達服務器,而服務器正在等待完成相應的TCP三路握手過程。這些套接字處于SYN_RCVD狀態。 已完成連接隊列:每個已完成TCP三路握手過程的客戶對應其中一項。這些套接字處于ESTABLISHED狀態。 ![](https://box.kancloud.cn/2016-06-20_57678b2fa8bed.jpg) ?? 每當在未完成連接隊列中創建一項時,來自監聽套接字的參數就復制到即將建立的連接中。連接的創建機制是完全自動的,無需服務器進程握手: ![](https://box.kancloud.cn/2016-06-20_57678b2fc3200.jpg) ## 5. accept函數 ?? accept函數由TCP服務器調用,用于從已完成連接隊列隊頭返回下一個已完成連接。如果已完成連接隊列為空,那么進程被投入睡眠(假定套接字為默認的阻塞方式) ~~~ #include <sys/socket.h> int accept( int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen ); 返回:若成功則為非負描述符,若出錯則為-1 ~~~ ?? 參數cliaddr和addrlen用來返回已連接的對端進程(客戶)的協議地址。addrlen是值-結果參數:調用前,我們將由*addrlen所引用的整數值置為由cliaddr所指的套接字地址結構的長度,返回時,該整數值即為由內核存放在該套接字地址結構內的確切字節數。 ?? 我們在srv.c中增加以下代碼,就可以看到客戶端的IP和端口了: ~~~ socklen_t len; struct sockaddr_in servaddr, cliaddr; len = sizeof(cliaddr); connfd = accept( listenfd, ( SA * )&cliaddr, &len ); inet_ntop( AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)); printf("connection from %s,port %d\n", buff , ntohs(cliaddr.sin_port)); ~~~ 而服務端則會輸出: ~~~ leichaojian@ThinkPad-T430i:~$ ./daytimetcpsrv1 connection from 127.0.0.1,port 57452 ~~~ ## 6. close函數 ?? close函數也用來關閉套接字,并終止TCP連接。 ~~~ #include <unistd.h> int close( int sockfd ); ~~~ ## 7. getsockname和getpeername函數 ?? 這兩個函數或者返回與某個套接字關聯的本地協議地址(getsockname),或者返回與某個套接字關聯的外地協議地址(getpeername) ### 1)getsockname的測試函數如下: 服務端: ~~~ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <sys/socket.h> #include <sys/types.h> #include <fcntl.h> #include <netinet/in.h> #define MAXLINE 1024 #define SA struct sockaddr int main(int argc, char **argv) { int listenfd, connfd; int buff[MAXLINE]; pid_t pid; time_t ticks; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; socklen_t cliLen; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(9877); bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); listen(listenfd, 5); for ( ; ; ){ cliLen = sizeof(cliaddr); connfd = accept(listenfd, (SA *)&cliaddr, &cliLen); if ((pid = fork()) == 0){ close(listenfd); ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); _exit(0); } if (waitpid(pid, NULL, 0) != pid){ printf("waitpid error\n"); exit(1); } close(connfd); } return 0; } ~~~ 客戶端: ~~~ #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <fcntl.h> #define MAXLINE 1024 #define SA struct sockaddr int main(int argc, char **argv) { int sockfd, n; struct sockaddr_in servaddr; char buff[MAXLINE + 1]; struct sockaddr_in cliaddr; socklen_t cliLen; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(9877); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); connect(sockfd, (SA *)&servaddr, sizeof(servaddr)); cliLen = sizeof(cliaddr); getsockname(sockfd, (SA *)&cliaddr, &cliLen); while ((n = read(sockfd, buff, MAXLINE)) > 0){ buff[n] = '\0'; fputs(buff, stdout); } return 0; } ~~~ ?? 當我們分別用gdb調試的時候,發現服務端和客戶端的cliaddr的內容是一致的: ~~~ (gdb) p cliaddr $2 = {sin_family = 2, sin_port = 49635, sin_addr = {s_addr = 16777343}, sin_zero = "\000\000\000\000\000\000\000"} ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看