tcp/ip协议栈--socket API 之listen()
来源:互联网 发布:淘宝移动wi fi 编辑:程序博客网 时间:2024/05/16 08:34
0x01 缘由
上篇博文介绍了bind的连接,了解了相关细节,这章继续学习socket API ,这篇关注listen。listen几个关键参数和队列是经常被面试官提出的问题。
0x02 API介绍
全连接队列的最大长度:
backlog保存的是完成三次握手、等待accept的全连接,而不是半连接。
负载不高时,backlog不用太大。(For complete connections)
系统最大的、未处理的全连接数量为:min(backlog, somaxconn),net.core.somaxconn默认为128。
这个值最终存储于sk->sk_max_ack_backlog。
半连接队列的最大长度:
tcp_max_syn_backlog默认值为256。(For incomplete connections)
当使用SYN Cookie时,这个参数变为无效。
半连接队列的最大长度为backlog、somaxconn、tcp_max_syn_backlog的最小值。
0x03 单步跟踪分析
3.1 SYSCALL_DEFINE2
/* * 系统调用向量 */SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args){ unsigned long a[6]; unsigned long a0, a1; int err; unsigned int len; if (call < 1 || call > SYS_ACCEPT4) return -EINVAL; len = nargs[call]; if (len > sizeof(a)) return -EINVAL; /* 用户空间复制相关参数 */ if (copy_from_user(a, args, len)) return -EFAULT; audit_socketcall(nargs[call] / sizeof(unsigned long), a); a0 = a[0]; a1 = a[1]; /* 根据call子调用号,来处理socket相关调用状态。*/ switch (call) { ...... case SYS_LISTEN: /* a0 = 3,a1 = 20 是在server.c代码中设设置队列一样*/ err = sys_listen(a0, a1); break; ....... default: err = -EINVAL; break; } return err;}
3.2 SYSCALL_DEFINE2 listen
SYSCALL_DEFINE2(listen, int, fd, int, backlog){ struct socket *sock; int err, fput_needed; int somaxconn; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { /* * [root@B200-45 test]# sysctl -a | grep somaxconn * net.core.somaxconn = 128 */ somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; /*如果backlog大于somaxconn则默认为somaxconn。k可以通过调整相关参数来提高相关连接数*/ if ((unsigned)backlog > somaxconn) backlog = somaxconn; /*SELInux相关 */ err = security_socket_listen(sock, backlog); if (!err) /*如果tcp,调用inet_listen*/ err = sock->ops->listen(sock, backlog); /* 将相关参数放入*/ fput_light(sock->file, fput_needed); } return err;}
3.3 inet_listen
/* 启动监听时,做的工作主要包括: 1.创建半连接队列的实例,初始化全连接队列。 2.初始化sock的一些变量,把它的状态设为TCP_LISTEN。 3.检查端口是否可用,防止bind()后其它进程修改了端口信息。 4.把sock链接进入监听哈希表listening_hash中。*/int inet_csk_listen_start(struct sock *sk, const int nr_table_entries){ struct inet_sock *inet = inet_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); /* 初始化全连接队列,创建半连接队列的实例 */ int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries); if (rc != 0) return rc; /* 在返回inet_listen()时赋值 * sk->sk_max_ack_backlog = 0; sk->sk_ack_backlog = 0; /* icsk->icsk_ack c初始化清零 */ inet_csk_delack_init(sk); /* There is race window here: we announce ourselves listening, 此处有一个竞争窗口:我们宣告我们正在监听,但是这个事务仍然没有被get_port校验。 这是可以的,因为这个套接字只有在验证完成后才进入哈希表。 */ sk->sk_state = TCP_LISTEN; /* 把sock的状态置为LISTEN */ if (!sk->sk_prot->get_port(sk, inet->num)) { inet->sport = htons(inet->num); //源端口 sk_dst_reset(sk); /*要么把自己加入到 tcp_hashinfo 中的 ehash 中,要么加入到 listening_hash 中,这要根据 sk_state 的值来操作,如果是 LISTEN,就加入后者,如果是除 LISTEN 之外的值,那么就加入 ehash 表,我们会在研究 connect 的代码中看到。*/ sk->sk_prot->hash(sk);/* 把sock链接入监听哈希表中 */ return 0; } sk->sk_state = TCP_CLOSE; /* 如果端口不可用,则释放半连接队列 */ __reqsk_queue_destroy(&icsk->icsk_accept_queue); return -EADDRINUSE;}
相关可参考:http://blog.csdn.net/zhangskd/article/details/14446581
0x04 总结
关键参数backlog再次强调配张图:
阅读全文
0 0
- tcp/ip协议栈--socket API 之listen()
- tcp/ip协议栈--socket API 之socket()
- tcp/ip协议栈--socket API 之bind()
- tcp/ip协议栈--socket API 之accept()
- tcp/ip协议栈--socket API 之connect()
- tcp/ip协议栈--socket API 之发送函数(send/write)
- tcp/ip协议栈--socket API 之发送函数(read/recv)
- Linux网络协议栈之TCP socket/bind/listen/connect/accept/close/shutdown
- socket API和TCP/IP协议学习
- Linux TCP/IP协议栈之Socket的实现分析
- Linux TCP/IP 协议栈之 Socket的实现分析
- Socket编程之Tcp/Ip协议服务
- socket前篇之 tcp/ip协议
- TCP/IP协议栈 之 TCP协议
- TCP/IP协议栈 之 IP协议
- TCP/IP编程之listen函数详解
- socket从userspace到kernel的api执行过程(不含tcp/ip协议栈部分)
- LwIP tcp/ip socket编程listen函数分析
- 写给大家看的设计书学习笔记
- 【实践】基于Ceph打造高性能高可靠的分布式块存储系统
- 再探Direct3D流水线
- 支付宝小程序灰度测试、版本回滚能力新上线
- Linux实用命令大全
- tcp/ip协议栈--socket API 之listen()
- HDU
- Netty 实现HTTP文件服务器
- iOS Masonry布局获取Frame
- 一个简单的汇编程序
- unit 2 工作目录切换 文件建立 拷贝 删除以及正则表达式
- C++指定小数点位数输出
- os.walk
- 极乐技术周报(第二十八期)