# 使用listen()和accept()函数

对于服务器端程序，使用 bind() 绑定套接字后，还需要使用 listen() 函数让套接字进入被动监听状态，再调用 accept() 函数，就可以随时响应客户端的请求了。

## listen() 函数

在 Linux 下使用 `<sys/socket.h>`头文件中 listen() 函数可以让套接字进入被动监听状态，它的原型为：

```c
int listen(int sock, int backlog);
```

sock 为需要进入监听状态的套接字，backlog 为请求队列的最大长度。

> 第二个参数backlog 是队列的大小，等于未完成连接队列(正在等待完成相应的TCP三路握手过程) + 已完成连接队列(已经完成相应的TCP三路握手过程)

所谓被动监听，是指当没有客户端请求时，套接字处于“睡眠”状态，只有当接收到客户端请求时，套接字才会被“唤醒”来响应请求。

> 转换成监听状态之后，才能够接受连接
>
> 调用完listen函数后，这个套接字就变成了被动套接字，否则默认是主动套接字
>
> 主动套接字是用来发起连接的，connect
>
> 被动套接字是用来接受连接的， accept

### 请求队列

当套接字正在处理客户端请求时，如果有新的请求进来，套接字是没法处理的，只能把它放进缓冲区，待当前请求处理完毕后，再从缓冲区中读取出来处理。如果不断有新的请求进来，它们就按照先后顺序在缓冲区中排队，直到缓冲区满。这个缓冲区，就称为请求队列（Request Queue）。

缓冲区的长度（能存放多少个客户端请求）可以通过 listen() 函数的 backlog 参数指定，但究竟为多少并没有什么标准，可以根据你的需求来定，并发量小的话可以是10或者20。

如果将 backlog 的值设置为 SOMAXCONN，就由系统来决定请求队列长度，这个值一般比较大，可能是几百，或者更多。

图示：

![](https://313308022-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30T9rtIVtc5eWQf%2F-LfnT9dAhhstiSPRKKVD%2F-LfnTGSDK9E0JF09EgZ6%2Fsocket_listen.jpg?generation=1558862961221550\&alt=media)

一旦调用了accept返回后，将这个条目从已完成连接队列移除，以便有更多的客户端能够发起连接。

## accept() 函数

当套接字处于监听状态时，可以通过 accept() 函数来接收客户端请求。它的原型为：

```c
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
```

它的参数与 listen() 和 connect() 是相同的：sock 为服务器端套接字，addr 为 sockaddr\_in 结构体变量，addrlen 为参数 addr 的长度，可由 sizeof() 求得。

> 从已完成连接队列返回第一个连接，如果已完成连接队列为空，则阻塞。
>
> 填充对方的地址，和对方的信息
>
> 成功返回已连接套接字，并变为主动套接字

**注意: accept() 返回一个新的套接字来和客户端通信，addr 保存了客户端的IP地址和端口号，而 sock 是服务器端的套接字，注意区分。**

后面和客户端通信时，要使用这个新生成的套接字，而不是原来服务器端的套接字。

最后需要说明的是：listen() 只是让套接字进入监听状态，并没有真正接收客户端请求，listen() 后面的代码会继续执行，直到遇到 accept()。accept() 会阻塞程序执行（后面代码不能被执行），直到有新的请求到来。
