免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: zhanghua111
打印 上一主题 下一主题

断开的TCP连接为什么还存在ESTABLISHED? [复制链接]

论坛徽章:
0
11 [报告]
发表于 2005-11-07 11:14 |只看该作者
上面的哥们,我想问你们2个问题:
1,为什么client断了,server就能知道?,(另client可能是网络断、CTRL+C关闭进程等)
2,可以显示established,和你的多线程有什么关系?

这个完全和网络原理,与操作系统协议栈的处理有关。

1,无论是哪端断,都有可能出现可以显示established的现象。
    1.1 client断,server端不可能立即知道,它只有通过TCP的保活定时器判断连接是否断。
          需要注意的是,socket里recv调用就原理来说应是应用层的网络活动,recv在指定时间内
          没有收到数据,并不代表TCP的连接已断。
    1.2 server断,操作系统连接信息也是定时更新的。可想而知,
          netstat 显示的也不是即时的信息。


有不对的地方,烦请哥们告之。

[ 本帖最后由 wangshim_ 于 2005-11-7 11:19 编辑 ]

论坛徽章:
0
12 [报告]
发表于 2005-11-07 11:28 |只看该作者
to wangshim_ :
1.我理解的没你这么深,我只是就程序的实现而言,当我select()到该fd可读并且read()的返回是0的时候,就认为该连接是断开了,对于你说的网络断和进程退出都一样.
2.我并没有说established和是否多线程有关系啊,提到多线程,是因为LZ说他是子线程处理连接,并且停在read().

论坛徽章:
0
13 [报告]
发表于 2005-11-07 11:30 |只看该作者
to wangshim_ :
顺便问一下,如果你写server,你怎么来判断client断开了连接呢?

论坛徽章:
0
14 [报告]
发表于 2005-11-07 13:05 |只看该作者
加状态控制.如果还要考虑server压力的话,加hearbeat探测(fraom server to client).

论坛徽章:
0
15 [报告]
发表于 2005-11-07 13:24 |只看该作者
据我所知,心跳一般是由client发给server的吧.来检测闲时长连接是否有效.

论坛徽章:
0
16 [报告]
发表于 2005-11-07 14:37 |只看该作者
client崩溃的现象,叫半打开连接。
心跳机制当然可以用,心跳一般还兼做其它用途,所以一般是client向server主动发送心跳数据。
我想说的是用TCP的保活定时器(keepalive 选项):
他的原理是每隔一定时间向client发送数据,例如“hello,world”,如果能收到ack应答,
就说明对方还处于连接中.他有个优点就是不会影响TCP的正常数据传输。

select等这些东西的应用,说实话,我是对他们的原理不清楚,还请各位赐教。

论坛徽章:
0
17 [报告]
发表于 2005-11-07 17:07 |只看该作者
呵呵. wangshim_ 兄,很高兴和你讨论这个问题.

先说一些题外话.
我对网络编程不怎么熟悉.接触socket是在几个月前.
我参加的项目中有一个小模块是个消息接收处理模块.所以我边学习边写了一个多线程tcp server.
当时处理server如何知道clinet退出这个问题, 我是参考了网上的资料(具体是哪里的文章我忘记了)并且做了测试后,
用的判断方法是:server时刻关注连接的fd,如果select()到该fd可读但是read()的返回是0的话,那么就认为client主动断开了连接.
具体原理我没有时间,同时也觉得没有必要去了解.在具体开发中,我认为会用select()或许比精通TCP协议更有用,你觉得呢?
现在由于这个帖子,我对其中的细节比较有兴趣了,所以看了一些资料,补充学习了一下.

下面看一下一个连接断开的过程.

