这个函数就是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.*