这个函数就是C环境下,支持非协程环境和协程环境的,就是socket的读函数,写函数是类似的。
【 在 stub 的大作中提到: 】
: 这样各种库才能统一使用协程
//timeout for second 
int RecvNet(int socket,char *buf,int n,int timeout)
{
int bcount=0,br,ret;
int i,repeat=0;
int fflag=-1;
    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); //异步操作
    } else 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));
        if(ret) ShowLog(1,"%s:setsockopt errno=%d,%s",__FUNCTION__,errno,strerror(errno));
        if(socket<FD_SETSIZE-1) {
        fd_set efds;
                        FD_ZERO(&efds);
                        FD_SET(socket, &efds);
                        ret=select(socket+1,&efds,NULL,&efds,&tmout);
                //      ShowLog(4,"%s:tid=%lx,aft select ret=%d,socket=%d",__FUNCTION__,pthread_self(),ret,socket);
                        if(ret<=0) {
                                 ShowLog(1,"%s:select=%d, err=%d,%s",__FUNCTION__,ret,errno,strerror(errno));
                                if(ret==0) return TIMEOUTERR;
                return ret;
            }
        }
    }
    *buf=0;
    br=0;
    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;
/*
yield:
1.找到当前线程的context。找不到返回-1;(后续任务以同步阻塞完成)
2.把事件提交给epoll,作为resume的条件。
3.swapcontext,挂起这个任务,线程回到epoll_wait,可以为别人服务了。
4.一旦这个任务的事件发生了,立即由epoll_wait激活一个线程,抓取相应的context,使用setcontext,恢复任务现场,返回0.
后续的动作就是继续NONBLOCK的IO。直至完成返回。
*/
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;
                if(i==TIMEOUTERR) return i;
            }
        }
    }
    if(fflag!=-1) fcntl(socket,F_SETFL,fflag);
    return bcount==0?-1:bcount;
}
--
FROM 221.218.62.*
本来也不是让各种库去用。每种库都有自己的IO方式,不可能有通用的。 
 那个程序就是一个read的包装,需要提供一个yield,用于与各种库的接口。 
typedef int (*T_YIELD)(int socket,int rwflg,int timeout); 
 T_YIELD get_yield(void); 
 T_YIELD set_yield(T_YIELD new_yield); 
如果需要与IOCP接口,可以增加两个参数:buffer,len。 
 然后,你自己的程序,就可以直接使用这个函数进行IO了。 
 至于使用哪个协程哪个异步IO,完全是对应用透明的。 
 应用软件只需要调用这个东西去获取数据,不需要关心是否协程,是否异步IO。用的哪个库。 
  
比如这个程序,各种模式的服务器都会用到,有时是协程环境,有时不是。 
  
 //服务器用,与客户端协商密钥。  
int mk_clikey(int socket,ENIGMA2 *tc,u_int *family) 
 { 
 ENIGMA2 t; 
 char buf[100],keybuf[56],cli_k[52],*cp; 
 int i,len; 
 int crymode=15; 
 unsigned short crc,recv_crc; 
 char addr[16]; 
  
 cp=getenv("CRYPTFLG"); 
 if(cp && isdigit(*cp)) crymode=atoi(cp)&0xf; 
 i=0; 
 *buf=0; 
 peeraddr(socket,addr); 
 len=RecvNet(socket,buf,26,15); 
。。。。。 
  
在服务器刚刚接通客户端的时候,要求客户端15秒内发送一个数据来。很可能客户端不是一个正常的,是一个攻击,它可能根本不发任何有效数据。为了避免锁死服务器,在协程服务器里边,这期间是yield的。 
 在非协程服务器,死等15秒。 
 底层,你用啥库都可以,它不关心也不知道底层用了啥。 
【 在 stub 的大作中提到: 】
: 你没懂我的意思,我拿去用,关键各种库不会拿去用啊
--
FROM 221.218.62.*