程序源碼:
DWORD WINAPI CServerSocket:: ListenThread(LPVOID lparam)
{
try
{
//加以下語句可消除Debug時出現(xiàn)的afxwin1.inl的報錯
//該錯誤在Release時是不出現(xiàn)的
//AFX_MANAGE_STATE(AfxGetStaticModuleState());
CServerSocket *pServerSocket=(CServerSocket *)lparam;
int createSucceed=pServerSocket->Create(pServerSocket->intPort); //第2次執(zhí)行時在這里出現(xiàn)WSAEADDRINUSE錯誤,原因是端口被占用
if(createSucceed==0)
{
AfxMessageBox("_ListenTcpThread Create錯誤!"+pServerSocket->GetError(GetLastError()));
return -1;
}
int listenSucceed=pServerSocket->Listen(10); //開始監(jiān)聽
if(listenSucceed==0)
{
AfxMessageBox("_ListenTcpThread Listen錯誤!"+pServerSocket->GetError(GetLastError()));
return -1;
}
CSocket recSo;
SOCKADDR_IN client;
int iAddrSize=sizeof(client);
int intSucceed=pServerSocket->Accept(recSo,(SOCKADDR *)&client,&iAddrSize); //接受連接并取得對方IP
....
}
先是查了不少文章,一堆搞不明白狀況的家伙胡亂指點,有的說是要AfxSocketInit(),有的說換個端口就可以了!
問題是我的這個端口是監(jiān)聽的,我隨機換的話,遠程的Client哪里知道該找哪個來連接???
總算找到下面的一段文字,把原理給說清楚了!解決辦法也很清楚 !
我的處理是,退出線程之前做一下SetSockOpt,代碼如下:
//停止服務程序監(jiān)聽
BOOL CServerSocket::StopServer()
{
DWORD exitcode;
WaitForSingleObject(m_hServerThread,500);
//解決WSAEADDRINUSE錯誤的解決辦法 start
//加這幾句可以解決:停止監(jiān)聽,再重新開啟監(jiān)聽出現(xiàn)Create語句發(fā)生WSAEADDRINUSE錯誤的問題
//問題提示端口已經(jīng)被占用
BOOL bDontLinger=FALSE;
this->SetSockOpt(SO_DONTLINGER,(const char *)&bDontLinger,sizeof(bDontLinger),SOL_SOCKET);
this->Close();
//解決WSAEADDRINUSE錯誤的解決辦法 end
if(!GetExitCodeThread(m_hServerThread,&exitcode))
{
TerminateThread(m_hServerThread,exitcode);
}
m_hServerThread=NULL;
return 0;
}
為什么TCP關(guān)閉后端口會處于TIME_WAIT狀態(tài)?
一般來說,tcp正常關(guān)閉需要四個包。比如a和b關(guān)閉連接,a先給b發(fā)一個fin,b會進行確認ack,然后b也會發(fā)出fin,當a接受到這個fin,并發(fā)出最后一個ack后,就會處于time_wait狀態(tài)。這個時間長短跟操作系統(tǒng)有關(guān),一般會在1-4分鐘,也就是兩倍的數(shù)據(jù)包(2msl)最大生存時間。TCP主動關(guān)閉方采用TIME_WAIT主要是為了實現(xiàn)終止TCP全雙工連接的可靠性及允許老的重復分節(jié)在網(wǎng)絡中消逝,等過了2msl(大約1~4分鐘)后TIME_WAIT就會消失。
所以說,主動發(fā)起關(guān)閉連接的一方會進入time_wait狀態(tài),這個時候,進程所占用的端口號不能被釋放。除非在你的程序中用setsockopt設(shè)置端口可重用(SOCK_REUSE)的選項,但這不是所有操作系統(tǒng)都支持的,解決TIME_WAIT的辦法我個人認為以下兩種比較好:
禁用LINGER
//Socket API
BOOL bDontLinger=FALSE;
setsockopt(m_socket,SOL_SOCKET,SO_DONTLINGER,(LPCTSTR)&bDontLinger,sizeof(BOOL));
closesocket(s);
//MFC CAsyncSocket或者CSocket
BOOL bDontLinger=FALSE;
m_socket->SetSockOpt(SO_DONTLINGER,(const char *)&bDontLinger,sizeof(bDontLinger),SOL_SOCKET);
m_socket->Close();