前言
根据TCP编程模型中我们可以看到之前的socket
和bind
接口是tcp服务器在为接收客户端的链接做准备,保证tcp的面向字节流,面向连接的可靠通信服务正常进行。接下来的listen
端口则为我们进行三次握手与客户端进行链接的接口。
TCP编程模型如下
函数描述
#include <sys/socket.h>
int listen(int sockfd, int backlog);
- 函数功能:将套接字文件描述符从主动转为被动文件描述符,然后用于被动监听客户端的连接
- 函数返回值:成功返回0,失败返回-1, errno被设置
- 参数:
a.sockfd
表示socket创建的套接字文件描述符
b.backlog
指定队列的容量
这个队列用于记录正在连接但是还没有连接完成的客户端,一般设置队列的容量为2,3即可。队列的最大容量需要小于30
代码实例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
void print_err(char *str, int line, int err_no) {
printf("%d, %s :%s\n",line,str,strerror(err_no));
_exit(-1);
}
int main()
{
int skfd = -1, ret = -1;
skfd = socket(AF_INET, SOCK_STREAM, 0);
if ( -1 == skfd) {
print_err("socket failed",__LINE__,errno);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET; //设置tcp协议族
addr.sin_port = 6789; //设置端口号
addr.sin_addr.s_addr = inet_addr("192.168.102.169"); //设置ip地址
ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));
if ( -1 == ret) {
print_err("bind failed",__LINE__,errno);
}
/*将套接字文件描述符从主动转为被动文件描述符,然后用于被动监听客户端的连接*/
ret = listen(skfd, 3);
if ( -1 == ret ) {
print_err("listen failed", __LINE__, errno);
}
return 0;
}
TCP服务器为什么调用listen
因为连接请求只能由客户端发起,此时服务端的listen函数是将服务端的主动描述符转为被动描述符,否则无法用于监听客户端的连接。
TCP服务器监听客户端链接时,使用的是socket
返回的“套接字文件描述符”来实现的,但是这个文件描述符默认是主动文件描述符(主动向对方发送数据),所以需要使用listen
函数将其转换为被动描述符(只能被动得等待别人主动发送数据,再回应),否则无法用于被动监听客户端。