socket编程API具体讨论-listen,accept

在socket的基础API中,有一个函数是listen,有一个函数是accept

#include <sys/socket.h>
int listen(int fd, int backlog);
参数backlog表示内核监听队列的最大长度

int accept(int fd, struct sockaddr *addr, socklen_t *addrlen);

这里,调用该函数,系统为该socket会创建一个监听队列,监听队列的长度由输入的backlog控制。也就是说,如果此时有客户端需要连接进来,那么服务器将会生成一个对应的socket放在监听队列里面,然后等待服务器使用accept提取监听队列中的处于establish状态的socket出来。
这里就有几个疑问点,

  • backlog是否完全对应着监听队列的长度?超过了这个长度,多余的socket,内核怎么处理?是丢弃?还是别的?
  • backlog范围内的socket的状态,肯定是establish吗?如果这个时候客户端直接挂掉,服务器还会保持establish吗?
  • 服务器使用accept连接的,一定是处于establish状态的socket吗?这个和第二个问题类似。

下面先讨论第一个问题:

static bool stop = false;

static void signal_deal(int sig)
{
    stop = true;
}

int main(int argc, char* argv[])
{
    signal(SIGTERM, signal_deal);
    int fd = ::socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
        exit(0);
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(5050);
    if (1 != inet_pton(AF_INET, "192.168.196.130", &addr.sin_addr))
        exit(0);
    if (0 != ::bind(fd, (const sockaddr*)&addr, sizeof(addr)))
        exit(0);
    if (0 != listen(fd, 5))
        exit(0);
    while (!stop)
    {
        sleep(1);
    }
    close(fd);
    return 0;
}

我在虚拟机上,执行这个程序。然后在window上使用telnet来连接这个服务器。然后使用netstat -nt来查看这个连接状况。
先说结论:在window上,telnet的次数多的超过了6次后,后面的socket处于SYN_SEND状态。在虚拟机上,telnet超过6次后,后面的socket不做任何处理。也就是说,超过backlog+1个数的socket都不会进行三次握手的完整过程。他们都在等待服务器调用accept从监听队列中提取socket出来后,等待补上。
在这里插入图片描述window上的
在这里插入图片描述
配合TCP状态转换图,可以看到,后面的socket都处于SYN_SEND状态,正等待着三次握手的进一步完成达到ESTABLISHED状态
在这里插入图片描述

下面考虑第二个问题?我先telnet一次,然后关闭,然后查看虚拟机服务器上的状态.
当第一个telnet 192.168.196.130 5050发出后,调用netstat可以查看到有一个socket在监听队列中,处于ESTABLISHED状态。接下来直接强制关闭掉这个客户端。查看结果。
在这里插入图片描述
这个监听队列,会一直保持着socket的当前状态,即使处于CLOSE_WAIT,掉线了,也不会将它移除出去。
那么就很好解释第三个问题了,使用accept是有可能从listen监听队列中提取到属于close_wait状态的,不一定是ETABLISHED的。
了解基础api的时候,应该配合TCP基本原理进行学习,这样才能深入了解这些基础函数做了什么,内核配合这些函数干了什么事?出现了问题的时候才好排查。

发布了78 篇原创文章 · 获赞 16 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/zeqi1991/article/details/98304761
今日推荐