一:Posix API是什么
POSIX,全称为“可移植操作系统接口”(Portable Operating System Interface),最初由IEEE组织制定,目的是为了使不同的操作系统之间可以互相兼容。POSIX标准定义了一系列API和命令行工具,规定了操作系统应该提供哪些功能,以及这些功能的调用方式和行为。这些标准都是为了让不同的操作系统之间具有更好的兼容性,以便更方便地编写跨平台程序。
对于Posix API,这里做了详细介绍一文带你彻底搞懂posix - 知乎
二:网络编程中的Posix API
客户端 | 服务端 | |
1:socket() | 1:socket() | epoll_create() |
2:bind() | 2:bind() | epoll_ctl() |
3:listent() | epoll_wait() | |
3:connect() | 4:accept() | |
4:send() | 5:recv() | |
5:recv() | 6:send() | |
6:close() | 7:close() |
-
关于socket
//函数原型
int socket(int domain, int type, int protocol);
socket,中文翻译为插座,表示一一对应的一个关系;程序调用socket时,会分配一个fd,同时根据传入的网络协议、通信方式创建对应的TCB(TCP control block)\UDPCB(udp control block),创建的控制块包含TCP\UDP协议的所有状态信息和控制信息,如源IP地址、源端口、目的IP地址、目的端口、传输层协议、序列号、确认号、窗口大小、拥塞控制等。这些信息将用于实现TCP协议的行为和进行网络通信的控制。
-
关于bind
//函数原型
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
bind函数与tcp协议无关,该函数在与将传入的网络参数和fd对应的控制块绑定;简单来讲相当于一个set设置过程,bind根据传入的fd找到与之对应的控制块,而后将五元组(源IP地址、源端口、目的IP地址、目的端口、传输层协议)信息设置到对应的控制块中。
在客户端,如果没有进行bind处理,程序会默认使用0.0.0.0基任意ip地址都可以作为源地址,同时分配一个任意port端口,此端口值在我们设置的设置的范围中。
//sysctl.conf文件中添加以下命令,控制端口范围
net.ipv4.ip_local_port_range = <起始端口号> <结束端口号>
-
关于listen,3次握手建立连接
//函数原型
int listen(int sockfd, int backlog);
backlog参数理解:(避免syn泛洪)
1:syn半连接队列的长度;(避免syn泛洪、dos攻击,但变的鸡肋(堡垒机等存在),当前在接收数据之前会做隔离,不用在考虑syn泛洪问题)
根据下图TCP三次握手,此时考虑的是大量conect连接介入,而不做后续处理,此时会导致大量节点堆积在syn队列中(syn泛洪);
2:syn+accept队列总长度,未被分配fd的tcb的数量;
若accept队列中不做处理,会造成大量连接堆积,此时可以避免此种情况发生。
3:描述accept全连接队列长度。
listen函数功能:
1:将tcb(tcp控制块)中的状态修改为TCP_STATUS_LISTEN;
2:分配一个syn(半连接)队列;
3:分配一个accept(全连接)队列。
acknum=syn seqnum+1,表示在acknum之前的所有数据都接收到了。
当客户端有链接过来时,会将传过来的tcb控制块作为节点存放到syn队列当中,当经过accept建立完连接之后会根据tcp五元组信匹配到syn队列中的tcb移动到accept队列当中,而后调用accept将节点从全连接队列中移除。
-
关于connect
//函数原型
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
connect函数就是发起连接请求,内核接收到请求会建立tcp3次握手连接。
-
关于send、recv
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
通过参数sockfd找到特定的网络通道进行数据接收和发送
如上图所示,send函数将数据从用户端拷贝到内核中,内核中何时发送数据应用层无法控制;在应用层调用3次send,可能在内核中会将这3次的数据作为一个数据包发送;
同理,recv也只是将数据从内核中拷贝到应用层。
-
关于accept
SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
1:调用accept之后,内核会根据传入的tcp五原则(ip信息参数)取出accept全连接队列中与之相匹配的节点。
2:从全连接队列中取出一个节点后,内核会为这个节点创建一个socket,这个新的套接字就是服务器和指定客户端的专属通道;
3:为新创建的套接字返回一个fd文件描述符。
-
关于close,四次挥手结束链接