我是靠谱客的博主 简单黄豆,最近开发中收集的这篇文章主要介绍对近段时间在EVC下学习情况的总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

从本期开校到现在,主要的精力花在了evc的学习,运用上。这里对学习的情况做一个总结。

(以在一个工程中实现的功能模块为序)

在一个名叫Orange的工程中,实现的功能模块有:

         ①用Tcp协议,在客户端和服务器端实现图片的传输。

         ②在客户端的显示设备上,显示从服务器端传来的图片,并显示。

         ③实现一个启动动画。

(下面详细总结)

①用Tcp协议,在客户端和服务器端实现图片的传输。

    首先,需要知道Tcp协议在工作的时候,客户端和服务器端都要做哪些动作来完成通信的功能。

        (以函数的形式说明他们的过程)

         a.Server:socket()-->bind()-->listen()-->accept()-->recv()-->send-->recv()-->closesocket().

         b.Client:    socket()------------------------->connect()-->send()-->recv()------------>closesocket() 。

    然后,需要知道对文件的操作。(因为传输的是图片,也是一种文件)

        (这里不做相信说明,百度一下)。

    最后,由于文件我传输的文件一般都大于4k,所以我选择了在服务器端用循环的方式读,在客户端再用

        循环的方式写文件。相关函数如下:

  /*-----------------------------------------------------------------
【函数介绍】: 从服务器端向客户端发送数据,按照约定pts2[]有4个值,故不指定它的长度
【入口参数】: buf:待发送的数据
              len:待发送的数据长度
     fileName:待发送的图片文件的名字
     pts2[]:待发送的坐标值

【出口参数】: (无)
【返回  值】: TRUE:发送数据成功;FALSE:发送数据失败
------------------------------------------------------------------*/
bool CTCPCustom_CE::SendData(CString fileName,POINT pts2[])
{
  // used to monitor the progress of a sending operation
    int fileLength, cbLeftToSend;
    // pointer to buffer for sending data
    // (memory is allocated after sending file size)
    BYTE* sendData = NULL;

    CFile sourceFile;
    CFileException fe;
    BOOL bFileIsOpen = FALSE;
   
    if( !( bFileIsOpen = sourceFile.Open(fileName,//_T("//Temp//mapxiao.bmp"),
           CFile::modeRead | CFile::typeBinary, &fe ) ) )
    {
  /* you should handle the error here */
        TCHAR strCause[256];
        fe.GetErrorMessage( strCause, 255 );
        if(fe.m_cause==CFileException::fileNotFound)
   AfxMessageBox(_T("File not found"),MB_YESNO|MB_ICONSTOP);
  return FALSE;
     }

    // first send length of file

    fileLength = sourceFile.GetLength();
    fileLength = htonl( fileLength );

 cbLeftToSend = sizeof( fileLength );

    do
    {
        int cbBytesSent;
        char* bp = (char*)(&fileLength) + sizeof(fileLength) - cbLeftToSend;
     cbBytesSent = send(m_socket,bp,cbLeftToSend,0);
       
        // test for errors and get out if they occurred
        if ( cbBytesSent == SOCKET_ERROR )
        {
            int iErrorCode = WSAGetLastError();
   //触发socket的Error事件
   m_pTCPServer_CE->OnClientError(m_pTCPServer_CE->m_pOwnerWnd,this,iErrorCode);
   //触发与服务器端断开连接事件
   m_pTCPServer_CE->OnClientClose(m_pTCPServer_CE->m_pOwnerWnd,this);
   //关闭socket
   Close();
   return FALSE;
  }
       
        // data was successfully sent, so account
        // for it with already-sent data
        cbLeftToSend -= cbBytesSent;
    }
    while ( cbLeftToSend>0 );
   
   
    // now send the file's data   
    sendData = new BYTE[1024];
   
    cbLeftToSend = sourceFile.GetLength();
   
    do
    {
        // read next chunk of SEND_BUFFER_SIZE bytes from file
       
        int sendThisTime, doneSoFar,buffOffset;
       
        sendThisTime = sourceFile.Read( sendData, 1024 );
        buffOffset = 0;
    do
        {

    char* bp=(char*)(sendData+buffOffset);
       doneSoFar=send(m_socket,bp,sendThisTime,0);
           
            // test for errors and get out if they occurred
            if ( doneSoFar == SOCKET_ERROR )
            {
                int iErrorCode = WSAGetLastError();
    //触发socket的Error事件
    m_pTCPServer_CE->OnClientError(m_pTCPServer_CE->m_pOwnerWnd,this,iErrorCode);
    //触发与服务器端断开连接事件
    m_pTCPServer_CE->OnClientClose(m_pTCPServer_CE->m_pOwnerWnd,this);
    //关闭socket
    Close();
    return FALSE;
            }
           
            // data was successfully sent,
            // so account for it with already-sent data
           
            buffOffset += doneSoFar;
            sendThisTime -= doneSoFar;
            cbLeftToSend -= doneSoFar;
        }
        while ( sendThisTime > 0 );
       Sleep(100);
         }
     
    while ( cbLeftToSend > 0 );
   
    //AfxMessageBox(_T(" SEND OK"),MB_YESNO|MB_ICONSTOP);
   
    // free allocated memory
    // if we got here from a goto that skipped allocation,
    // delete of NULL pointer
    // is permissible under C++ standard and is harmless
    delete[] sendData;
   
    if ( bFileIsOpen )
        sourceFile.Close();
        // only close file if it's open (open might have failed above)

    //Send the points
  cbLeftToSend=4*sizeof(POINT);
  int nSendBytes=0;
  int nBytes=0;
  while (nSendBytes < cbLeftToSend)
     {
     nBytes = send(m_socket,(char*)pts2+nSendBytes,cbLeftToSend-nSendBytes,0);
    if (nBytes==SOCKET_ERROR )
    {
    int iErrorCode = WSAGetLastError();
    //触发socket的Error事件
    m_pTCPServer_CE->OnClientError(m_pTCPServer_CE->m_pOwnerWnd,this,iErrorCode);
    //触发与服务器端断开连接事件
    m_pTCPServer_CE->OnClientClose(m_pTCPServer_CE->m_pOwnerWnd,this);
    //关闭socket
    Close();
    return FALSE;
   }

  nSendBytes = nSendBytes + nBytes;
  
  if (nSendBytes < cbLeftToSend)
  {
      Sleep(1000);
  }
 }

 return TRUE;
}
/*--------------------------------------------------------------------
【函数介绍】:  此线程用于监听TCP客户端通讯的事件,例如当接收到数据、
      连接断开和通讯过程发生错误等事件
【入口参数】:  lparam:无类型指针,可以通过此参数,向线程中传入需要用到的资源。
      在这里我们将CTCPClient_CE类实例指针传进来
【出口参数】:  (无)
【返回  值】:  返回值没有特别的意义,在此我们将返回值设为0。
---------------------------------------------------------------------*/
DWORD CTCPClient_CE::SocketThreadFunc(LPVOID lparam)
{
 
 CTCPClient_CE *pSocket;
 //得到CTCPClient_CE实例指针
 pSocket = (CTCPClient_CE*)lparam;
 //定义读事件集合
 fd_set fdRead;
 int ret;
 //定义事件等待时间
 TIMEVAL aTime;
 aTime.tv_sec = 1;
 aTime.tv_usec = 0;
 while (TRUE)
 {
        //收到退出事件,结束线程
  if (WaitForSingleObject(pSocket->m_exitThreadEvent,0) == WAIT_OBJECT_0)
  {
   break;
  }
  //置空fdRead事件为空
  FD_ZERO(&fdRead);
  //给客户端socket设置读事件
  FD_SET(pSocket->m_socket,&fdRead);
  //调用select函数,判断是否有读事件发生
  ret = select(0,&fdRead,NULL,NULL,&aTime);
  
  if (ret == SOCKET_ERROR)
  {
   //触发错误事件
   pSocket->OnError(pSocket->m_pOwnerWnd,1);
   //触发连接断开事件
   pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
   //关闭客户端socket
   closesocket(pSocket->m_socket);
   break;
  }
  
  
  if (ret > 0)
  {
   if (FD_ISSET(pSocket->m_socket,&fdRead))
   {
    //When the fdRead is set,read data from server
    //used to monitor the progress of a receive operation
    int dataLength,cbBytesRet,cbLeftToReceive;
    //pointer to buffer for receiving data
    //memory is allocated after obtaining file size
    BYTE *recdData=NULL;
    
    CFileException fe;
    BOOL bFileIsOpen=FALSE;
    CFile destFile(_T("//profiles//third.bmp"), CFile::modeCreate | CFile::modeWrite|CFile::typeBinary   );
   
    //get the file's size first
    cbLeftToReceive=sizeof(dataLength);

    do{
     char *bp=(char*)(&dataLength)+sizeof(dataLength)-cbLeftToReceive;
     cbBytesRet=recv(pSocket->m_socket,bp,cbLeftToReceive,0);
     
     //test for errors and get out if they occurred
     if(cbBytesRet==SOCKET_ERROR||cbBytesRet==0){
      int iError = WSAGetLastError();
      //触发socket错误事件
      pSocket->OnError(pSocket->m_pOwnerWnd,iError);
      //触发与服务器断开事件
      pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
      //关闭客户端socket
      closesocket(pSocket->m_socket);
      break;
     }
     cbLeftToReceive-=cbBytesRet;
    }
    while(cbLeftToReceive>0);
    dataLength=ntohl(dataLength);


    //now get the file in 4096 chunks at a time

    recdData = new byte[1024];
    cbLeftToReceive=dataLength;
    do{
     int iiGet,iiRecd;
     iiGet=(cbLeftToReceive<1024)?cbLeftToReceive:1024;

     iiRecd=recv(pSocket->m_socket,(char*)recdData,iiGet,0);
     //test for errors and get out if they occurred

     if(iiRecd==SOCKET_ERROR||iiRecd==0){
      int iError = WSAGetLastError();
      //触发socket错误事件
      pSocket->OnError(pSocket->m_pOwnerWnd,iError);
      //触发与服务器断开事件
      pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
      //关闭客户端socket
      closesocket(pSocket->m_socket);
      break; 
     }
      
     destFile.Write(recdData,iiRecd);//write it
     cbLeftToReceive-=iiRecd;
     Sleep(100);
    }
    while(cbLeftToReceive>0);

    //AfxMessageBox(_T("RECEIVE OK"),MB_YESNO|MB_ICONSTOP);
    delete[]recdData;
    if(bFileIsOpen)
     destFile.Close();
    //Read the POINT data
    char *recvBuf=new char[4*sizeof(POINT)];
    int recvLen;
    recvLen=recv(pSocket->m_socket,recvBuf,4*sizeof(POINT),0);
    if(recvLen==SOCKET_ERROR||recvLen==0){
     int iError = WSAGetLastError();
      //触发socket错误事件
      pSocket->OnError(pSocket->m_pOwnerWnd,iError);
      //触发与服务器断开事件
      pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
      //关闭客户端socket
      closesocket(pSocket->m_socket);
    }
    else
    {
       //触发数据接收事件
        pSocket->OnRead(pSocket->m_pOwnerWnd,recvBuf/*,recvLen*/);
    }
    //发生读事件
     /*char  recvBuf[20];
    DWORD *nBytesWrite=0;
    int  rcv,fileLen;
    int temp;
    rcv=recv(pSocket->m_socket,recvBuf,20,0);
    fileLen=atoi(recvBuf);  //Get file length
    
    //char* recvBuf2=new char[1024];
    char recvBuf2[1024];
    while (1)
    {
     rcv = recv(pSocket->m_socket, recvBuf2, 1024, 0);
     if (rcv == 0)
     {
       break;
     }
     file.Write(recvBuf2, rcv);
     temp+=rcv;
    }
    //file.Close();
    if (temp == fileLen)
    {
     AfxMessageBox(_T("文件接收成功!"));
    }
     else
     {
      AfxMessageBox(_T("文件接收失败!"));
     }
    
     if (recvLen == SOCKET_ERROR)
     {
      int iError = WSAGetLastError();
     //触发socket错误事件
      pSocket->OnError(pSocket->m_pOwnerWnd,iError);
     //触发与服务器断开事件
      pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
     //关闭客户端socket
      closesocket(pSocket->m_socket);
      break;
     }
     else if (recvLen == 0)
    {
     //触发与服务器端断开事件
      pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
     //关闭客户端socket
      closesocket(pSocket->m_socket);
      break;
    }
    else
    {
       //触发数据接收事件
        pSocket->OnRead(pSocket->m_pOwnerWnd,recvBuf,recvLen);
    }
    //}  */
   }
  }
 }
 return 0;
}

 (文件传输部分主要参考 Mike O'Neill  Network Transfer Of Files Using MFC's CSocket Class

  网址:http://www.codeproject.com/internet/SocketFileTransfer.asp

上面的源码中,有一Sleep(100)的语句。它很短,但在这里缺起了很大的作用。我几乎用了一个星期的时间才把它用在正确的地方。

 如果不用Sleep(100),用WSAGetLastError()会返回一个10035号的错误。MSDN对它的解释为:

   Resource temporarily unavailable. This error is returned from operations on nonblocking     sockets that   cannot be completed immediately, for example recv when no data is queued to be read from the socket. It is a nonfatal error, and the operation should be retried later. It is normal for WSAEWOULDBLOCK to be reported as the result from calling connect on a nonblocking SOCK_STREAM socket, since some time must elapse for the connection to be established.

     结合着用了Sleep(100)后,我是这样理解的。在服务器端向客户端传数据的时候,有一个outbuffer用来存储待传输的数据。因为传输数据的速度很快。所以,如果客户端没有及时地把数据取走的话。outbuffer就很快被写满,再没有空于的空间给服务器端放发送的数据。这样,就会发出10035的错误。这也是在实际运用过程中,为什么在未加Sleep之前,传数据总是传到大约48k的时候就出错的原因。在加了sleep过后,就会有足够多的时间让客户端取走数据。这样服务器端和客户端就很好地协调起来。

 ②在客户端的显示设备上,显示从服务器端传来的图片,并显示。

       这里,首先要对位图的知识做相对熟悉的了解。

      关于BMP的资料,可以参考http://asp.6to23.com/iseesoft/devdoc/imgdoc/bmp.htm,里面有详细的介绍。

       里面的内容主要有:

  •     关于位图
  •        位图类型
  •        位图、设备描述表、和绘图表面
  •        位图旋转
  •        位图伸缩
  •        用于画刷的位图
  •        位图存储
  •     使用位图
  •        捕捉图像
  •        拉伸或压缩一幅图像
  •        保存一幅图像
  •      附录:与位图相关的函数及结构
  •           然后,关于调色板也应有些了解。你可以从 http://www.lzu.edu.cn/netteach/jiaochen/vc++5.0/vc++5.0/chap11/chap11_1.htm 

              了解到进一步的知识。

                就我自己的应用而言,关于对位图的显示的相关代码如下:

                 (首先是一般的方法)

                  bool CSHBmpDlg::ShowBmp()
                  {
     
                    CPaintDC dc(this);
                    CDC m_dcMem;
                    HBITMAP m_hBmpOld;
                    HBITMAP m_hBmpNew;

                    m_hBmpNew=SHLoadDIBitmap(_T("//Temp//linuxw.bmp"));
                   if(m_hBmpNew==NULL){
                         AfxMessageBox(_T("Failed"));
                         return FALSE;
                         }
                   BITMAP bmInfo;
                  GetObject(m_hBmpNew,sizeof(BITMAP),&bmInfo);
                   VERIFY(m_hBmpOld=(HBITMAP)SelectObject(m_dcMem,m_hBmpNew));
     
                   dc.BitBlt(0,0,bmInfo.bmWidth,bmInfo.bmHeight,&m_dcMem,0,0,SRCCOPY);

                     m_dcMem.SelectObject(m_hBmpOld);
                     delete m_dcMem;
                       return TRUE;

                  }

                   (另外,通过从将图片读入内存,然后从内存中把数据组成位图。我感觉这种是不是高明一点)

                       /*************************************************************************

      Function:  Read (CFile&)

       Purpose:  Reads in the specified DIB file into a global chunk of
        memory.

       Returns:  Number of read bytes.

    *************************************************************************/

    DWORD CDib::Read(CFile& file)
    {
     // Ensures no memory leaks will occur
     Free();
     
     BITMAPFILEHEADER bmfHeader;

     // Go read the DIB file header and check if it's valid.
     if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) != sizeof(bmfHeader))
      return 0;
     if (bmfHeader.bfType != DIB_HEADER_MARKER)
      return 0;
     DWORD dwReadBytes = sizeof(bmfHeader);

     // Allocate memory for DIB
     m_pBMI = (LPBITMAPINFO)LocalAlloc(LPTR, bmfHeader.bfOffBits-sizeof(BITMAPFILEHEADER) + 256*sizeof(RGBQUAD));
     if (!m_pBMI)
      return 0;

     // Read header.
     if (file.Read(m_pBMI, bmfHeader.bfOffBits-sizeof(BITMAPFILEHEADER)) != (UINT)(bmfHeader.bfOffBits-sizeof(BITMAPFILEHEADER)))
     {
      LocalFree(m_pBMI);
      m_pBMI = NULL;
      return 0;
     }
     dwReadBytes += bmfHeader.bfOffBits-sizeof(BITMAPFILEHEADER);

     DWORD dwLength = file.GetLength();
     // Go read the bits.
     m_pBits = (LPBYTE)LocalAlloc(LPTR, dwLength - bmfHeader.bfOffBits);
     if (m_pBits == 0)
     {
      LocalFree(m_pBMI);
      m_pBMI = NULL;
      return 0;
     }
     
     if (file.ReadHuge(m_pBits, dwLength-bmfHeader.bfOffBits) != (dwLength - bmfHeader.bfOffBits))
     {
      LocalFree(m_pBMI);
      m_pBMI = NULL;
      LocalFree(m_pBits);
      m_pBits = NULL;
      return 0;
     }
     dwReadBytes += dwLength - bmfHeader.bfOffBits;

     CreatePalette();
     return dwReadBytes;
    }

       /*************************************************************************
     *
     * CreatePalette()
     *
     * Return Value:
     *
     * TRUE if succesfull, FALSE otherwise
     *
     * Description:
     *
     * This function creates a palette from a DIB by allocating memory for the
     * logical palette, reading and storing the colors from the DIB's color table
     * into the logical palette, creating a palette from this logical palette,
     * and then returning the palette's handle. This allows the DIB to be
     * displayed using the best possible colors (important for DIBs with 256 or
     * more colors).
     *
     ************************************************************************/

    BOOL CDib::CreatePalette()
    {
     if (!m_pBMI)
      return FALSE;

       //get the number of colors in the DIB
       WORD wNumColors = NumColors();

       if (wNumColors != 0)
       {
      // allocate memory block for logical palette
          
         //HANDLE hLogPal = ::GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY)*wNumColors);
           
      // if not enough memory, clean up and return NULL
      //if (hLogPal == 0)
      // return FALSE;

      //LPLOGPALETTE lpPal = (LPLOGPALETTE)::GlobalLock((HGLOBAL)hLogPal);
            LPLOGPALETTE lpPal=(LPLOGPALETTE)LocalAlloc(LPTR,sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*wNumColors);
      // set version and number of palette entries
      lpPal->palVersion = PALVERSION;
      lpPal->palNumEntries = (WORD)wNumColors;

      for (int i = 0; i < (int)wNumColors; i++)
      {
       lpPal->palPalEntry[i].peRed = m_pBMI->bmiColors[i].rgbRed;
       lpPal->palPalEntry[i].peGreen = m_pBMI->bmiColors[i].rgbGreen;
       lpPal->palPalEntry[i].peBlue = m_pBMI->bmiColors[i].rgbBlue;
       lpPal->palPalEntry[i].peFlags = 0;
      }

      /* create the palette and get handle to it */
      if (m_pPalette)
      {
       m_pPalette->DeleteObject();
       delete m_pPalette;
      }
      m_pPalette = new CPalette;
      BOOL bResult = m_pPalette->CreatePalette(lpPal);
            LocalFree((HLOCAL)lpPal);
      //::GlobalUnlock((HGLOBAL) hLogPal);
      //::GlobalFree((HGLOBAL) hLogPal);
      return bResult;
     }

     return TRUE;
    }

     

      ③实现一个启动动画。

       因为我做的是基于对话框的,下面就基于对话框做些介绍。大家也可以参考http://www.seven.net.cn/Article_Print.asp?ArticleID=112

      1.利用组件库中的Splash Screen组件实现
      (1)用Photoshop等制作启动画面图像,保存为bmp格式。
      (2)用Appwizard建一个基于单文档的工程Splash。 
      (3)在资源中插入位图资源
      打开VC++的资源编辑器,用鼠标右键单击Resources文件夹,选择Import命令,插入所制作
      的位图。如果位图超过256色,VC会弹出一个对话框,提示位图已经插入但不能在位图编辑
      器中显示,确定即可。将位图ID改为IDB_SPLASH。
      (4)添加Splash Screen控件
       ①选择菜单“project”/“Add To Project”/“Conponents and Controls”打开对话框
       ,在列表框中双击“Visual C++ Conponents”选项,选择“Splash Screen”控件,然后
       单击“Insert”。
        ②确认或修改类名和位图资源ID,单击OK确认。
        ③编译、连接,漂亮的启动画面就显示出来了。
       (5)如果需要改变启动画面的停留时间,就修改SetTimer()函数的第二个参数,默认是750
         毫秒。该函数所在位置:
    int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
     ...
       // Set a timer to destroy the splash screen.
       SetTimer(1, 750, NULL); //修改第二个参数以调整画面停留时间
       return 0;
    }
    2)建立基于对话框的工程Cover。
    (3)文件移植
    ①将Splash1.cpp 和Splash1.h 两个文件从方法一建立的Splash工程拷贝到Cover工程中,
    并且分别加入到Source Files和Header Files中;
    ②导入位图文件到工程的资源中,改ID为IDB_SPLASH。
    (4)修改代码,实现启动画面的调用
    ①添加CCoverApp 的InitInstance() 函数代码
    #include "Splash1.h" //加在Cover.cpp文件的头文件调用部位
    BOOL CCoverApp::InitInstance()
    {
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
    CSplashWnd::EnableSplashScreen(cmdInfo.m_bShowSplash);
    ...
    }
    ②使用ClassWizard 添加OnCreate() 函数到对话框类CCoverDlg中,并修改代码
    #include "Splash1.h" //加在CoverDlg.cpp文件的头文件调用部位
    int CCoverDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    ...
    CSplashWnd::ShowSplashScreen(this); //显示启动画面
    ...
    }

    在vc下,用红色标记的部分能够实现。但是,在evc中,则需要将CSplashWnd::ShowSplashScreen(this);

    放入OnInitDialog()中才能实现。

        上面就是主要的部分。

         另外,大家可以参考:

          CDC使用技巧之最快最方便的实现放大缩小功能(http://www.softb2b.net/info/35439.htm)

           Add Zoom and Scale Capabilities to CScrollView ( http://www.codeguru.com/Cpp/W-D/doc_view/scrolling/article.php/c3345)

           关于图像的一些算法,可以参考http://asp.6to23.com/iseesoft/now.htm。

           在你的MFC应用程序中显示一个JPG文件(http://www.vckbase.com/document/viewdoc/?id=1028)

          (完)


最后

以上就是简单黄豆为你收集整理的对近段时间在EVC下学习情况的总结的全部内容,希望文章能够帮你解决对近段时间在EVC下学习情况的总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部