Chinaunix首页 | 论坛 | 博客
  • 博客访问: 535454
  • 博文数量: 86
  • 博客积分: 1076
  • 博客等级: 准尉
  • 技术积分: 1018
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-02 19:15
文章分类

全部博文(86)

文章存档

2013年(15)

2012年(69)

2011年(2)

分类: LINUX

2012-03-31 11:16:21

参考自:http://blog.csdn.net/polarbearboy/article/details/6679601

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

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

1、服务端代码如下:


点击(此处)折叠或打开

  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.
  13.  *
  14.  */

  15. #include <stdio.h>
  16. #include <sys/socket.h>
  17. #include <unistd.h>
  18. #include <sys/types.h>
  19. #include <netinet/in.h>
  20. #include <stdlib.h>
  21. #include <time.h>
  22. #include <strings.h>
  23. #include <string.h>

  24. #define SERVER_PORT 20000
  25. #define LENGTH_OF_LISTEN_QUEUE 10
  26. #define BUFFER_SIZE 255
  27. #define WELCOME_MESSAGE "welcome to our server."

  28. int main(int argc, char **argv)
  29. {
  30.     int server_fd, client_fd;
  31.     struct sockaddr_in server_addr, client_addr;

  32.     if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  33.     {
  34.         printf("create socket error, exit!\n");
  35.         exit(1);
  36.     }

  37.     bzero(&server_addr, sizeof(server_addr));
  38.     server_addr.sin_family = AF_INET;
  39.     server_addr.sin_port = htons(SERVER_PORT);
  40.     server_addr.sin_addr.s_addr = htons(INADDR_ANY);

  41.     if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
  42.     {
  43.         printf("bind to port %d failed, exit!\n", SERVER_PORT);
  44.         exit(1);
  45.     }

  46.     if(listen(server_fd, LENGTH_OF_LISTEN_QUEUE) < 0)
  47.     {
  48.         printf("failed to listen, exit!\n");
  49.         exit(1);
  50.     }

  51.     while(1)
  52.     {
  53.         char buf[BUFFER_SIZE];
  54.         long timestamp;
  55.         socklen_t length = sizeof(client_addr);
  56.         client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &length);
  57.         if(client_fd <0)
  58.         {
  59.             printf("call accept error, break from while loop!\n");
  60.             break;
  61.         }
  62.         strcpy(buf, WELCOME_MESSAGE);
  63.         printf("connect from client: IP: %s, Port: %d\n", (char *)inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
  64.         timestamp = time(NULL);
  65.         strcat(buf, "timestamp on server:");
  66.         strcat(buf, ctime(&timestamp));
  67.         send(client_fd, buf, BUFFER_SIZE, 0);
  68.         close(client_fd);
  69.         close(server_fd);

  70.         return 0;
  71.     }
  72. }

客户端代码:

