其他知识点
TIME_WAIT:主动关闭
CLOSE_WAIT:被动关闭
FIN:关闭连接
SYN:建立连接
ACK:响应
RST:连接重置
PSH:有DATA数据传输
2MSL:报文最大生存时间 MSL是一个数据报在网络中单向发出 到认定丢失的时间
TTL:生存时间
服务出现异常最常出现的状况
服务器保持大量的TIME_WAIT状态
服务器保持大量的CLOSE_WAIT状态
保持大量的time_wait状态
背景:爬虫服务器和web服务器器
解决问题:保证最后一个ACK能够正常传输
网络上仍然有可能有残余的数据包能够正常处理
TIME_WAIT原因:
假设最后的一个ACK丢失,那么被动关闭一方收不到这最后一个ACK则会重发FIN。此时主动关闭一方必须保持一个有效的(time_wait状态下维持)状态信息,以便可以重发ACK。如果主动关闭的socket不维持这种状态而是进入close状态,那么主动关闭的一方在收到被动关闭方重新发送的FIN时则响应给被动方一个RST。被动方收到这个RST后会认为此次回话出错了。所以如果TCP想要完成必要的操作而终止双方的数据流传输,就必须完全正确的传输四次握手的四步,不能有任何的丢失。这就是为什么在socket在关闭后,任然处于time_wait状态的第一个原因。因为他要等待可能出现的错误(被动关闭端没有接收到最后一个ACK),以便重发ACK。
假设目前连接的通信双方都调用了close(),双方同时进入closed的终结状态,而没有走 time_wait状态。则会出现如下问题:假如现在有一个新的连接建立起来,使用的IP地址与之前的端口完全相同,现在建立的一个连接是之前连接的完全复用,我们还假定之前连接中有数据报残存在网络之中,这样的话现在的连接收到的数据有可能是之前连接的报文。为了防止这一点。TCP不允许新的连接复用time_wait状态下的socket。处于time_wait状态的socket在等待2MSL时间后(之所以是两倍的MSL,是由于MSL是一个数据报在网络中单向发出 到认定丢失的时间,即(Maximum Segment Lifetime)报文最长存活时间,一个数据报有可能在发送途中或是其响应过程中成为残余数据报,确认一个数据报及其响应的丢弃需要两倍的MSL),将会转为closed状态。这就意味着,一个成功建立的连接,必须使得之前网络中残余的数据报都丢失了。
2.服务器保持了大量的close_wait()状态
time_wait问题可以通过调整内核参数和适当的设置web服务器的keep-Alive值来解决。因为time_wait是自己可控的,要么就是对方连接的异常,要么就是自己没有快速的回收资源,总之不是由于自己程序错误引起的。但是close_wait就不一样了,从上图中我们可以看到服务器保持大量的close_wait只有一种情况,那就是对方发送一个FIN后,程序自己这边没有进一步发送ACK以确认。换句话说就是在对方关闭连接后,程序里没有检测到,或者程序里本身就已经忘了这个时候需要关闭连接,于是这个资源就一直被程序占用着。这个时候快速的解决方法是:
关闭正在运行的程序,这个需要视业务情况而定。
尽快的修改程序里的bug,然后测试提交到线上服务器。
cpu密集型:线程数=cpu核数*2
IO密集型:线程数=cpu核心数/(1-阻塞系数)
mutex:递归(可重入)和非递归(不可重入)
RAII:利用对象生命周期管理程序资源(包括内存、文件句柄、锁等)的技术.
使用 RAII 时,一般在资源获得的同时构造对象, 在对象生存期间,资源一直保持有效;对象析构时,资源被释放。
关键:要保证资源的释放顺序与获取顺序严格相反
/*************************************************************************
@FileName: test.cc
@Author: zkm
@Email: zachary9121@gmail.com
@Time: Thu 04 Jul 2019 09:16:58 AM CST
************************************************************************/
pthread_onece
/*************************************************************************
@FileName: pthread_onece.cc
@Author: zkm
@Email: zachary9121@gmail.com
@Time: Wed 03 Jul 2019 06:00:17 PM CST
************************************************************************/
多线程服务端开发
进程间通信:TCP sockets
线程同步四项原则
使用互斥锁的条件变量,关键RAII技术
空悬指针:指向已经销毁的对象或已经回收的地址
野指针:没有初始化的指针SIGPIPE信号处理
SIGPIPE信号详解
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
解决方案:
TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道, 但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制, 一个端点无法获知对端的socket是调用了close还是shutdown.
对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.
为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:
close与shutdown的区别
10. shutdown()函数可以选择关闭全双工连接的读通道或者写通道,如果两个通道同时关闭,则这个连接不能再继续通信。close()函数会同时关闭全双工连接的读写通道,除了关闭连接外,还会释放套接字占用的文件描述符。而shutdown()只会关闭连接,但是不会释放占用的文件描述符。所以即使使用了SHUT_RDWR类型调用shutdown()关闭连接,也仍然要调用close()来释放连接占用的文件描述符。
多线程服务器模型-one loop per thread
此种模型下,程序的每个IO线程有一个event loop, 用于处理读写和定时事件。
*eventloop 代表线程的主循环,需要让哪个线程干活,就把timer或者IO channel注册到那个线程的loop里。
*对实时性有要求的connection可以独占一个线程
*对于数据量的可以独占一个线程,并把数据处理任务分到另几个计算线程中(用线程池)
优点:
* 服务器中线程数目基本固定,可以在程序启动时设置,不会频繁创建与销毁。
*可以方便在线程间调配负载。
accept和accpet4的区别
可以看到两者的区别仅仅在于
accept4()有第四个参数flags,这个参数如果为0,就跟accept()一样;下面的两个参数可以用按位OR来获取不同的行为:
SOCK_NONBLOCK:为新打开的文件描述符设置O_NONBLOCK标志位,这跟用fcntl()设置的效果是一样的,区别就是用fcntl()的话需要多调用个函数。
SOCK_CLOEXEC: 为新打开的文件描述符设置FD_CLOEXEC标志位,该标志位的作用是在进程使用fork()加上execve()的时候自动关闭打开的文件描述符。其实使用fcntl()设置FD_CLOEXEC标志位(也就是用open()的时候设置的O_CLOEXEC标志位)也能达到同样的效果,但跟fcntl()有什么不同呢?在多线程环境中,如果使用fcntl()会多出一步操作,这样就可能形成竞争;而使用accept4()就可以直接在打开的文件描述符上设置,可以消除竞争的问题。(原则上该竞争在那些新建文件描述符的调用中都存在,所以很多linux的系统调用都做了类似的处理。)
13.recv中的MSG_PEEK作用

浙公网安备 33010602011771号