監聽本地埠,接受客戶端連入,維護連入 Socket 佇列。
bool CListenSocket::StartListening() {
bListening = true;
// SO_REUSEADDR 可解決快速重啟 LowID 問題,但若埠被別程序佔用會導致雙程式監聽同埠
if (!Create(thePrefs.GetPort(), SOCK_STREAM, FD_ACCEPT, thePrefs.GetBindAddrA(), FALSE))
return false;
if (!Listen()) return false;
m_port = thePrefs.GetPort();
return true;
}
非實際關閉/開啟埠,僅切換 bListening 標誌。當連線數過多暫停 (StopListening),連線數下降時呼叫此函式恢復接受。
void CListenSocket::OnAccept(int nErrorCode) {
if (nErrorCode) return;
m_nPendingConnections++;
if (TooManySockets(true) && !theApp.serverconnect->IsConnecting()) {
StopListening(); return;
}
if (!bListening) ReStartListening();
while (m_nPendingConnections > 0) {
m_nPendingConnections--;
CClientReqSocket* newclient;
SOCKADDR_IN SockAddr; int iSockAddrLen = sizeof(SockAddr);
// 條件式接受 (WSAAccept + 回呼過濾)
if (thePrefs.GetConditionalTCPAccept() && !thePrefs.GetProxySettings().UseProxy) {
SOCKET sNew = WSAAccept(m_SocketData.hSocket, (SOCKADDR*)&SockAddr, &iSockAddrLen, AcceptConnectionCond, 0);
// 錯誤處理:WSAEWOULDBLOCK 重設計數器;其他錯誤累計超過 10 次重建 Socket
newclient = new CClientReqSocket;
newclient->InitAsyncSocketExInstance();
newclient->m_SocketData.hSocket = sNew;
newclient->AttachHandle(sNew);
} else {
// 一般 Accept
newclient = new CClientReqSocket;
if (!Accept(*newclient, (SOCKADDR*)&SockAddr, &iSockAddrLen)) { /* 錯誤處理同上 */ }
}
AddConnection(); // 計數器+1
// 安全性檢查
if (SockAddr.sin_addr.S_un.S_addr == 0)
newclient->GetPeerName((SOCKADDR*)&SockAddr, &iSockAddrLen);
if (theApp.ipfilter->IsFiltered(SockAddr.sin_addr.S_un.S_addr)) {
newclient->Safe_Delete(); theStats.filteredclients++; continue;
}
if (theApp.clientlist->IsBannedClient(SockAddr.sin_addr.S_un.S_addr)) {
newclient->Safe_Delete(); continue;
}
newclient->AsyncSelect(FD_WRITE | FD_READ | FD_CLOSE);
}
}
WSAAccept 搭配回呼 AcceptConnectionCond 可在核心層過濾連線。WSAEWOULDBLOCK 重設待處理計數;連續錯誤 >10 次重建監聽 Socket。ipfilter 與 clientlist 封禁清單。AsyncSelect 註冊讀/寫/關閉通知。基於 eMule 0.47 版本分析。