为了理解后面的步骤.需要先贴一些代码上来.这里可以先跳过这些代码.
(代码是<Unix Network Programming Volume 1,Third Edition The Sockets Networking API>上直接拷过来的.)


  1. 这是server:

  2. 1 #include    "unp.h"

  3. 2 void
  4. 3 str_echo(int sockfd)
  5. 4 {
  6. 5     ssize_t n;
  7. 6     char    buf[MAXLINE];

  8. 7   again:
  9. 8     while ( (n = read(sockfd, buf, MAXLINE)) > 0)
  10. 9         Writen(sockfd, buf, n);

  11. 10     if (n < 0 && errno == EINTR)
  12. 11         goto again;
  13. 12     else if (n < 0)
  14. 13         err_sys("str_echo: read error");
  15. 14 }

  16. 2 int
  17. 3 main(int argc, char **argv)
  18. 4 {
  19. 5     int     listenfd, connfd;
  20. 6     pid_t   childpid;
  21. 7     socklen_t clilen;
  22. 8     struct sockaddr_in cliaddr, servaddr;

  23. 9     listenfd = Socket (AF_INET, SOCK_STREAM, 0);

  24. 10     bzero(&servaddr, sizeof(servaddr));
  25. 11     servaddr.sin_family = AF_INET;
  26. 12     servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  27. 13     servaddr.sin_port = htons (SERV_PORT);

  28. 14     Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

  29. 15     Listen(listenfd, LISTENQ);

  30. 16     for ( ; ; )  {
  31. 17         clilen = sizeof(cliaddr);
  32. 18         connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

  33. 19         if ( (childpid = Fork()) == 0) { /* child process */
  34. 20             Close(listenfd);    /* close listening socket */
  35. 21             str_echo(connfd);   /* process the request */
  36. 22             exit (0);
  37. 23         }
  38. 24         Close(connfd);          /* parent closes connected socket */
  39. 25     }
  40. 26 }
复制代码


  1. 这是client:

  2. 1 #include    "unp.h"

  3. 2 void
  4. 3 str_cli(FILE *fp, int sockfd)
  5. 4 {
  6. 5     char    sendline[MAXLINE], recvline[MAXLINE];

  7. 6     while (Fgets(sendline, MAXLINE, fp) != NULL) {

  8. 7         Writen(sockfd, sendline, strlen (sendline));

  9. 8         if (Readline(sockfd, recvline, MAXLINE) == 0)
  10. 9             err_quit("str_cli: server terminated prematurely");

  11. 10         Fputs(recvline, stdout);
  12. 11     }
  13. 12 }

  14. 2 int
  15. 3 main(int argc, char **argv)
  16. 4 {
  17. 5     int     sockfd;
  18. 6     struct sockaddr_in servaddr;

  19. 7     if (argc != 2)
  20. 8         err_quit("usage: tcpcli <IPaddress>");

  21. 9     sockfd = Socket(AF_INET, SOCK_STREAM, 0);

  22. 10     bzero(&servaddr, sizeof(servaddr));
  23. 11     servaddr.sin_family = AF_INET;
  24. 12     servaddr.sin_port = htons(SERV_PORT);
  25. 13     Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

  26. 14     Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

  27. 15     str_cli(stdin, sockfd);     /* do it all */

  28. 16     exit(0);
  29. 17 }

  30. Create socket
复制代码


