Linux网络编程之tcpdump抓包分析TCP三次握手过程

原文地址:http://blog.csdn.net/polarbearboy/article/details/6679601

使用TCP协议进行网络通讯时,通信的两端首先需要建立起一条连接链路,当然这并不表示使用UDP通信不需要“连接链路”,这里说的连接链路指的是通信协议范畴的东东,并不是物理介质或者电磁波信号,只所以说TCP是面向连接的网络通信协议,主要是指双方在通信时都会保持一些连接相关的信息,比如已收到的分组的序列号,下一次需要收到的分组的序号,对方的滑动窗口信息等等。

        OK,闲话少扯,我们进入主题,下面结合一个简单的TCP服务端与客户端代码,借助tcpdump命令来分析一下TCP建立连接时的三次握手过程(Three-way handshake process)。

服务端代码如下:

  1. /** 
  2.  * server.c 
  3.  * 
  4.  * TCP server program, it is a simple example only. 
  5.  * 
  6.  * Writen By: Zhou Jianchun 
  7.  * Date: 2011.08.12 
  8.  * 
  9.  * Compiled With: gcc -o client client.c 
  10.  * Tested On: Ubuntu 11.04 LTS 
  11.  * 
  12.  * gcc version: 4.5.2 
  13.  * 
  14.  */  
  15.   
  16. #include <stdio.h>  
  17. #include <sys/socket.h>  
  18. #include <unistd.h>  
  19. #include <sys/types.h>  
  20. #include <netinet/in.h>  
  21. #include <stdlib.h>  
  22. #include <time.h>  
  23. #include <strings.h>  
  24. #include <string.h>  
  25.   
  26. #define SERVER_PORT 20000  
  27. #define LENGTH_OF_LISTEN_QUEUE 10  
  28. #define BUFFER_SIZE 255  
  29. #define WELCOME_MESSAGE "welcome to our server."  
  30.   
  31. int main(int argc, char **argv)  
  32. {  
  33.     int server_fd, client_fd;  
  34.     struct sockaddr_in server_addr, client_addr;  
  35.   
  36.     if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  37.     {  
  38.         printf("create socket error, exit!\n");  
  39.         exit(1);  
  40.     }  
  41.   
  42.     bzero(&server_addr, sizeof(server_addr));  
  43.     server_addr.sin_family = AF_INET;  
  44.     server_addr.sin_port = htons(SERVER_PORT);  
  45.     server_addr.sin_addr.s_addr = htons(INADDR_ANY);  
  46.   
  47.     if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)  
  48.     {  
  49.         printf("bind to port %d failed, exit!\n", SERVER_PORT);  
  50.         exit(1);  
  51.     }  
  52.   
  53.     if(listen(server_fd, LENGTH_OF_LISTEN_QUEUE) < 0)  
  54.     {  
  55.         printf("failed to listen, exit!\n");  
  56.         exit(1);  
  57.     }  
  58.   
  59.     while(1)  
  60.     {  
  61.         char buf[BUFFER_SIZE];  
  62.         long timestamp;  
  63.         socklen_t length = sizeof(client_addr);  
  64.         client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &length);  
  65.         if(client_fd <0)  
  66.         {  
  67.             printf("call accept error, break from while loop!\n");  
  68.             break;  
  69.         }  
  70.         strcpy(buf, WELCOME_MESSAGE);  
  71.         printf("connect from client: IP: %s, Port: %d\n", (char *)inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));  
  72.         timestamp = time(NULL);  
  73.         strcat(buf, "timestamp on server:");  
  74.         strcat(buf, ctime(×tamp));  
  75.         send(client_fd, buf, BUFFER_SIZE, 0);  
  76.         close(client_fd);  
  77.   
  78.         close(server_fd);  
  79.         return 0;  
  80.     }  
  81. }  

客户端代码:
  1. /** 
  2.  * client.c 
  3.  * 
  4.  * TCP client program, it is a simple example only. 
  5.  * 
  6.  * Writen By: Zhou Jianchun 
  7.  * Date: 2011.08.12 
  8.  * 
  9.  * Compiled With: gcc -o client client.c 
  10.  * Tested On: Ubuntu 11.04 LTS 
  11.  * 
  12.  * gcc version: 4.5.2 
  13.  * 
  14.  */  
  15.   
  16. #include <stdio.h>  
  17. #include <sys/socket.h>  
  18. #include <unistd.h>  
  19. #include <sys/types.h>  
  20. #include <netinet/in.h>  
  21. #include <stdlib.h>  
  22. #include <string.h>  
  23.   
  24. #define SERVER_PORT 20000  
  25. #define CLIENT_PORT ((20001 + rand()) % 65536)  
  26. #define BUFFER_SIZE 255  
  27. #define REQUEST_MESSAGE "welcome to connect the server.\n"  
  28.   
  29. void usage(char *name)  
  30. {  
  31.     printf("usage: %s IP\n", name);  
  32. }  
  33. int main(int argc, char **argv)  
  34. {  
  35.     int server_fd, client_fd, length = 0;  
  36.     struct sockaddr_in server_addr, client_addr;  
  37.     socklen_t socklen = sizeof(server_addr);  
  38.     char buf[BUFFER_SIZE];  
  39.   
  40.     if(argc < 2)  
  41.     {  
  42.         usage(argv[0]);  
  43.         exit(1);  
  44.     }  
  45.     if((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  46.     {  
  47.         printf("create socket error, exit!\n");  
  48.         exit(1);  
  49.     }  
  50.     srand(time(NULL));  
  51.     bzero(&client_addr, sizeof(client_addr));  
  52.     client_addr.sin_family = AF_INET;  
  53.     //client_addr.sin_port = htons(CLIENT_PORT);  
  54.     client_addr.sin_port = htons(40000);  
  55.     client_addr.sin_addr.s_addr = htons(INADDR_ANY);  
  56.   
  57.     bzero(&server_addr, sizeof(server_addr));  
  58.     server_addr.sin_family = AF_INET;  
  59.     inet_aton(argv[1], &server_addr.sin_addr);  
  60.     server_addr.sin_port = htons(SERVER_PORT);  
  61.   
  62.     /*if(bind(client_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0) 
  63.     { 
  64.         printf("bind to port %d failed, exit!\n", CLIENT_PORT); 
  65.         exit(1); 
  66.     }*/  
  67.   
  68.     if(connect(client_fd, (struct sockaddr*)&server_addr, socklen) < 0)  
  69.     {  
  70.         printf("can not connect to %s, exit!\n", argv[1]);  
  71.         exit(1);  
  72.     }  
  73.   
  74.     /*length = recv(client_fd, buf, BUFFER_SIZE, 0); 
  75.     if(length < 0) 
  76.     { 
  77.         printf("recieve data from %s error, exit!\n", argv[1]); 
  78.         exit(1); 
  79.     } 
  80.     */  
  81.     char *tmp = buf;  
  82.     while((length = read(client_fd, tmp, BUFFER_SIZE)) > 0)  
  83.     {  
  84.         tmp += length;  
  85.     }  
  86.     printf("frome server %s:\n\t%s", argv[1], buf);  
  87.     close(client_fd);  
  88.     return 0;  
  89. }  

代码逻辑十分简单,服务端程序启动后监听在20000端口,等待外部连接,客户端启动后连接过来,服务端发送一串字符串信息给客户端,然后退出,客户端在读取完信息后也退出。

运行程序之前先在另一个终端下输入如下命令:

tcpdump 'port 20000' -i lo -S

待两端程序退出后可以看到该命令输出如下信息:

  1. 17:05:35.358403 IP neptune.local.49493 > neptune.local.20000: Flags [S], seq 1317094743, win 32792, options [mss 16396,sackOK,TS val 7083694 ecr 0,nop,wscale 6], length 0  
  2. 17:05:35.358439 IP neptune.local.20000 > neptune.local.49493: Flags [S.], seq 1311370954, ack 1317094744, win 32768, options [mss 16396,sackOK,TS val 7083694 ecr 7083694,nop,wscale 6], length 0  
  3. 17:05:35.358468 IP neptune.local.49493 > neptune.local.20000: Flags [.], ack 1311370955, win 513, options [nop,nop,TS val 7083694 ecr 7083694], length 0  
  4. 17:05:35.358871 IP neptune.local.20000 > neptune.local.49493: Flags [P.], seq 1311370955:1311371210, ack 1317094744, win 512, options [nop,nop,TS val 7083694 ecr 7083694], length 255  
  5. 17:05:35.358890 IP neptune.local.49493 > neptune.local.20000: Flags [.], ack 1311371210, win 530, options [nop,nop,TS val 7083694 ecr 7083694], length 0  
  6. 17:05:35.358913 IP neptune.local.20000 > neptune.local.49493: Flags [F.], seq 1311371210, ack 1317094744, win 512, options [nop,nop,TS val 7083694 ecr 7083694], length 0  
  7. 17:05:35.359419 IP neptune.local.49493 > neptune.local.20000: Flags [F.], seq 1317094744, ack 1311371211, win 530, options [nop,nop,TS val 7083694 ecr 7083694], length 0  
  8. 17:05:35.359441 IP neptune.local.20000 > neptune.local.49493: Flags [.], ack 1317094745, win 512, options [nop,nop,TS val 7083694 ecr 7083694], length 0  

下面我们逐条进行分析:

1.客户端通过49493端口向服务端的20000端口发送一个SYN同步请求包,展开第一次握手,其中Flags [S]表求数据包的类型为SYN, 即同步请求包,seq字段标识数据包序列号。

2.服务端发送ACK确认包,同时附代一个SYN请求包,在确认客户端同步请求的同时向客户端发送同步请求,其中Flags [S.]中的点号表示这是个确认包(ACK),S表示它同时又是一个SYN请求包。因为TCP是双工通信协议,连接建立之后双方可以同时收发数据,所以双方都发送了SYN包请求同步。

3.客户端发送ACK包确认服务端的SYN同步请求,可以看到此时Flags中只有一个小数点,表示这个包只是用来做确认的。

到此为止,三次握手过程就结束了,双方如果都收到了ACK包,则都进入到ESTABLISHED状态,表明此时可以进行数据发送了。

4.服务端向客户端发送一个数据包,包中的内容就是一个字符串,可以看到此时的Flags标识中有个字母P,意为PUSH DATA,就是发送数据的意思。

至此TCP三次握手过程的分析就结束了,由于本人水平有限,博客中的不妥或错误之处在所难免,殷切希望读者批评指正。同时也欢迎读者共同探讨相关的内容,如果乐意交流的话请留下您宝贵的意见,谢谢。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值