例如:int RecvNet(int socket,char *buf,int n,int timeout)
接收,整个系统,包括客户端,管理器,服务器,及各种模式(多进程,多线程,线程池),都是通过这个函数进行的。
它基本就是read。多了个TIMEOUT。它原来是阻塞模式循环接收。
改成协程模式后,要与原来的兼容。
解决办法是,里面加一个:
static T_YIELD yield=NULL;
T_YIELD get_yield()
{
return yield;
}
T_YIELD set_yield(T_YIELD new_yield)
{
T_YIELD oldyield=yield;
yield=new_yield;
return oldyield;
}
如果在非协程环境,yield=NULL,就进行原来的IO.
协程环境设置yield函数,它就会变成非阻塞模式。
if(socket<0) return SYSERR;
if(!buf && n<0) return 0;
if(yield) {
fflag=fcntl(socket,F_GETFL,0);
if(fflag!=-1) fcntl(socket,F_SETFL,fflag|O_ASYNC|O_NONBLOCK); //异步操作
}
如果异步操作出现任何异常,就立即转换为同步模式,不会耽误事。
while(bcount<n){
if((br=read(socket,buf,n-bcount))>0){//如果能收到,就收,无需转入异步模式。
bcount+=br;
buf+=br;
repeat=0;
continue;
}
if(fflag==-1 && errno==EAGAIN) return TIMEOUTERR;
if(br<=0 && errno && errno != EAGAIN){
if(errno!=ECONNRESET)
ShowLog(1,"%s:br=%d,err=%d,%s",__FUNCTION__,br,errno,strerror(errno));
break;
}
if(bcount < n && fflag!=-1) { //切换任务
if(repeat++>3) return -errno;
ShowLog(5,"%s:tid=%lX,socket=%d,yield to schedle bcount=%d/%d",__FUNCTION__,pthread_self(),socket,bcount,n);
i=yield(socket,0,timeout);
if(i<0) {
if(timeout>0) {
struct timeval tmout;
tmout.tv_sec=timeout;
tmout.tv_usec=0;
ret=setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&tmout,sizeof(tmout));
}
fcntl(socket,F_SETFL,fflag);
fflag=-1;
【 在 wallyz 的大作中提到: 】
: 我有点纳闷,如果“IO会长时间占用线程”,这里我理解的IO肯定是非阻塞式的IO,那非阻塞IO怎么会长时间占用线程呢?
: 即使要发的数据包特别长,在socket buffer未满的情况下,本质上也还是内存拷贝(甚至零拷贝),也是不会长时间占用线程的
: 而如果socket buffer满了,遇见EAGAIN,这时候不应该就地重试send或者sleep或者就地poll,而是应该放弃这次send,转而处理别的连接,等待之前的socket的EPOLLOUT事件之后再重新send,所以没有道理会有一个连接长时间占用线程
: ...................
--
修改:ylh0315 FROM 221.218.61.*
FROM 221.218.61.*