网络聊天项目—select模型实现
1.在TCPNet类里定义集合fd_set m_fdsets;
2.在TCPNet类初始化清空集合FD_ZERO(&m_fdsets);
3.将需要查看的socket加入到集合中FD_SET(socketwaiter,&m_fdsets);
在主线程监听完成之后创建一个线程等待客户端的连接,然后在连接线程函数里accept()阻塞等待,将连接上的客户端的socket(服务员和客人已连接,服务员等待客人说话)加入到集合m_fdsets中。
- 出现问题:整个集合交给select管理,select管理一段时间后返回,假如没有一个socket发生网络事件,数组(集合)就空了,下次再让查看socket换需要再将socket加入到集合中,这样每次初始化数组集合太麻烦。
- 解决办法:在recv接收数据线程函数里边定义一个临时的集合,将已初始化好的集合赋值给temp临时集合,每次将临时集合交给select管理,然后再次初始化用开始的集合给temp赋值。
4.将集合交给select管理select();定时查看有没有可读数据。在主线程创建等待连接客户端的线程后创建一个接收数据的线程,然后在接收数据线程函数里定义一个临时集合fd_set m_fdtemp,将集合赋值给临时集合,然后将临时集合交给select管理。select在内核里定时查看设定的时间,将发生网络事件的socket留在集合中返回。
5.检验是否发生网络事件,FD_ISSET();因为socket是一个链表需要循环查看。
如果发生网络事件就去处理,然后select继续查看。
unsigned __stdcall TCPNet::ThreadRecv( void * lpvoid)
{
TCPNet *pthis = (TCPNet*)lpvoid;
//接受数据
SOCKET sockWaiter = (SOCKET)lpvoid;
int nBufferSize;
int nRes;
char *pszbuf = NULL;
fd_set fdtemp;
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100;
while(TCPNet::m_bFlagQuit)
{
fdtemp = pthis->m_fdsets;
//将整个集合交给select管理
select(0,&fdtemp,0,0,&tv);
//检验是否发生网络事件 socket是一个链表,循环遍历
for(int i=0;i<pthis->m_fdsets.fd_count;i++)
{
if(FD_ISSET(pthis->m_fdsets.fd_array[i],&fdtemp))
{
nRes = recv(pthis->m_fdsets.fd_array[i],(char *)&nBufferSize,sizeof(int),0);
//int nRes = recv(sockWaiter,szbuf,sizeof(szbuf),0);
if(nRes <= 0)
continue;
//首先根据接收到的包大小申请相应的空间
pszbuf = new char[nBufferSize];
int offset = 0;
while(nBufferSize)
{
/*从首地址开始放数据*/
nRes = recv(pthis->m_fdsets.fd_array[i],pszbuf+offset,nBufferSize,0);
offset += nRes;
nBufferSize -= nRes;
}
//处理
m_pKernel->DealData(pszbuf,pthis->m_fdsets.fd_array[i]);
}
}
delete []pszbuf;
pszbuf = NULL;
}
return 0;
}