需要一个简单的demo,客户端向服务器发送一张图片。
乍一想,觉得是一个挺简单的需求,实际写的时候,需要考虑很多细节。
指定一个简单的应用层协议,用于图片传输。简单一点,payload_len+payoad。其中payload_len,四字节长度,指示图片的大小。就是一个完整的报文有图片长度+图片内容组成。服务侧使用的socket采用non blocking模式。
服务侧使用的socket采用non blocking模式,希望采用epoll对fd进行管理。问题出现,epoll中的ET和LT有什么区别?可以ref[1]。当然采用LT似乎简单一些,因为socket缓冲区中的数据没有一次读完,下次调用epoll_wait函数时,相对应的socket上仍会发生新的可读事件,可以继续读取。
在ET模式下,那就需要一次把数据读完。否则,如果没有新的外部数据到来,对应的fd上就不会发生EPOLLIN事件。这里就需要循环读取:
if(events[i].events&EPOLLIN){
int n=0;
uint8_t buf[1500];
n=su_tcp_recv(con_fd,buf,1500);
if(n<=0){
std::cout<<"con close"<<std::endl;
su_socket_destroy(con_fd);
struct epoll_event ev;
epoll_ctl(epfd_, EPOLL_CTL_DEL, events[i].data.fd, &ev);
}
auto it=clients_.find(con_fd);
if(n>0){
std::cout<<"in "<<n<<std::endl;
if(it!=clients_.end()){
it->second->OnNewData(buf,n);
}
}
//data may not read all so, continue read, in ET mode
for(;;){
n=su_tcp_recv(con_fd,buf,1500);
if(n>0){
std::cout<<"in "<<n<<std::endl;
if(it!=clients_.end()){
it->second->OnNewData(buf,n);
}
std::cout<<" total "<<n<<std::endl;
}else{
std::cout<<" break "<<std::endl;
break;
}
}
}
服务器侧如何获取客户端关闭连接事件呢(客户端调用close函数)?[2]有个ET的例子,但是针对的是http协议,服务侧获取一次数据,就断开与客户端的连接。我这个例子是要多次获取数据,是长连接。
我发现,LT模式,在没有注册EPOLLRDHUP时,在EPOLLIN事件中,调用read函数,返回值为0,说明客户端断开连接。注册EPOLLRDHUP,就需要在EPOLLRDHUP中处理客户端端断开连接事件。而ET模式,必须注册EPOLLRDHUP,才能处理断开连接事件。
代码测试LT模式,可在server.cc,line120中的注册事件,改成这样:
ev.events=EPOLLIN/*|EPOLLRDHUP|EPOLLET*/;
数据读取的缓冲区,采用mbuf。
struct mbuf {
TAILQ_ENTRY(mbuf) next;
uint8_t *pos;//read marker
uint8_t *last;//write marker
uint8_t *start;//start of buf
uint8_t *end; //end of buf
struct mhdr *queue; // the queue contain the buf
};
struct mbuf *mbuf_get(struct context *);
void mbuf_recycle(struct context *, struct mbuf *);
int mbuf_read_size(struct mbuf *);
int mbuf_write_size(struct mbuf *);
int mbuf_write(struct mbuf * mbuf,void *data,int len);
int mbuf_read(struct mbuf * mbuf,void*dst,int len);
int mbuf_peek(struct mbuf * mbuf,void*dst,int len);
bool mbuf_can_recycle(struct mbuf * mbuf);
bool mhdr_readable(struct mhdr *header);
mbuf本身是free bad为socket缓冲区设计的结构。之前阅读ele的corvs[4]缓存代理时,觉得这个很有用,就摘下来了,改一改,用上了。
代码url[5]
Ref:
[1] epoll中读写数据 的注意事项
[2] epoll et
[3] mbuf
[4] corvus
[5] tcp-learn