一、三次握手

                                             

                                                                             
        图1  tcp的三次握手

在上图中需要注意几点:

1、序列号:其范围在0~(2**32-1)之内,并且可循环利用,用以确定信息是安全且有序的传送。

2、ack存在的意义在于:确定是安全连接,序号未被劫持。

                                                            

                                                                             
  图2  网络变成核心API

结合图一和图二,有以下几点需要注意:

1、connect 触发三次握手
#include<sys/typs.h> #include<sys/socket.h> int connect(int sockfd, const
struct sockaddr*serv_addr,socklen_t addrlen);
        第一个参数 int sockfd确定了自身的IP和port,第二个参数 const struct sockaddr*serv_addr
告诉客户端服务器的IP和port。

2、能否自定义IP/PORT?

        通过bind函数,可以进行IP和PORT的设定。

3、阻塞与非阻塞的区别?

       
阻塞与非阻塞的产生都需要两个条件:一是系统调用;二是资源没有准备好。不同之处在于非阻塞在产生系统系统调用之后直接返回错误,而阻塞情况会一直在等待,直到资源准备好为止。

4、API 阻塞与否的原因分析。

       
(1)listen():为普通函数。原因在于内核为任何一个给定的监听套接字维护两个队列,即半连接队列(SYN请求连接的报文已有某个客户发送到达服务器,而服务器正在等待完成相应的TCP三次握手过程,这些套接字处于SYN_RCVD状态)和全连接队列(每个已经完成TCP三次握手的客户,这些套接字处于ESTABLISHED状态)。因此,没有等待资源的过程,所以属于普通函数。除此之外,该函数完成三次握手的第二次回包过程。

                                                      

                                                                            图3
 TCP三次握手和监听套接字的两个队列

        Q1:listen函数是否能实现非阻塞或阻塞函数,若能,论述其优缺点?

       
A:若listen为阻塞函数,则回包过程需要不断的在内核态和用户台之间切换,对于服务器而言是很大的开销。若listen为非阻塞函数,该函数完成三次握手的第二次回包过程。回包的过程是由内核来做,避免了用户态和内核态的切换,减轻服务器的开销。

        Q2:socket()、bind()、listen()在TCP协议栈内能否实现为一个API?

        A:可以,因为这三个函数都为普通函数。但是在编程过程中我们遵循函数式编辑,否则会带来参数过多的问题。分为三个函数来描述,也可以使逻辑更为详细。

       
(2)accept():用于从已完成连接队列头部返回下一个已完成连接,如果已完成连接队列为空,那么进程就投入睡眠(假定套接字为默认的阻塞方式)。如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。

        Q:accept为阻塞函数的原因?

        A:若accept()为非阻塞函数的话,每出现一个连接,就会进行一次调用,在多次调用中这些连接并未全部建立好,会造成资源的浪费。

二、四次挥手

                                                

                                                                             
        图4  四次挥手

        Q1:触发四次挥手的函数及其运行逻辑?

        A:close()触发四次挥手。close
一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字描述符不能再由调用进程使用,也就是说它不能再作为read或write的第一个参数,然而TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列。

         
 在多进程并发服务器中,父子进程共享着套接字,套接字描述符引用计数记录着共享着的进程个数,当父进程或某一子进程close掉套接字时,描述符引用计数会相应的减一,当引用计数仍大于零时,这个close调用就不会引发TCP的四路握手断连过程。 
  

        Q2:connect()是否为阻塞函数?

        A:connect()为阻塞函数,其等待三次握手。

        Q3:accept(sockfd,struct sockaddr *addr,addrlen)第二个参数的作用?

        A:这个addr是输出型参数,是一个指向struct
sockaddr结构的指针,里面存储着远程连接过来的计算机的信息,如IP地址和端口。其实质是向内核要客户端的IP和PORT.

        Q4:accept返回的fd与listen返回的fd是否为一个fd?

       
A:首先,listen后,是肯定占用了对应的端口号的,任何别的尝试使用该端口的操作肯定会报错,accept返回的fd若占用新的端口号,则接下来的读写操作无法进行。而这两个fd的五元组是通过远端的客户端的IP和PORT不同而加以区分的。

        Q5:返回的文件描述符上限是多少,如何调整上限。

       
A:为系统能达到的文件描述符的上限(默认是1024)减3,(0,1,2为系统占用,分别代表标准输入,标准输出和标准错误文件),该最大值可以通过命令Ulimit来实现调整。

        Q6:accept()返回的文件描述符的上限取决于全连接队列的上限?

        A:并不是。(待解决)

        Q7:处理百万连接必须准备的有哪些?

        A:1、listen的两个连接队列尽可能大;2、accept返回的个数尽可能多;3、连接五元组尽量使内核内存消耗到最低、。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信