点击(此处)折叠或打开

  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. #include <stdio.h>
  16. #include <sys/socket.h>
  17. #include <unistd.h>
  18. #include <sys/types.h>
  19. #include <netinet/in.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22.   
  23. #define SERVER_PORT 20000
  24. #define CLIENT_PORT ((20001 + rand()) % 65536)
  25. #define BUFFER_SIZE 255
  26. #define REQUEST_MESSAGE "welcome to connect the server.\n"
  27.  
  28. void usage(char *name)
  29. {
  30.     printf("usage: %s IP\n", name);
  31. }

  32. int main(int argc, char **argv)
  33. {
  34.     int server_fd, client_fd, length = 0;
  35.     struct sockaddr_in server_addr, client_addr;
  36.     socklen_t socklen = sizeof(server_addr);
  37.     char buf[BUFFER_SIZE];
  38.  
  39.     if(argc < 2)
  40.     {
  41.         usage(argv[0]);
  42.         exit(1);
  43.     }
  44.     if((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  45.     {
  46.         printf("create socket error, exit!\n");
  47.         exit(1);
  48.     }
  49.     srand(time(NULL));
  50.     bzero(&client_addr, sizeof(client_addr));
  51.     client_addr.sin_family = AF_INET;
  52.     //client_addr.sin_port = htons(CLIENT_PORT);
  53.     client_addr.sin_port = htons(40000);
  54.     client_addr.sin_addr.s_addr = htons(INADDR_ANY);
  55.   
  56.     bzero(&server_addr, sizeof(server_addr));
  57.     server_addr.sin_family = AF_INET;
  58.     inet_aton(argv[1], &server_addr.sin_addr);
  59.     server_addr.sin_port = htons(SERVER_PORT);
  60.  
  61.     /*if(bind(client_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0)
  62.     {
  63.         printf("bind to port %d failed, exit!\n", CLIENT_PORT);
  64.         exit(1);
  65.     }*/

  66.     if(connect(client_fd, (struct sockaddr*)&server_addr, socklen) < 0)
  67.     {
  68.         printf("can not connect to %s, exit!\n", argv[1]);
  69.         exit(1);
  70.     }
  71.   
  72.     /*length = recv(client_fd, buf, BUFFER_SIZE, 0);
  73.     if(length < 0)
  74.     {
  75.         printf("recieve data from %s error, exit!\n", argv[1]);
  76.         exit(1);
  77.     }
  78.     */
  79.     char *tmp = buf;
  80.     while((length = read(client_fd, tmp, BUFFER_SIZE)) > 0)
  81.     {
  82.         tmp += length;
  83.     }
  84.     printf("frome server %s:\n\t%s", argv[1], buf);
  85.     close(client_fd);

  86.     return 0;
  87. }

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

利用gcc编译,然后先看看运行结果:

点击(此处)折叠或打开

  1. root@jgf:~/JGF/study/socket# ./server&
  2. [3] 24647
  3. root@jgf:~/JGF/study/socket# ./client
  4. usage: ./client IP
  5. root@jgf:~/JGF/study/socket# ./client localhost
  6. connect from client: IP: 127.0.0.1, Port: 42553
  7. frome server localhost:
  8. welcome to our server.timestamp on server:Sat Mar 31 11:19:52 2012
  9. [3] Done ./server
  10. root@jgf:~/JGF/study/socket#

2、使用tcpdump工具分析

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

#tcpdump 'port 20000' -i lo -S&

见我的运行结果:


点击(此处)折叠或打开

  1. root@jgf:~/JGF/study/socket# tcpdump 'port 20000' -i lo -S&
  2. [3] 24667
  3. root@jgf:~/JGF/study/socket# tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  4. listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes

  5. root@jgf:~/JGF/study/socket# ./server&
  6. [4] 24671
  7. root@jgf:~/JGF/study/socket# ./client localhost
  8. connect from client: IP: 127.0.0.1, Port: 34585
  9. 11:22:00.051701 IP localhost.localdomain.34585 > localhost.localdomain.20000: Flags [S], seq 3563066036, win 32792, options [mss 16396,sackOK,TS val 153765034 ecr 0,nop,wscale 6], length 0
  10. 11:22:00.052161 IP localhost.localdomain.20000 > localhost.localdomain.34585: Flags [S.], seq 3565563744, ack 3563066037, win 32768, options [mss 16396,sackOK,TS val 153765034 ecr 153765034,nop,wscale 6], length 0
  11. 11:22:00.052337 IP localhost.localdomain.34585 > localhost.localdomain.20000: Flags [.], ack 3565563745, win 513, options [nop,nop,TS val 153765034 ecr 153765034], length 0
  12. 11:22:00.053530 IP localhost.localdomain.20000 > localhost.localdomain.34585: Flags [P.], seq 3565563745:3565564000, ack 3563066037, win 512, options [nop,nop,TS val 153765034 ecr 153765034], length 255
  13. 11:22:00.053604 IP localhost.localdomain.20000 > localhost.localdomain.34585: Flags [F.], seq 3565564000, ack 3563066037, win 512, options [nop,nop,TS val 153765034 ecr 153765034], length 0
  14. 11:22:00.054259 IP localhost.localdomain.34585 > localhost.localdomain.20000: Flags [.], ack 3565564000, win 530, options [nop,nop,TS val 153765034 ecr 153765034], length 0
  15. frome server localhost:
  16. welcome to our server.timestamp on server:Sat Mar 31 11:22:00 2012
  17. 11:22:00.055626 IP localhost.localdomain.34585 > localhost.localdomain.20000: Flags [F.], seq 3563066037, ack 3565564001, win 530, options [nop,nop,TS val 153765035 ecr 153765034], length 0
  18. 11:22:00.055719 IP localhost.localdomain.20000 > localhost.localdomain.34585: Flags [.], ack 3563066038, win 512, options [nop,nop,TS val 153765035 ecr 153765035], length 0
  19. [4] Done ./server
  20. root@jgf:~/JGF/study/socket#
下面我们逐条进行分析:

(1).客户端通过34585端口向服务端的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,就是发送数据的意思。




阅读(1376) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~