分类:
2012-05-26 22:24:05
原文地址:FileZilla FTP服务器源代码分析09 作者:sinodragon21
CListenSocket是CAsyncSocketEx类的子类,在启动的时候用来监听21端口。
pListenSocket->Create(nPort, SOCK_STREAM, FD_ACCEPT, NULL)
可见,CListenSocket只处理FD_ACCEPT消息。
看一下:
void CListenSocket::OnAccept(int nErrorCode)
{
CAsyncSocketEx
socket;
if (!Accept(socket)) // 这里调用了win API:
accept方法,建立了一个新连接,socket就这个新连接的SOCKET
{
int nError =
WSAGetLastError();
CStdString str;
str.Format(_T("Failure in
CListenSocket::OnAccept(%d) - call to CAsyncSocketEx::Accept failed,
errorcode
%d"), nErrorCode, nError);
SendStatus(str, 1);
SendStatus(_T("If
you use a firewall, please check your firewall configuration"), 1);
return;
}
// 权限检查,先不管
if (!AccessAllowed(socket))
{
CStdStringA
str = "550 No connections allowed from your IP\r\n";
socket.Send(str,
str.GetLength());
return;
}
// 检查FileZilla Server是否处于锁定状态,即不允许建立新连接
if (m_bLocked)
{
CStdStringA
str = "421 Server is locked, please try again later.\r\n";
socket.Send(str,
str.GetLength());
return;
}
// 下面从可用的线程中,找出目前负荷最小的线程,即线程中负责的connection最少的线程
int minnum =
255*255*255;
CServerThread *pBestThread=0;;
for
(std::list
{
int
num=(*iter)->GetNumConnections();
if (num
{
minnum=num;
pBestThread=*iter;
if
(!num)
break;
}
}
if (!pBestThread)
{
char
str[] = "421 Server offline.";
socket.Send(str, strlen(str)+1);
socket.Close();
return;
}
/* Disable Nagle algorithm. Most of the time single short strings
get
* transferred over the control connection. Waiting for
additional data
* where there will be most likely none affects
performance.
*/
BOOL value = TRUE;
socket.SetSockOpt(TCP_NODELAY,
&value, sizeof(value), IPPROTO_TCP); //
设置不使用Nagle算法,参见TCP协议Nagle算法部分
SOCKET sockethandle = socket.Detach();
pBestThread->AddSocket(sockethandle, m_ssl); // 转交服务线程来处理
CAsyncSocketEx::OnAccept(nErrorCode); // 父类的缺省处理为空
}
可见,CListenSocket::OnAccept主要工作是
1、创建一个socket来接收新的客户端连接
2、进行一些检查
设置,如权限检查,Nagle算法设置等
3、找到一个负荷最小的后台服务线程,由交那个线程处理
为了更清楚服务线程CServerThread的机制,先看回顾一下当时这个服务线程是如何被创建的。
CServer类的Create()片断:
for (int i = 0; i < num; i++) // 这里num是需要创建的服务线程的数量
{
int
index = GetNextThreadNotificationID(); //
得到这个线程的一个标识,即在线程数组std::vector
m_ThreadNotificationIDs中的index
CServerThread *pThread = new
CServerThread(WM_FILEZILLA_SERVERMSG + index);
m_ThreadNotificationIDs[index]
= pThread;
// If the CREATE_SUSPENDED flag is specified, the
thread is created in a suspended state,
// and will not run until
the ResumeThread function is called.
// If this value is zero, the
thread runs immediately after creation.
if
(pThread->Create(THREAD_PRIORITY_NORMAL, CREATE_SUSPENDED))
{
pThread->ResumeThread();
m_ThreadArray.push_back(pThread);
}
}
看一下pThread->Create(THREAD_PRIORITY_NORMAL, CREATE_SUSPENDED),由于CServerThread继承于CThread,因此调用了CThread的create:
BOOL CThread::Create(int nPriority /*=THREAD_PRIORITY_NORMAL*/, DWORD
dwCreateFlags /*=0*/)
{
m_hThread=CreateThread(0, 0, ThreadProc,
this, dwCreateFlags, &m_dwThreadId); // 调用win api创建一个线程
if
(!m_hThread)
{
delete this;
return FALSE;
}
::SetThreadPriority(m_hThread,
nPriority);
return TRUE;
}
注意创建线程的时候,指定线程的初始状态为
CREATE_SUSPENDED。
创建成功后,调用pThread->ResumeThread():
DWORD CThread::ResumeThread()
{
// 下面使用win
API:ResumeThread启动这个线程,线程开动后, 会自动跑到ThreadProc函数(create时指定)
DWORD
res=::ResumeThread(m_hThread); // 这个函数过后,有两个线程在跑,一个是刚才的主线程,一个是刚启动的线程
if
(!m_started) //
主线程运行到这里,由于m_started还是0,所以通过下面的WaitForSingleObject,进行了等待状态
{
WaitForSingleObject(m_hEventStarted,
INFINITE);
}
return res;
}
刚启动的线程进入了ThreadProc函数:
DWORD WINAPI CThread::ThreadProc(LPVOID
lpParameter)
{
// 在CreateThread时指定的参数LPVOID
lpParameter为this,即CThread
return ((CThread *)lpParameter)->Run();
}
即
运行Run方法:
DWORD CThread::Run()
{
InitInstance(); //
这里CServerThread类重写了这个方法,因此进入CServerThread::InitInstance(),进行了一些内存变量的初始
化
// The SetEvent function sets the specified event object to the
signaled state.
SetEvent(m_hEventStarted); //
设置event为active,使得刚才在等待的主线程复活,继续CServer的启动工作
m_started = true;
MSG
msg;
while (GetMessage(&msg, 0, 0, 0)) // 进入这个线程的消息循环
{
TranslateMessage(&msg);
if
(!msg.hwnd)
OnThreadMessage(msg.message, msg.wParam, msg.lParam);
// 调用OnThreadMessage处理消息
DispatchMessage(&msg);
}
DWORD
res=ExitInstance();
delete this;
return res;
}
可见,服务器启动后,刚开始没有消息时,CServerThread在GetMessage()时进入了block状态,一旦有消息到来,这个服务 线程就苏醒,接着
处理消息。
下面回到最初的CListenSocket::OnAccept(),最后调用了
pBestThread->AddSocket(sockethandle, m_ssl); // 转交服务线程来处理
仔细看一下后台的服务线程是如何处理消息的。
void CServerThread::AddSocket(SOCKET
sockethandle, bool ssl)
{
// 调用了父类的方法
PostThreadMessage(WM_FILEZILLA_THREADMSG,
ssl ? FTM_NEWSOCKET_SSL : FTM_NEWSOCKET, (LPARAM)sockethandle);
}
接着:
BOOL CThread::PostThreadMessage(UINT message, WPARAM wParam,
LPARAM lParam)
{
// posts a message to the message queue of the
specified thread.
BOOL res=::PostThreadMessage(m_dwThreadId,
message, wParam, lParam);;
ASSERT(res);
return res;
}
PostThreadMessage是windows API,作用是把消息message发送到线程m_dwThreadId,
根据前面的代码,在这里就是把消息WM_FILEZILLA_THREADMSG,以及参数FTM_NEWSOCKET, sockethandle发送到那个负荷最小的后台服务线程,
由于在启动时,那个后台线程处于GetMessage()的block中,因此收到这个消息到,那个后台线程苏醒,接着调用 OnThreadMessage来处理这个WM_FILEZILLA_THREADMSG消息。