一个关于多线程网络编程的问题
我在编写一个多线程的聊天程序,在没有构建多线程之前,所有的功能运行良好,但是在重构成多线程之后,服务器端在接受数据时会出错,具体情况是这样的:
我在服务器端的CServerDoc的OnNewDocument()中启动端口的侦听以及多线程服务:
BOOL CServerDoc::OnNewDocument()
{
.....
clientList.RemoveAll(); //该全局链表用于存放向Server端发送数据的Client端对象,由各线程依次处理
m_pSocket = newCListeningSocket(this);
CThread (ReceThread;
ReceThread = new CReceiveThread(this);
CThreads *ReceThreads = new CThreads(ReceThread,5);
ReceThreas->Start(); //启动五个线程,当clientList中一有数据加入,就对其中的对象进行处理.
chPort = GetPrivateProfileInt(sConfigSection,"Port",1000,CONFIG_FILE);
if(m_pSocket->Create(chPort))
{
if(m_pSocket->Listen())
return TURE;
}
return FALSE;
}
当有用户通过客户端接入时,服务器端通过OnAccept消息响应函数来调用Doc类的ProcessPendingAccept()函数为该用户创建一个CClientSocket对象,用以从该用户的客户端接受数据或者将数据从服务器端发送给该用户,至此,进程都处于单线程状态:
void CServerDoc::ProcessPendingAccept()
{
CClientSocket* pSocket = new CClientSocket(this);
if(m_pSocket->Accept(*pSocket,(sockaddr*)&addr,&len))
{
pSocket->Init();
m_connectionList.AddTail(pSocket);//m_connectionList链表是Doc类的成员,用以存放连入服务器端的客户端(CClientSocket类)对象
}
else
delete pSocket;
}
当某个连入服务器的客户端有数据发送过来的时候,其相应的CClientSocket对象便启动事件使线程同步:
void CClientSocket::OnRecieve(int nErrorCode)
{
CSocket::OnReceive(nErrorCode);
clientList.AddTail(this); //将自己存入clientList链表中,等待某线程对其进行处理
if(clientList.GetCount()>0)
g_reclistStart.SetEvent(); // 开启事件,使等待中的线程启动
}
线程被解除等待状态之后会继续执行ClassProc()函数中接下去的程序,来处理排列在clientList中客户端Socket对象,接受其发过来的数据:
CRecieveThread::ClassProc()
{
while(1)
{
::WaitForSingleObject(g_rcvlistStart,INFINITE); //等待事件开启
m_cs.Lock();
if(clientList.GetCount()==0)
{
m_cs.Unlock();
cnotinue;
}
CClientSocket* pSocket = (CClientSocket*)clientList.RemoveTail(); //取出排在链表中的数据发送对象
if(clientList.GetCount()==0)
g_reclistStart.ResetEvent(); //链表为空时关闭事件
m_cs.Unlock();
do
{
CMsg* pMsg = ReadMsg(pSocket); //将对象传递的数据存入CMsg对象中(在此处出了问题)
if(pMsg->m_bClose)
{
m_cs.Lock();
m_pDoc->CloseSocket(pSocket);
m_cs.Unlock();
break;
}
}
while(!pSocket->m_pArchiveIn->IsBifferEmpty());
UpdateTable(pSocket);
UpdateClents();
}
return 1;
}
下面是ReadMsg()中的调用,其中代码很多,现只写出关键的调用:
CMsg* CRecieveThread::ReadMsg(CClientSocket* pSocket)
{
CMsg *msg = new CMsg();
......
TRY
{
pSocket->RecieveMsg(msg); 调用数据传递对象中的RecieveMsg()成员函数
......
}
......
return msg;
}
void CClientSocket::RecieveMsg(CMsg* pMsg)
{
pMsg->Serialize(*m_pArchiveIn);
}
void CMsg::Serialize(CArchive& ar)
{
if(ar.IsStoring())
{
ar<<(WORD)m_bClose;
ar<<m_strText;
}
else
{
CString stp;
WORD wd;
m_nameList.RemoveAll();
ar>>wd; //程序走到此处出错,似乎无法读出ar中的内容
m_bClose = (BOOL)wd;
ar>>m_strText;
......
}
m_msgList.Serialize(ar)
}
但是,我只要对程序稍加改动:
BOOL CServerDoc::OnNewDocument()
{
.....
clientList.RemoveAll();
m_pSocket = newCListeningSocket(this);
CThread (ReceThread;
ReceThread = new CReceiveThread(this);
CThreads *ReceThreads = new CThreads(ReceThread,5);
//ReceThreas->Start(); //注释掉该句
chPort = GetPrivateProfileInt(sConfigSection,"Port",1000,CONFIG_FILE);
if(m_pSocket->Create(chPort))
{
if(m_pSocket->Listen())
return TURE;
}
return FALSE;
}
void CClientSocket::OnReceive(int nErrorCode)
{
CSocket::(nErrorCode)
clientList.AddTail(this);
m_pCtd->ClassProc //当有数据传过来时直接用CReceiveThread类的指针调用ClassProc函数
// if(clientList.GetCount()>0)
// g_reclistStart.SetEvent();
}
CRecieveThread::ClassProc()
{
/* while(1)
{
::WaitForSingleObject(g_rcvlistStart,INFINITE); //等待事件开启
m_cs.Lock();
if(clientList.GetCount()==0)
{
m_cs.Unlock();
cnotinue;
}
*/
CClientSocket* pSocket = (CClientSocket*)clientList.RemoveTail(); //取出排在链表中的数据发送对象
if(clientList.GetCount()==0)
g_reclistStart.ResetEvent(); //链表为空时关闭事件
m_cs.Unlock();
do
{
CMsg* pMsg = ReadMsg(pSocket); //将对象传递的数据存入CMsg对象中(在此处出了问题)
if(pMsg->m_bClose)
{
m_cs.Lock();
m_pDoc->CloseSocket(pSocket);
m_cs.Unlock();
break;
}
}
while(!pSocket->m_pArchiveIn->IsBifferEmpty());
UpdateTable(pSocket);
UpdateClents();
// }
return 1;
}
这样的话就等于使用单线程,聊天程序便可以正常运行,ar中的内容也可以正常读出...我实在有点想不通为什么会发生这种错误,不知哪位高人可以看出其中的蹊跷,帮我指点一二