这两天解除了网络模型,踩了一些坑, 现在写一点惨痛教训。
TCP 才read的时候,可能有这几种情况
1. 返回值>0,这种情况又分为2中情况
1.1 返回值==想要读到的size,这种情况意味着这次读工作良好, 且读到了想要的byte数,在处理的时候,不要标记该连接的状态,继续下次read
1.2 返回值 < 想要读到的size,这种情况意味着TCP接收缓冲区内的数据已经读完,我们在处理的时候,要标记该连接的状态为not ready,这次读完就要去等下一个epoll了
2. 返回值== 0, 这种情况是对端做了close的操作,这时候,我们应用层也要做清空缓冲区的操作。
3. 返回值 <0 , 这种情况下是遇到了错误,错误分为以下几种:
3.1 errno == EINTR , 这种是遭遇中断,应该continue,继续read.
3.2 errno == EAGAIN || EWOULDBLOCK , 这种是读完了,其操作应该类似上面< 想要的size
3.3 其他错误,是该连接出错,应该断连接
agent 代码
ssize_t conn_recv(struct conn *conn, void *buf, size_t size) {
ssize_t n;
ASSERT(buf != NULL);
ASSERT(size > 0);
ASSERT(conn ->flag & RECV_READY);
for (;;) {
n = da_read(conn ->fd, buf, size); // read 函数
log_debug("recv on fd %d %zd of %zu" , conn->fd, n, size);
if (n > 0) { //返回值大于0
if (n < (ssize_t ) size) {//返回值小于size,这次epoll的数据都已经读完
conn->flag &= ~RECV_READY ;// 置这个连接为不ready
}
conn->recv_bytes += (size_t ) n; // 等于size,epoll数据可能没读完
return n;
}
if (n == 0) {// 返回值等于零,对端关连接
conn->flag &= ~RECV_READY ;
conn->eof = 1;//置连接状态为eof
return n;
}
if (errno == EINTR) {//EINTR错误码,应该继续read
log_debug("recv on sd %d not ready - eintr" , conn->fd);
continue;
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
conn->flag &= ~RECV_READY ;
return -2;
} else {
conn->flag &= ~RECV_READY ;
conn->error = 1;
conn->err = CONN_RECV_ERR ;
return -1;
}
}
return -1;
}
做了一些项目,开始对网络框架有了一些新的理解,然后重新阅读agent的代码,有了一些新的理解, 记下来,以免忘记
首先呢, 在agent建立了连接之后,就新建了一个conn 类型的结构体对象,然后把这个conn注册到epoll的响应事件中去。
在epoll_wait 响应的时候,我们可以通过epoll_event结构体的data.ptr成员可以获取得到这个响应的conn是哪个。
读和写类似, 所以本文着重讲read的过程:
那么在获得这个conn的读事件响应之后,agent是怎么做的呢?
首先给这个连接置一个flag 叫 RECV_READY,表示当前的连接可以读
然后conn当中维护了一个msg结构体叫rmsg,表示该连接接收的msg,如果rmsg为空,则新分配空间,然后返回rmsg进行处理
msg结构体中也维护了一个buf的队列,一个buf结构体保存了实际收到的数据,从msg获取得到队列的最后一个buf,如果buf为空,或者最后一个buf也是满的就新建一个buf,插在msg结构体的尾部,然后把msg的pos指向buf的pos
然后接收数据了,根据最后buf可以放的下的大小,定义read系统调用的最后一个参数,read之后返回值的具体处理看前面一篇文章,
read返回的状态有4种,
连接状态还是ready,那么就是从msg_pos开始继续做逻辑,然后从上述的1步骤,继续下一轮循环
连接状态不ready,那么就做逻辑,然后这个conn的处理就结束了,不再下一轮循环
conn->eof == 1,即收到read到的字节为0,那么就将conn里的msg清空
conn->error == 1,即read的完之后为-1 ,且errno不为Intr 和 EAGAIN,那么则返回-1,然后做错误处理,清空连接