想了解listen()accept()的新动态吗?本文将为您提供详细的信息,此外,我们还将为您介绍关于#TCP你学得会#之当listen和accept遇到fork、(OS64)指定的网络名不再可用,w
想了解listen( ) accept( )的新动态吗?本文将为您提供详细的信息,此外,我们还将为您介绍关于#TCP 你学得会# 之 当 listen 和 accept 遇到 fork、(OS 64)指定的网络名不再可用,winnt_accept: Asynchronous AcceptEx failed.、.net – HttpListener.Start()Vista中的AccessDenied错误、AAAI 2020 Accepted 论文 list 及最佳论文奖的新知识。
本文目录一览:- listen( ) accept( )
- #TCP 你学得会# 之 当 listen 和 accept 遇到 fork
- (OS 64)指定的网络名不再可用,winnt_accept: Asynchronous AcceptEx failed.
- .net – HttpListener.Start()Vista中的AccessDenied错误
- AAAI 2020 Accepted 论文 list 及最佳论文奖
listen( ) accept( )
服务器端,创建socket,bind绑定套接字后,还需要使用listen()函数让套接字进入被动监听状态,再调用accept()函数,就可以随时响应客户端的请求了
listen() 函数
通过 listen() 函数可以让套接字进入被动监听状态
int listen(int sock,int backlog); //Linux
sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。
缓冲区的长度(能存放多少个客户端请求)可以通过 listen() 函数的 backlog 参数指定,可以根据你的需求来定,并发量小的话可以是10或者20。
如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误,对于 Windows,客户端会收到 WSAECONNREFUSED 错误。
注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数。
accept() 函数
当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。它的原型为:
int accept(int sock,struct sockaddr *addr,socklen_t *addrlen); //Linux
sock 为服务器端套接字
addr 为 sockaddr_in 结构体变量(使用强制转化为 sockaddr类型的,在前面学bind中有)
addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。最后需要说明的是:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
#TCP 你学得会# 之 当 listen 和 accept 遇到 fork
在经典的 TCP/IP 网络编程书籍中都介绍过这样一种模型:
“服务器在某知名端口监听,并 fork 若干子进程,当有新的连接请求到来时在子进程中通过 accept 调用获取新连接并进行处理”;
听起来一切顺理成章,但仔细想想就会有很多疑问,比如 “父子进程属于两个不同的进程空间,父进程中监听的端口如何在子进程中 accept?”;
另外网上还有一些讨论,比如 “多个进程在同一个描述符上 accept 时会产生 “惊群” 效应”;
一切又扑朔迷离起来了。
本文将以此为背景,通过实践和源码相结合的方式来一探究竟。
本文所采用的服务器模型如下:
int main(int argc, char *argv[]){
socket();
bind();
listen();
fork();
if( parent ){
accept();
}
else if( child ){
accept();
}
else{
/*error*/
}
return 0;
}
这里比文章开头介绍的架构更进一步,我们在父进程中也调用了 accept (),看看是个什么情形。
首先,启动服务器:
$ ps -ef | grep server
yyy 6182 3573 0 18:08 pts/3 00:00:00 ./tcp_server_tem
yyy 6183 6182 0 18:08 pts/3 00:00:00 ./tcp_server_tem
$ sudo netstat -antp | grep 54321
tcp 0 0 192.168.31.162:54321 0.0.0.0:* LISTEN 6182/tcp_server_tem
使用 ps 命令查看,父进程(6182)和子进程(6183)均已经正常启动,并且 netstat 命令中只显示了父进程(6182)监听在指定的端口上(54321)。
如果只有父进程在该端口上监听,那么子进程中是如何做到成功 accept 的呢?
我们知道,socket 的实质也是描述符,那么就深入进程所拥有的描述符表中看一下吧:
$ ls -l /proc/6182/fd
total 0
lrwx------ 1 yyy yyy 64 Feb 3 18:08 0 -> /dev/pts/3
lrwx------ 1 yyy yyy 64 Feb 3 18:08 1 -> /dev/pts/3
lrwx------ 1 yyy yyy 64 Feb 3 18:08 2 -> /dev/pts/3
lrwx------ 1 yyy yyy 64 Feb 3 18:08 3 -> socket:[50899]
$ ls -l /proc/6183/fd
total 0
lrwx------ 1 yyy yyy 64 Feb 3 18:08 0 -> /dev/pts/3
lrwx------ 1 yyy yyy 64 Feb 3 18:08 1 -> /dev/pts/3
lrwx------ 1 yyy yyy 64 Feb 3 18:08 2 -> /dev/pts/3
lrwx------ 1 yyy yyy 64 Feb 3 18:08 3 -> socket:[50899]
$ cat /proc/net/tcp | grep 50899
1: A21FA8C0:D431 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1001 0 50899 1 0000000000000000 100 0 0 10 -1
可以看到,虽然 netstat 中没有显示,但其实父子进程都拥有该监听 socket(子进程是通过 fork 时的描述符拷贝而从父进程中继承来的),并指向同一个节点(50899)。这样在父子进程中就都可以通过对应的描述符来操作内核中对应的同一个 sock 对象了。
好了,下面启动客户端来看一下效果(测试中使用的客户端和服务器均跑在同一台物理机器上):
server 6183 accept clientsock 4
server 6183 recv zero
server 6183 now do accept!
server 6182 accept clientsock 4
server 6182 recv zero
server 6182 now do accept!
server 6183 accept clientsock 4
server 6183 recv zero
server 6183 now do accept!
server 6182 accept clientsock 4
server 6182 recv zero
server 6182 now do accept!
神奇,父子进程都能够通过 accept 获取新连接,并且看起来还是交替进行的。
那么 accept 函数究竟是如何实现的呢,还是得要去协议栈的源码里面扒一扒才行啊。
Kernel 3.16.1。
TCP/IP 协议栈中,accept 系统调用对应的实现函数是 inet_csk_accept:
net/ipv4/inet_connection_sock.c
/*
* This will accept the next outstanding connection.
*/
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct request_sock_queue *queue = &icsk->icsk_accept_queue;
struct sock *newsk;
struct request_sock *req;
int error;
lock_sock(sk);
/* We need to make sure that this socket is listening,
* and that it has something pending.
*/
error = -EINVAL;
if (sk->sk_state != TCP_LISTEN)
goto out_err;
/* Find already established connection */
if (reqsk_queue_empty(queue)) {
long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
/* If this is a non blocking socket don''t sleep */
error = -EAGAIN;
if (!timeo)
goto out_err;
error = inet_csk_wait_for_connect(sk, timeo);
if (error)
goto out_err;
}
req = reqsk_queue_remove(queue);
newsk = req->sk;
sk_acceptq_removed(sk);
if (sk->sk_protocol == IPPROTO_TCP && queue->fastopenq != NULL) {
spin_lock_bh(&queue->fastopenq->lock);
if (tcp_rsk(req)->listener) {
/* We are still waiting for the final ACK from 3WHS
* so can''t free req now. Instead, we set req->sk to
* NULL to signify that the child socket is taken
* so reqsk_fastopen_remove() will free the req
* when 3WHS finishes (or is aborted).
*/
req->sk = NULL;
req = NULL;
}
spin_unlock_bh(&queue->fastopenq->lock);
}
out:
release_sock(sk);
if (req)
__reqsk_free(req);
return newsk;
out_err:
newsk = NULL;
req = NULL;
*err = error;
goto out;
}
EXPORT_SYMBOL(inet_csk_accept);
如果调用时还没有可以 accept 的连接且使用了阻塞模式的话,则会进入 inet_csk_wait_for_connect 函数:
/*
* Wait for an incoming connection, avoid race conditions. This must be called
* with the socket locked.
*/
static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
{
struct inet_connection_sock *icsk = inet_csk(sk);
DEFINE_WAIT(wait);
int err;
/*
* True wake-one mechanism for incoming connections: only
* one process gets woken up, not the ''whole herd''.
* Since we do not ''race & poll'' for established sockets
* anymore, the common case will execute the loop only once.
*
* Subtle issue: "add_wait_queue_exclusive()" will be added
* after any current non-exclusive waiters, and we know that
* it will always _stay_ after any new non-exclusive waiters
* because all non-exclusive waiters are added at the
* beginning of the wait-queue. As such, it''s ok to "drop"
* our exclusiveness temporarily when we get woken up without
* having to remove and re-insert us on the wait queue.
*/
for (;;) {
prepare_to_wait_exclusive(sk_sleep(sk), &wait,
TASK_INTERRUPTIBLE);
release_sock(sk);
if (reqsk_queue_empty(&icsk->icsk_accept_queue))
timeo = schedule_timeout(timeo);
lock_sock(sk);
err = 0;
if (!reqsk_queue_empty(&icsk->icsk_accept_queue))
break;
err = -EINVAL;
if (sk->sk_state != TCP_LISTEN)
break;
err = sock_intr_errno(timeo);
if (signal_pending(current))
break;
err = -EAGAIN;
if (!timeo)
break;
}
finish_wait(sk_sleep(sk), &wait);
return err;
}
这里使用了等待队列来完成任务,并且注释中说的很清楚了,采用了 “wake-one” 的机制,不会发生 “whole herd”,也就是 “惊群” 的情况。
还有另外一种模型,就是 accept 之后再 fork,然后在父进程中关闭 accept 套接字,在子进程中关闭监听套接字,这样做的缺点在于 fork 系统调用的性能损耗,但好在现在的 fork 实现了 “copy-on-write” 机制,就不再展开说了。
马上就要过年放假了,下一篇文章应该就是猴年了,祝大家新年快乐!
(OS 64)指定的网络名不再可用,winnt_accept: Asynchronous AcceptEx failed.
<IfModule mpm_winnt.c> ThreadsPerChild 150 MaxRequestsPerChild 10000 Win32disableAcceptEx </IfModule>
.net – HttpListener.Start()Vista中的AccessDenied错误
class Program { static void Main(string[] args) { HttpListener listener = new HttpListener(); listener.Prefixes.Add("http://myip:8080/app/"); listener.Start(); //.... and so on } }
我继续使用netsh(netsh http show list uri)添加了uri
netsh http add urlacl url=http://+:8080/app user=domain\user
仍然得到相同的错误.添加ACL确实适用于其他项目(他们没有使用HttpListener).我尝试了多个端口/应用程序名称组合,没有任何作用.
任何想法可能是什么原因?
在Vista上运行.Net 3.5 SP1
如果在代码中我指定了其中一个ips(就像我上面提到的那样)
listener.Prefixes.Add("http://myip1:8080/app/");
那么为了避免异常,我需要注册它与IP绑定弱通配符
netsh http add urlacl url=http://myip1:8080/app user=domain\user
但是,如果我添加前缀与强通配符(加号)
listener.Prefixes.Add("http://+:8080/app/");
并使用相同的通配符进行注册
netsh http add urlacl url=http://+:8080/app user=domain\user
那么没有错误,我可以从两个ip访问我的应用程序.
AAAI 2020 Accepted 论文 list 及最佳论文奖
AAAI 2020 论文 list 可以从下面链接下载:
https://aaai.org/Conferences/AAAI-20/wp-content/uploads/2020/01/AAAI-20-Accepted-Paper-List.pdf
最佳论文奖:
题目:WinoGrande: An Adversarial Winograd Schema Challenge at Scale
机构:艾伦人工智能研究院、华盛顿大学
论文链接:https://arxiv.org/abs/1907.10641
最佳论文奖提名奖:
题目:A Unifying View on Individual Bounds and Heuristic Inaccuracies in Bidirectional Search
机构:Riken 高级智能项目中心(Center for Advanced Intelligence Project)、奥克兰大学
论文链接:
https://ai.dmi.unibas.ch/research/reading_group/alcazar-et-al-aaai2020.pdf
最佳学生论文奖:
题目:Fair Division of Mixed Divisible and Indivisible Goods
机构:南洋理工大学、清华大学、香港大学
论文链接:https://arxiv.org/pdf/1911.07048.pdf
最佳学生论文奖提名奖:
题目:Lifelong Learning with a Changing Action Set
机构:马萨诸塞大学阿默斯特分校、Adobe Research
论文链接:https://arxiv.org/abs/1906.01770
特别的杰出论文奖:
题目:A Distributed Multi-Sensor Machine Learning Approach to Earthquake Early Warning
机构:雷恩大学、罗格斯大学、俄勒冈大学
论文链接:https://hal.archives-ouvertes.fr/hal-02373429v2/document
蓝天创意奖:蓝天创意奖用来鼓励哪些能够激发学界探索新方法、新思路的研究工作。
第一名:Back to the Future for Dialogue Research
第二名:AI for Explaining Decisions in Multi-Agent Environments
第三名:Unveiling Hidden Intentions
我们今天的关于listen( ) accept( )的分享已经告一段落,感谢您的关注,如果您想了解更多关于#TCP 你学得会# 之 当 listen 和 accept 遇到 fork、(OS 64)指定的网络名不再可用,winnt_accept: Asynchronous AcceptEx failed.、.net – HttpListener.Start()Vista中的AccessDenied错误、AAAI 2020 Accepted 论文 list 及最佳论文奖的相关信息,请在本站查询。
本文标签: