1.errno
errno 是记录系统的最后一次错误代码,只有当一个库函数调用失败时,errno才会被设置。
常用的errno:
EINTR 系统调用中断
ETIMEOUT 连接超时
2.read/write ( send/receive) 原理
属于文件I/O,是带有缓冲区的操作函数。通过切换 fd 的阻塞标志(nonblock)转换阻塞与非阻塞。
write函数先将要发送的数据放在进程缓冲区中,然后向socket的发送缓冲区进行拷贝,这时可能出现进程缓冲区的数据量大于发送缓冲区所能接受的范围,若此时在阻塞模式,应用进程将挂起,(此时内核不会返回write函数)直到进程缓冲区的数据都拷贝到发送缓冲区,而write函数正常返回只能说明数据已完全被发送缓冲区接受。若处于非阻塞模式,此时write操作将会失败,内核返回EAGAIN错误。
对于每个socket,都有自己的发送缓冲区和接收缓冲区,发送缓冲区里的数据发出后,不会马上清除该数据,而要等到接收到对方的ACK后才清除这部分数据。接收端将收到的数据暂存到接收缓冲区中,自动进行确认。阻塞形成的原因一般是:接收端不及时从接收缓冲区取出数据,导致接收缓冲区填满,由于TCP的滑动窗口和阻塞控制,阻止发送端发送缓冲区发送数据,发送缓冲区填满,导致阻塞。
而read阻塞原因则是,发送端的数据没有到达,从接收缓冲区到进程缓冲区拷贝阻塞。
3.TCP三次握手和四次挥手原理
I.三次握手:客户端向服务器端发送SYN J 请求(包含将要发送数据的初始序列号),这时客户端在connect()函数阻塞。服务器端接收到SYN J,向客户端发出自己的SYN K 和 ACK J+1,此时服务器端accept()函数阻塞。
客户端接收到服务端报文,connect()函数成功返回,并发送ACK K+1,服务器端接收,accept()成功返回。
II.四次挥手,TCP断开连接时。客户端应用程序先调用close, 主动关闭。向对端发送一个 FIN M分节,进入FIN_WAIT_1状态,服务端接收到 FIN M ,表示它将接受不到客户端额外数据,进入CLOSE_WAIT 状态,向客户端发送ACK M+1 ,客户端接收后,进入FIN_WAIT_2状态,等待服务端调用close。服务端发送FIN N,客户端接受到后,发送ACK N+1 ,进入最终的TIME_WAIT状态(一般1~4分钟,这里进入TIME_WAIT状态而不是直接关闭是因为若最后的ACK丢失,服务端将重发FIN, 需要客户端重发ACK,若直接退出,系统将回应RST,服务端会认为是一个错误)。
TCP关闭时,每一段都要发送一个FIN,这种情况在应用进程调用close、进程终止、进程被动终止时都会发生。
4.TCP协议下可能丢包问题
TCP能保证socket的发送端和接收端buffer的内容、顺序一致,而应用程序只需要考虑发送端和接收端read/write操作在进程缓冲区和socket缓冲区的顺序一致。
TCP网络波动或丢包,TCP连接时SYN中有序号,比如发送了100个包,第一个包丢失,那就先缓存着,API表现为什么都没收到(read函数读不到),等待让客户端重传,(若等了太长时间服务端就断开连接)。
5.epoll边缘触发ET和水平触发LT两种工作模式
边缘触发,只有当状态发生改变时才触发;水平触发,只要还有没处理的事件就会一直通知。
epoll的工作过程:epoll_wait 调用 ep_poll,当rdlist为空时(无 就绪 fd )挂起当前进程,直到 rdlist 不空时进程才被唤醒。文件 fd 状态改变 ,导致 fd 的回调函数 ep_poll_callback() 被调用。ep_poll_callback() 将相应 fd 的 epitem 加入到 rdlist ,进程被唤醒,epoll_wait 继续执行。ep_poll_transfer 函数将 rdlist 中的 epitem 拷贝到 txlist 中,并将rdlist清空。ep_send_events函数(很关键),它扫描txlist中的每个epitem,调用其关联fd对用的poll方法(图中蓝线)。此时对poll的调用仅仅是取得fd上较新的events(防止之前events被更新),之后将取得的events和相应的fd发送到用户空间(封装在struct epoll_event,从epoll_wait返回)。之后如果这个 epitem对应的 fd 是 LT(水平触发)且监听的event是用户关心的,则将其重新加入 rdlist ,而ET模式则不加。
当你需要写数据的时候,写了一半返回了EAGAIN,ET模式下,当再次返回EPOLLOUT时,继续写出待写出的数据,当没有数据要写出时,不处理直接略过即可。而LT模式则需要先打开EPOLLOUT,当没有数据需要写出时,再关闭EPOLLOUT(否则会一直返回EPOLLOUT事件)。