尽可能使用非阻塞socket
int flags, s;
flags = fcntl (fd, F_GETFL, 0);
if (flags == -1){
close(fd);
return -1;
}
flags |= O_NONBLOCK;
s = fcntl (fd, F_SETFL, flags);
if (s == -1){
close(fd);
return -1;
}使用效率高效的epoll机制获取多个socket上的IN/OUT事件
非阻塞socket连接服务端时,不一定立即连接得上服务器
通过判断connect函数的返回值和错误号做进一步跟踪
int ret = ::connect(this->fd, (struct sockaddr*)&(server_addr), sizeof(server_addr));
if(ret == 0){
this->on_connected();
}else if(ret < 0 && errno != EINPROGRESS){
//error
}else{
on_connecting();
}IN/OUT事件时如果是connecting需要判断socket状态,socket reset发生时也会产生事件,然后才进入有效的读写
if(this->connect_status == 1){//connecting
int status = 0;
socklen_t slen;
if(getsockopt(this->fd, SOL_SOCKET, SO_ERROR, (void*)&status, &slen) < 0){
this->on_epollhup();
return;
}
if(status != 0){
this->on_epollhup();
return;
}
on_connected();
}读写发生错误时,通过错误码判断是错误还是数据读完或者缓冲区满了,发生错误则按socket断开处理
错误代码包括:SIGPIP, EAGAIN 等, EAGAIN表示读完或者缓冲区满,等待下一次事件处理读或者写
为了达到更高的性能,epoll使用EPOLLET(边沿触发)机制,但是如果事件发生时,无数据可写时,下一次有数据时
则不会发生OUT事件,因此可以记录一个写空转标识write_nil_loop,epoll_wait事件之前判断是否有数据发送并且发生write_nil_loop,如果有,则重新登记epoll事件
if(worker->is_write_nil_loop() && (events & EPOLLOUT)){
epoll->remove(worker);
epoll->add(worker, events);
}else if(worker->get_events_mask() != events){
epoll->modify(worker, events);
}
同时,读取数据时,尽可能将数据读完,直到recv返回值-1,errno== EAGAIN,其他错误按照出错处理
来源:oschina
链接:https://my.oschina.net/u/96206/blog/509896