本文要求讀者在閱讀之前應該對TCP通過三次握手建立和關閉連接有一定的了解,本文并沒有詳細講解三次握手,只是通過一個實例對三次握手進行了一下驗證。
tcp連接的建立和關閉想必大家都已經非常熟悉了!通過三次握手建立連接和通過三次或者四次(半關閉)握手來關閉連接!在這里,我想通過一個具體的實例程序,來分析一下這個過程!
首先說用到的工具吧,linux下的tcpdump命令,和自己用c語言寫的一個服務器端和一個客戶端程序。程序的代碼如下:
頭文件:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 #include<netdb.h> 7 #include<errno.h> 8 #include<signal.h> 9 #include<unistd.h>10 #include<string.h>11 #include<sys/wait.h>12 #include<arpa/inet.h>Header
服務器端:
1 #include"header.h" 2 int main(int argc,char *argv[]) 3 { 4 int socket_n; //套接字描述符 5 int listen_s; //監聽套接字描述符 6 socklen_t cli_addr_len; //客戶端地址長度 7 struct sockaddr_in server_addr; //服務器地址 8 struct sockaddr_in client_addr; //客戶端地址 9 10 int n=0; //接受到的數據長度11 char buffer[256]; //數據緩沖區12 int maxLen=sizeof(buffer);13 memset(buffer,0,maxLen);14 char cli_addr[20];15 16 //回創建監聽套接字17 listen_s=socket(AF_INET,SOCK_STREAM,0);18 19 //創建本地服務器套接字20 memset(&server_addr,0,sizeof(server_addr));21 server_addr.sin_family = AF_INET;22 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);23 server_addr.sin_port=htons(9877);24 25 //將套接字綁定到本地套接字地址26 if(bind(listen_s,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)27 {28 perror("Error:binding failed!");29 exit(0);30 }31 //監聽鏈接請求32 if(listen(listen_s,maxLen) < 0)33 {34 perror("Error:listening failed!");35 exit(1);36 }37 38 while(1)39 {40 if((socket_n=accept(listen_s,(struct sockaddr *)&client_addr,&cli_addr_len)) < 0)41 {42 perror("Error:accepting failed!");43 exit(1);44 }45 read(socket_n,buffer,maxLen);46 inet_ntop(AF_INET,&client_addr.sin_addr,cli_addr,sizeof(cli_addr));47 PRintf("%s sent %s",cli_addr,buffer);48 write(socket_n,buffer,strlen(buffer));49 printf("剛剛建立的連接即將關閉/n");50 close(socket_n);51 }52 return 0;53 }Server
客戶端:
1 #include"header.h" 2 int main(int argc,char *argv[]) 3 { 4 int sockfd; 5 char buffer_s[256]; 6 char buffer_r[256]; 7 struct sockaddr_in servaddr; 8 9 memset(buffer_r,0,sizeof(buffer_r));10 memset(buffer_s,0,sizeof(buffer_s));11 12 if(argc != 2)13 {14 printf("usage : client <ip address>!");15 exit(0);16 }17 18 sockfd = socket(AF_INET,SOCK_STREAM,0);19 20 memset(&servaddr,0,sizeof(servaddr));21 servaddr.sin_family = AF_INET;22 servaddr.sin_port = htons(9877);23 inet_pton(AF_INET,argv[1],&servaddr.sin_addr);24 25 connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));26 27 while(fgets(buffer_s,256,stdin) != NULL)28 {29 write(sockfd,buffer_s,strlen(buffer_s));30 if(read(sockfd,buffer_r,256) == 0)31 {32 printf("client:server terminated prematurely"); 33 }34 fputs(buffer_r,stdout);35 }36 37 return 0;38 }Client
tcpdump用的命令是這句:
tcpdump -i lo tcp port 9877 and host 127.0.0.1
這條命令表示,我抓取lo網卡(環回接口)上ip為127.0.0.1且端口號為9877(這個9877是我的程序中服務器綁定的接口)的包!
運行的結果如下圖:
客戶端發送一個"a/n"給服務器,并且接收到一個"a/n";
這個截的圖有點多了,這個與本文有關的部分就是最底下的從./server 開始的部分,服務器接受到一個"a/n",回傳給客戶端之后立刻將這個連接關閉,并且提示"剛剛建立的連接即將關閉"!
這個是抓包軟件抓到的圖,這個就得好好分析分析了!
首先需要說明幾點的是,這個分析是從那個15:32:38.348872開始,那個38264表示的是客戶端的端口號,9877表示的是服務器的端口號,關于有些包的符號位(圖中flags部分)中應該有ACK這個標志,可是具體沒有顯示,我認為可能是tcpdump省略了,還有些包中的SYN符號(用一個S表示)也可能省略了。另外需要說明的一點是服務器和客戶端的序號應該都是隨機數,可是連接建立之后就自動從1開始,我認為這個是tcpdump這個軟件自動進行了計算!
第一條信息表示客戶端發送給服務器一個包,其序號seq為598232472,標志位為SYN,就是建立連接的第一次握手??蛻舳税l送自己的序號。
第二條信息表示服務器發送給客戶端一個包,其序號seq為3283581888,確認號為5982324272,標志位為SYC(理論上還應該有ACK,可能這里沒有顯示出來),這就是建立連接的第二次握手,服務器發送這邊的序號,并對客戶端的序號進行確認。
第三條信息表示客戶端發送給服務器一個包,沒有序號,確認號為1(我認為這里是tcpdump這個抓包軟件進行了處理),表示想要從服務器接受第一個字節,到這里,三次握手已經完成,客戶端到服務器的連接已經建立。
第四條信息表示客戶端發送兩個字節的信息給服務器,序號seq為1,確認號為1。標志位為PSH(表示不在窗口里面緩存,直接交給應用程序)。
第五條信息表示服務器對客戶端發送的信息表示確認,確認號為3,沒有序號。
第六條信息表示服務器發送給客戶端兩個字節的信息,序號為1,確認號為3.符號位為PSH。
第七條信息表示服務器發送給客戶端一個連接終止的FIN信息。序號為3,確認號為3,從這里開始了連接關閉的三次握手過程。(這里是服務器主動關閉的,所以三次握手就變成了二次握手~~)。
第八條信息表示客戶端發送給服務器的一個確認信息,確認號為3,表示對服務器發送的那兩個字節的確認。
第九條信息表示客戶端發送一個確認信息給服務器,確認號為4,表示對服務器發送的FIN終止信息進行確認,至此,那個TCP連接也就關閉了。
OK,這就是三次握手的實例,希望能夠幫助大家更好地理解三次握手這個過程。
新聞熱點
疑難解答