<Unix Network Programming Volume 1,Third Edition The Sockets Networking API.chm>
中关于一个连接正常断开过程的描述:

  1. client连上了server.client输入^D.

  2. 1.When we type our EOF character, fgets returns a null pointer
  3.   and the function str_cli returns.

  4. 2.When str_cli returns to the client main function,
  5.   the latter terminates by calling exit.
  6.   //步骤1和2告诉我们client退出

  7. 3.Part of process termination is the closing of all open descriptors,
  8.   so the client socket is closed by the kernel.
  9.   This sends a FIN to the server, to which the server TCP responds with an ACK.
  10.   //上面这句比较关键,我想这就是server select()的时候可读的原因
  11.   This is the first half of the TCP connection termination sequence.
  12.   At this point, the server socket is in the CLOSE_WAIT state
  13.   and the client socket is in the FIN_WAIT_2 state.

  14. 4.When the server TCP receives the FIN,
  15.   the server child is blocked in a call to readline,
  16.   //server开始读这个文件描述符
  17.   and readline then returns 0.
  18.   //但是缓冲区没有数据可读,所以返回0
  19.   This causes the str_echo function to return to the server child main.

  20. 5.The server child terminates by calling exit.

  21. 6.All open descriptors in the server child are closed.
  22.   The closing of the connected socket by the child causes the final two segments
  23.   of the TCP connection termination to take place: a FIN from the server to the client,
  24.   and an ACK from the client. At this point, the connection is completely terminated.
  25.   The client socket enters the TIME_WAIT state.

  26. 7.Finally, the SIGCHLD signal is sent to the parent when the server child terminates.
  27.   This occurs in this example, but we do not catch the signal in our code,
  28.   and the default action of the signal is to be ignored.
  29.   Thus, the child enters the zombie state. We can verify this with the ps command.
复制代码



以上还不能证明"select()到文件描述符可读但是read()返回0,就说明对方断开连接"是正确的.
因为还有一种情况,如果client发送的就是0字节的内容呢?于是我做了一个简单的测试,
发现write()0字节的时候server根本没有反应,不会select()到fd可读,或者就是说0字节是更本write()不过去的(本来就没有嘛).

还有一种情况,如果client是被kill -9了呢,没来得及close()文件描述符怎么办?
我想这种情况系统会自动close的吧.不知道是否正确.

所以,我的理解是:client断开,server是能够理解知道的,并且是根据"select()到文件描述符可读但是read()返回0"来判断.

对了,select()的功能大致就是告诉我们一个描述符是否已经准备好读,写或异常.

匆匆看了一些资料,没时间细想,还忘各位指正.

论坛徽章:
0
18 [报告]
发表于 2005-11-07 22:52 |只看该作者
原帖由 wangshim_ 于 2005-11-7 11:14 发表
上面的哥们,我想问你们2个问题:
1,为什么client断了,server就能知道?,(另client可能是网络断、CTRL+C关闭进程等)
2,可以显示established,和你的多线程有什么关系?

这个完全和网络原理,与操作系统 ...


问题1:

在一般的client/server处理模型中,服务器端一般都是在等待客户端的输入,在这个时候如果client端了,server端的socket的read都会返回-1,在这个时候我们就知道client已经断开了,我们可以close这个socket.

问题2:

TCP的确是使用保活定时器来实现keep-alive的,但是这个跟应用程序无关,他的数据根本不会到应用程序。

论坛徽章:
0
19 [报告]
发表于 2005-11-07 23:13 |只看该作者
除了wang说的部分正确以外,其他全错误.
client端在长连接情况下,如果没有write动作,那么session上没有数据流动,这种情况下,如果client crashed了,那么client 的os没有机会发送FIN报文,server是不知道client出事了,那么对应的server上的sock状态还是established 的,并且会保持这一状态,即使client又从新发起了一个连接请求(server上从新创建一个sockfd).
上面只是给出了其中的一种情况,类似的东西还有.

解决的办法就是对应每一个client,给它生成一个ID,并且将该client ID对应的信息存储在server上,当该client下一次发起请求建立完session之后,检测该client是不是上一次状态还存在,如果是的话,server上主动close上一个session,并清除上一次的status内容.

如果client不能预期什么时候动作的话,server应该定期检查所有status为已经连接,并且长时间没有动作的client的状态,具体的根据情况定,可以是server用heartbeat检测client.也可以是暴力拆除.在暴力拆除的情况下,可能会误伤client,那么client只需要redo一次,但是如果存在交易处理的情况,那么需要考虑rollback等情况.

没那么简单.

论坛徽章:
0
20 [报告]
发表于 2005-11-08 08:50 |只看该作者
楼上的请注意,我在17楼说的是 normal termination. 而不是 crashes. 请你说个"全错误"的理由.
至于 crashed 的情况, 我一会有时间也学习一下.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP