套接字描述符的就绪条件

     在使用select,poll,epoll等I/O复用模型时,我们一直在说当某个套接字描述符准备好读,或准备好写,或者在描述符上发生一个待处理的异常条件时,会触发相应的可读可写事件,下面对套接字描述的可读可写,以及异常条件做一个总结。

一,套接字可读

     满足以下几个条件中的任何一个时,一个套接字准备好读。
1,套接字接收缓冲区中的数据字节数大于等于套接字缓冲区低水位标记的当前大小。对此套接字进行读操作不会阻塞且返回准备好读入的数据大小。可以通过SO_RCVLOWAT来设置指定套接字的低水位标记。对于TCP,UDP默认为1。

int nRecvLowAT= 32;
socklen_t len = sizeof(nRecvLowAT);
setstockopt(nSocket,SOL_SOCKET,SO_RCVLOWAT,(void*)&nRecvLowAT,len);
//注意,windows和linux的setstockopt第三个参数类型有区别
//windows下是char*,linux下是void*

2,该套接字接收到了FIN,但是没有给对端ACK,此时读处于半关闭状态,对这样的套接字进行读操作将不会阻塞且返回0(表示读不到任何数据)。

3,该套接字是一个监听套接字,且已完成的连接数大于0(收到了另一端的连接请求)。对这样的套接字再调用accept通常不会阻塞,且accept后应该将新的连接套接字再次丢入到套接字集合中进行监听。

4,该套接字上有一个套接字错误待处理,对这样的套接字进行读操作将不会阻塞,且返回-1,同时errno设置成具体的错误值。同时,这些错误还可以通过SO_ERROR套接字选项获取:


int error = 0;
socklen_t len = sizeof(error);
getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (void*)&error, &len);

二,套接字可写

     满足以下几个条件中的任何一个时,一个套接字准备好写。
1,该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小值,并且该套接字已连接,或该套接字不需连接(UDP套接字)。这意味着如果我们把这样的套接字设置为非阻塞, 写操作将不会阻塞且返回准备好读入的数据大小。

对阻塞式套接字进行读操作,只要有数据总会返回一个大于0的数(实际读取的字节数);
对阻塞式套接字进行写操作,将会一直阻塞到所有数据都会被内核接受为止
(比如发送缓冲区大小为8192字节,但是write 8193字节的数据时,write将会阻塞,
等待最后一个字节可用空间。)

可以通过SO_SNDLOWAT来设置指定套接字的低水位标记。对于TCP,UDP默认为2048。

int nSendLowAT= 32;
socklen_t len = sizeof(nSendLowAT);
setstockopt(nSocket,SOL_SOCKET,SO_SNDLOWAT,(void*)&nSendLowAT,len);

2,该套接字的写半部关闭。当一个套接字收到FIN后,第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送)。但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以,第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号,导致进程退出,此信号一般需要屏蔽。

3,使用非阻塞connect的套接字已建立连接,或者connect失败。这样的套接字会触发写操作。

4,该套接字上有一个套接字错误待处理,对这样的套接字进行读操作将不会阻塞,且返回-1,同时errno设置成具体的错误值。同时,这些错误还可以通过SO_ERROR套接字选项获取(同上)。

三,套接字异常

     如果一个套接字上存在带外数据或者仍处于带外标记,那么它有异常条件会被触发。同时当某个套接字上发生错误时,它将由select标记为既可读又可写(上面描述的可读可写条件的第四点)。

四,代码演示

     不得不说,开源代码能够开源,它们的细节考虑确实周到,此处以Teamtalk的服务端代码为例,它的服务端底层都是使用共用的BaseSocket和EventDispatch来实现的,先看看它调用epoll_wait的地方:
在这里插入图片描述
     在一个线程中,while循环调用epoll_wait等待套接字的可读,可写,异常被触发。再根据EPOLL类型分别调用OnRead(),OnWrite(),OnClose()函数。我们先看可读事件OnRead()的处理:
在这里插入图片描述
     它做了三件事:
1,处理新的连接,并将新连接再次丢入epoll中进行监控,这个在_AcceptNewSocket()中实现。
2,判断套接字是否异常,读取数据是否<=0
3,套接字上接收缓冲区中的数据字节数大于等于套接字缓冲区低水位标记的当前大小时,触发业务回调。
     而这几点把我们上面讲述的套接字可读的几种情况全部包含进去了,考虑非常周全。

     同样的,我们再看可写事件OnWrite()的处理:
在这里插入图片描述     它也做了三件事:
1,如果该套接字已经连接上了,且发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小值,那么调用业务的回调函数。
2,如果该套接字处于正在连接中,判断该套接字是否异常,如果异常,则关闭。
3,如果该套接字处于正在连接中,且该套接字正常,那么触发第一次连接事件。
     这几点也把套接字可写的几种情况全部考虑进去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Simple Simple

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值