您的位置:首页 > 理论基础 > 计算机网络

Linux 网络编程 select复用模式

2015-12-08 22:35 579 查看
起先用多线程进行网络编程,即在服务器上监听socket,当监听到新的客户端连接时,开启一个线程进行处理,直至结束。后来感觉如果客户端很多的话,就会有相应的线程,必定影响效率,所以就尝试了用select复用模式。在网上查了很多资料,感觉大部分都是误人子弟,而且还都是ctrl-c  ctrl-v的,连转载都没标。


在服务器上有两种描述字,一种是服务器自己的socket描述符,用于监听新的连接:

int listenFD = socket(AF_INET, SOCK_STREAM, 0);


另一种就是收到的新的连接的描述符:

int acceptFD = accept(listenFD,(struct sockaddr *)&clientAddr,&len);


网上很多教程是说把select()和accept()放在两个线程里去实现,即线程一:

while(true)
{
int acceptFD = accept(listenFD,(struct sockaddr *)&clientAddr,&len);
if(accpetSk < 0)
{
cout<<"accept failed!"<<endl;
return ;
}
cout<<"accept a new socket"<<endl;
FD_SET(acceptFD,&mFdRead);
}


线程2:

FD_ZERO(&mFdRead);
while(true)
{
int ret =select(maxFd + 1,&mFdRead,NULL,NULL,NULL);
//或者select(100,&mFdRead,NULL,NULL,&tv);
}


其中mFdRead是fd_set类型的描述字集合。

这种方法的话如果收到一个新的连接,不能马上进行读写和发送,甚至永远阻塞在select上,因为当线程二运行到select()时,就一直阻塞在那里(前提是最后一个参数为NULL或者0),即使accept()到一个新的描述字,并FD_SET()放入集合mFdRead中,但select所用的集合还是原来的旧集合,而且maxFD也是旧的,所以即使新的描述字读写状态发生变化了,select()根本不会检测到。当select用超时设置时,那也是等超时到了,在下一个周期中进行重新检测,这会导致数据丢失。

所以最好的办法是将服务器用于监听的套间字也放入到描述字集合中和收到的对端的描述字进行统一的监测。这样的话也不需要将accept()和select()分到两个线程中,只需一个就可以了。步骤为:

1.生成监听套间字。

2.将套间字set到描述字集合中;

3.进行select()监测;

4.监测到描述字状态变化并可读写时,先判断变化的描述符是监听套间字还是接受到的对端描述符。

5.如果是监听套间字,则进行accept()操作;

6.如果是对端描述符则进行recv() send()操作;

代码为:

int listenFD = 0;
int maxFd = 0;
fd_set fdRead;
vector<int> vecFd;
listenFd = socket(AF_INET, SOCK_STREAM, 0);
bind(.......);
listen(.......);

maxFd = listenFd;
while(true)
{
FD_ZERO(&fdRead);
FD_SET(listenFD,&fdRead);
for(int i = 0 ; i < vecFd.size(); ++i)
{
FD_SET(vecFd[i],&fdRead);
}
struct timeval tv = {10,0};
int selRet = select(maxFd + 1,&fdRead,NULL,NULL,&tv);
if(selRet <= 0)
{
cout<<"select failed or overtime!!"<<endl;
continue;
}
//首先先查看是不是监听套间字检测到状态变化
if(FD_ISSET(listenFd,&fdRead))
{
struct sockaddr_in clientAddr;
socklen_t  len = sizeof(clientAddr);
int acceptFd = accept(listenFD,(struct sockaddr *)&clientAddr,&len);
if(acceptFd < 0)
{
continue;
}
FD_SET(acceptFd,&fdRead);  //将新的描述符set到集合中
vecFd.push_back(acceptFd);
maxFd = max(maxFd,acceptFd);  //获得描述符中最大的值
}

for(int i = 0; i < vecFd.size(); ++i)
{
if(FD_ISSET(vecFd[i],&fdRead))
{
int recRet = recv(......);
int sendRet = send(......);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络编程 select linux