zoukankan      html  css  js  c++  java
  • VC FTP服务器程序分析(一)

      想在QT上移植一个FTP服务器程序,先学习windows下的FTP服务器例子,然后随便动手写点东西。

      在pudn上搜索 "FTP服务器端和客户端实现 VC“这几个关键字,就可以搜到下面要分析的这段代码。

      软件结构大概是这样的,CServerDlg类是应用程序的主窗口类,当点击了控件栏上的开始按钮后,在消息响应函数中就创建了FTP服务器的监听socket。

     1 void CServerDlg::OnStart() 
     2 {
     3     // TODO: Add your command handler code here
     4     if (m_bRunning)
     5     return;
     6 
     7     // created the listen socket
     8     if (m_ListenSocket.Create(m_ControlPort))
     9     {
    10         // start listening
    11         if (m_ListenSocket.Listen())
    12         {
    13             m_ListenSocket.m_pWndDlg = this;
    14             m_bRunning = TRUE;    
    15             
    16             AddTraceLine(AfxGetThread()->m_nThreadID, "服务器在端口 %d 启动成功.", m_ControlPort);
    17             m_wndToolBar.GetToolBarCtrl().EnableButton(IDC_START, !m_bRunning);
    18             m_wndToolBar.GetToolBarCtrl().EnableButton(IDC_STOP, m_bRunning);
    19             m_wndStatusBar.SetPaneText(0, "服务器正在运行中", TRUE);
    20             return;
    21         }
    22     }
    23     AddTraceLine(AfxGetThread()->m_nThreadID, "服务器不能在端口 %d 启动,请检查是否有其他程序占用此端口.", m_ControlPort);
    24 }

      第8行创建了一个监听端口,m_ListenSocket是CListenSocket类对象,CListenSocket是下一个要分析的类。FTP的监听端口被固定为了21,即第8行的变量m_ControlPort = 21。然后就是m_ListenSocket.Listen,在这个套接字上监听。使用CAsyncSocket写一个tcp服务器的话基本也是这两步。AfxGetThread()获取当前线程类对象指针。下面再简要介绍下FTP的网络通信模型。

      FTP跟tcp协议一样,是服务器客户端结构。当然FTP是在tcp协议层之上的,FTP服务器使用了3个tcp套接字,FTP客户端使用了2个tcp套接字。FTP服务器必须要有一个tcp监听服务套接字,也就是上面函数中的m_ListenSocket,它监听的端口是固定的-21。然后当一个客户端连接上来以后(C端用了一个socket,S端对应一个socket),服务器和客户端用这个连接上来的套接字作为控制命令信息socket。C和S之间的控制命令信息传输就使用这两个socket,FTP协议是文件传输协议,那么文件数据的传输是怎么传输的呢,CS之间另开了一个socket专门传输数据,至于怎么开的这个数据传输socket,要从后面的分析来看了,我也忘了。这段话解释了FTP通信的基本结构了。后面的一切都好解释了。

      先来看服务器的监听套接字类--CListenSocket,除去构造函数和析构函数就剩一个函数--OnAccept,这也间接说明了监听套接字的唯一功能。OnAccept函数是CAsyncSocket类的一个虚函数,子类重载了此函数。当有客户端连接时函数被调用。

     1 void CListenSocket::OnAccept(int nErrorCode) 
     2 {
     3     // New connection is being established
     4     CSocket sock;
     5 
     6     // Accept the connection using a temp CSocket object.
     7     Accept(sock);
     8 
     9     // Create a thread to handle the connection. The thread is created suspended so that we can
    10     // set variables in CConnectThread before it starts executing.
    11     CClientThread* pThread = (CClientThread*)AfxBeginThread(RUNTIME_CLASS(CClientThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
    12     if (!pThread)
    13     {
    14         sock.Close();
    15         TRACE("Could not create thread
    ");
    16         return;
    17     }
    18     
    19     CServerDlg* pDlg = (CServerDlg*) m_pWndDlg;
    20 
    21     // set members of CClientThread.m_socket
    22     pThread->m_ControlSocket.m_pCriticalSection = &pDlg->m_CriticalSection;
    23     pThread->m_hWndOwner = m_pWndDlg->GetSafeHwnd();
    24 
    25     pDlg->m_CriticalSection.Lock();
    26     // since everything is successful, add the thread to our list
    27     pDlg->m_ThreadList.AddTail(pThread);
    28     pDlg->m_CriticalSection.Unlock();
    29 
    30     // Pass the socket to the thread by passing the socket handle. You cannot pass
    31     // a CSocket object across threads.
    32     pThread->m_hSocket = sock.Detach();
    33 
    34     // Now start the thread.
    35     pThread->ResumeThread();
    36 
    37     CAsyncSocket::OnAccept(nErrorCode);
    38 }

      第7行,得到了连接上来的套接字,保存在临时变量sock中。第11行,开了一个线程。这也是这个程序考虑得比较周全的地方,它将数据传输控制socket和数据传输socket封装在了一个线程对象中统一管理,增加了服务器对多客户端的反应能力--多线程的功能本来就这样。AfxBeginThread的原型如下:

      CWinThread* AFXAPI AfxBeginThread(
      CRuntimeClass* pThreadClass,   //从CWinThread派生的RUNTIME_CLASS类
      int nPriority,                             //指定线程优先级,如果为0,则与创建该线程的线程相同
      UINT nStackSize,                      //指定线程的堆栈大小,如果为0,则与创建该线程的线程相同
      DWORD dwCreateFlags,            //一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
      LPSECURITY_ATTRIBUTES lpSecurityAttrs) //参数5表示线程的安全属性,NT下有用
      AfxBeginThread有两种,工作者线程和用户界面线程。
      继续上面的代码,后面将主程序窗口句柄传递给线程对象类,主要是为了好给主程序窗口发送消息。第32行,pThread->m_hSocket = sock.Detach();
         此成员函数从CAsyncSocket对象中撤消m_hSocket数据成员中的SOCKET句柄。
         第35行,pThread->ResumeThread();开始让这个线程运行。
       总结一下,CListenSocket就实现了客户端连接处理函数OnAccept,接收到一个客户端请求以后,就新建一个客户端线程,将节目句柄和这个客户端的socket句柄hSocket传递给了这个线程对象。
  • 相关阅读:
    菜鸟记录:如何获取LOGINVIEW控件状态模板中的子控件
    无法安装dotnetFramework35sp1的解决方法
    MOSS2007小技巧:不用SPD轻松删除错误页面上的问题Webpart
    在动态页面里象静态页面一样控制整个网页的缓存和更新
    烦人的网页iframe去除
    经典sql注入教程
    自己写的后台静态权限验证类
    Asp.net项目从Vs2003转换到Vs2005的常见问题大全及解决方法
    C# 相当于ASP里Eval中的计算公式的方法(超简单的方法)
    1 UNIX与Linux的发展
  • 原文地址:https://www.cnblogs.com/kanite/p/5153251.html
Copyright © 2011-2022 走看看