socket原理详解 (3)

这三个参数和bind的三个参数类型一直,只不过此处strcut sockaddr表示对端公开的地址。三个参数都是传入参数。connect顾名思义就是拿来建立连接的函数,只有像tcp这样面向连接、提供可靠服务的协议才需要建立连接

3.4、listen接口

int listen(int sockfd, int backlog)

告知内核在sockfd这个描述符上监听是否有连接到来,并设置同时能完成的最大连接数为backlog。3.6节还会继续解释这个参数。当调用listen后,内核就会建立两个队列,一个SYN队列,表示接受到请求,但未完成三次握手的连接;另一个是ACCEPT队列,表示已经完成了三次握手的队列

sockfd 是调用socket()函数创建的socket描述符

backlog 已经完成三次握手而等待accept的连接数

关于backlog , man listen的描述如下:

The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog. When syncookies are enabled there is no logical maximum length and this setting is ignored. See tcp(7) for more information.

If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn, then it is silently truncated to that value; the default value in this file is 128. In kernels before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.

3.5、accept接口

int accept(int listen_sockfd, struct sockaddr *addr, socklen_t *addrlen)

这三个参数与bind的三个参数含义一致,不过,此处的后两个参数是传出参数。在使用listen函数告知内核监听的描述符后,内核就会建立两个队列,一个SYN队列,表示接受到请求,但未完成三次握手的连接;另一个是ACCEPT队列,表示已经完成了三次握手的队列。而accept函数就是从ACCEPT队列中拿一个连接,并生成一个新的描述符,新的描述符所指向的结构体so_pcb中的请求端ip地址、请求端端口将被初始化。

从上面可以知道,accpet的返回值是一个新的描述符,我们姑且称之为new_sockfd。那么new_sockfd和listen_sockfd有和不同呢?不同之处就在于listen_sockfd所指向的结构体so_pcb中的请求端ip地址、请求端端口没有被初始化,而new_sockfd的这两个属性被初始化了。

3.6、listen、connect、accept流程及原理

以AF_INET,SOCK_STREAM,IPPROTO_TCP三个参数实例化的socket为例,通过一个副图来讲解这三个函数的工作流程及粗浅原理(图片改自)

       

socket原理详解

                           图6 listen、accept、connect流程及原理图

服务器端在调用listen之后,内核会建立两个队列,SYN队列和ACCEPT队列,其中ACCPET队列的长度由backlog指定。

服务器端在调用accpet之后,将阻塞,等待ACCPT队列有元素。

客户端在调用connect之后,将开始发起SYN请求,请求与服务器建立连接,此时称为第一次握手。

服务器端在接受到SYN请求之后,把请求方放入SYN队列中,并给客户端回复一个确认帧ACK,此帧还会携带一个请求与客户端建立连接的请求标志,也就是SYN,这称为第二次握手

客户端收到SYN+ACK帧后,connect返回,并发送确认建立连接帧ACK给服务器端。这称为第三次握手

服务器端收到ACK帧后,会把请求方从SYN队列中移出,放至ACCEPT队列中,而accept函数也等到了自己的资源,从阻塞中唤醒,从ACCEPT队列中取出请求方,重新建立一个新的sockfd,并返回。

这就是listen,accept,connect这三个函数的工作流程及原理。从这个过程可以看到,在connect函数中发生了两次握手。

更加详细的accept建立连接流程及原理请移步下面这个博文,该博文博主是个大牛,讲解的通熟易懂并且有深度:

3.7、发送消息接口

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

这几个接口都比较好理解,查一下man pages就知道什么含义了,man pages中讲解的非常清楚。这里只说一下flags参数,也是摘抄自man pages。

flags:

MSG_DONTWAIT (since Linux 2.2),不阻塞

Enables nonblocking operation; if the operation would block, EAGAIN or EWOULDBLOCK is returned (this can also be enabled using
the O_NONBLOCK flag with the F_SETFL fcntl(2)).

MSG_DONTROUTE,数据包不允许通过网关

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjsxd.html