MFC下CSocket编程详解

1. 常用的函数和注意事项(详细的函数接口说明请查看MSDN):

    CSocket::Create 初始化(一般写服务器程序都不要用为好,用下面的 CSocket::Socket 初始化)

   CSocket::Socket初始化

    CSocket::SetSockOpt 设置socket选项

    CSocket::Bind 绑定地址端口

    CSocket::Connect 连接

    CSocket::Listen  监听

    CSocket::Accept 接收外部连接的socket
 
    CSocket::Send 发送内容

    CSocket::Receive 接收内容

    CSocket::Close 关闭(不等于delete)

    1) 在使用MFC编写socket程序时,必须要包含<afxsock.h>都文件。

    2) AfxSocketInit() 这个函数,在使用CSocket前一定要先调用该函数,否则使用CSocket会出错;并且该函数还有一个重要的使用方式,
       就是在某个线程下使用 CSocket 前一定要调用,就算主线程调用了该函数,在子线程下使用 CSocket 也要先调用该函数,要不会出错。

    3) 还要注意的是, Create 方法已经包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再调用 Bind ,要不一定出错。

2. 以下是使用例子代码,通过例子来学习如何使用 CSocket 进行编程, 并且附件上有完整的例子代码。例子的可以在我的发布资源中找到:MFC下CSocket编程例子 http://download.csdn.net/source/379597

    1) 客户端主要代码:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //初始化  
  2. AfxSocketInit();  
  3.    
  4.        //创建 CSocket 对象  
  5. CSocket aSocket;  
  6.   
  7. CString strIP;  
  8. CString strPort;  
  9. CString strText;  
  10.   
  11. this->GetDlgItem(IDC_EDIT_IP)->GetWindowText(strIP);  
  12. this->GetDlgItem(IDC_EDIT_PORT)->GetWindowText(strPort);  
  13. this->GetDlgItem(IDC_EDIT_TEXT)->GetWindowText(strText);  
  14.   
  15. //初始化 CSocket 对象, 因为客户端不需要绑定任何端口和地址, 所以用默认参数即可  
  16. if(!aSocket.Create())  
  17. {  
  18.  char szMsg[1024] = {0};  
  19.   
  20.  sprintf(szMsg, "create faild: %d", aSocket.GetLastError());  
  21.   
  22.  AfxMessageBox(szMsg);  
  23.  return;  
  24. }  
  25.   
  26. //转换需要连接的端口内容类型  
  27. int nPort = atoi(strPort);  
  28.   
  29.        //连接指定的地址和端口  
  30. if(aSocket.Connect(strIP, nPort))  
  31. {  
  32.  char szRecValue[1024] = {0};  
  33.   
  34.                //发送内容给服务器  
  35.  aSocket.Send(strText, strText.GetLength());  
  36.    
  37.  //接收服务器发送回来的内容(该方法会阻塞, 在此等待有内容接收到才继续向下执行)  
  38.  aSocket.Receive((void *)szRecValue, 1024);  
  39.   
  40.  AfxMessageBox(szRecValue);  
  41. }  
  42. else  
  43. {  
  44.  char szMsg[1024] = {0};  
  45.    
  46.  sprintf(szMsg, "create faild: %d", aSocket.GetLastError());  
  47.    
  48.  AfxMessageBox(szMsg);  
  49. }  
  50.   
  51. //关闭  
  52. aSocket.Close();  

    2)服务器端代码:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. unsigned int StartServer(LPVOID lParam)  
  2. {  
  3.         //初始化Winscok  
  4.     if (!AfxSocketInit())  
  5.     {  
  6.         AfxMessageBox(IDP_SOCKETS_INIT_FAILED);  
  7.         return 1;  
  8.     }  
  9.   
  10.     m_exit = false;  
  11.   
  12.     CServerDlg *aDlg = (CServerDlg *)lParam;  
  13.   
  14.     CString strPort;  
  15.       
  16.     aDlg->GetDlgItemText(IDC_EDIT_PORT, strPort);  
  17.       
  18.     UINT nPort = atoi(strPort);  
  19.       
  20.     //socket------------------------------------------------  
  21.       
  22.     CSocket aSocket, serverSocket;  
  23.     //最好不要使用aSocket.Create创建,因为容易会出现10048错误  
  24.     if (!aSocket.Socket())  
  25.     {  
  26.         char szError[256] = {0};  
  27.           
  28.         sprintf(szError, "Create Faild: %d", GetLastError());  
  29.           
  30.         AfxMessageBox(szError);  
  31.           
  32.         return 1;   
  33.     }  
  34.   
  35.     BOOL bOptVal = TRUE;  
  36.     int bOptLen = sizeof(BOOL);  
  37.   
  38.      //设置Socket的选项, 解决10048错误必须的步骤  
  39.     aSocket.SetSockOpt(SO_REUSEADDR, (void *)&bOptVal, bOptLen, SOL_SOCKET);  
  40.         //监听  
  41.     if(!aSocket.Listen(10))  
  42.     {      
  43.         char szError[256] = {0};  
  44.           
  45.         sprintf(szError, "Listen Faild: %d", GetLastError());  
  46.           
  47.         AfxMessageBox(szError);  
  48.           
  49.         return 1;  
  50.     }  
  51.       
  52.     CString strText;  
  53.       
  54.     aDlg->GetDlgItemText(IDC_EDIT_LOG, strText);  
  55.       
  56.     strText += "Server Start!  ";  
  57.       
  58.     aDlg->SetDlgItemText(IDC_EDIT_LOG, strText);  
  59.   
  60.     while(!m_exit)  
  61.     {  
  62.         //接收外部连接  
  63.         if(!aSocket.Accept(serverSocket))  
  64.         {  
  65.             continue;  
  66.         }  
  67.         else  
  68.         {  
  69.             char szRecvMsg[256] = {0};  
  70.             char szOutMsg[256] = {0};      
  71.               
  72.                 //接收客户端内容:阻塞  
  73.             serverSocket.Receive(szRecvMsg, 256);  
  74.   
  75.             sprintf(szOutMsg, "Receive Msg: %s  ", szRecvMsg);  
  76.               
  77.             aDlg->GetDlgItemText(IDC_EDIT_LOG, strText);  
  78.               
  79.             strText += szOutMsg;  
  80.               
  81.             aDlg->SetDlgItemText(IDC_EDIT_LOG, strText);  
  82.        
  83.                 //发送内容给客户端  
  84.             serverSocket.Send("Have Receive The Msg", 50);  
  85.                 //关闭  
  86.             serverSocket.Close();  
  87.         }  
  88.           
  89.     }  
  90.       
  91.         //关闭  
  92.     aSocket.Close();  
  93.     serverSocket.Close();  
  94.       
  95.     aDlg->GetDlgItemText(IDC_EDIT_LOG, strText);  
  96.       
  97.     strText += "Have Close!";  
  98.       
  99.     aDlg->SetDlgItemText(IDC_EDIT_LOG, strText);  
  100.   
  101.     return 0;  
  102. }  
  103.   
  104.       
  105.     //绑定端口  
  106.     if (!aSocket.Bind(nPort))  
  107.     {  
  108.         char szError[256] = {0};  
  109.               
  110.         sprintf(szError, "Bind Faild: %d", GetLastError());  
  111.               
  112.         AfxMessageBox(szError);  
  113.               
  114.         return 1;   
  115.     }  
  116.     

   3) SDK 下的服务器端代码

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.  //子线程函数  
  2.       unsigned int StartServer(LPVOID lParam)  
  3.       {  
  4.          
  5.       //初始化Winsock, AfxSocketInit() 也是封装了这些语句, 不过 AfxSocketInit() 所做的事比这里多些  
  6.   
  7.    WSADATA wsaData;  
  8.     
  9.           //Winsock 的版本, 建议用1.1 ,兼容性好  
  10.    WORD wVersionRequested = MAKEWORD(1, 1);  
  11.    int nResult = WSAStartup(wVersionRequested, &wsaData);  
  12.    if (nResult != 0)  
  13.    {  
  14.  return 1;  
  15.    }  
  16.   
  17.       //-----------------------------------------------------   
  18.   
  19.           m_exit = false;  
  20.   
  21.    CServerDlg *aDlg = (CServerDlg *)lParam;  
  22.   
  23.    CString strPort;  
  24.   
  25.    aDlg->GetDlgItemText(IDC_EDIT_PORT, strPort);  
  26.   
  27.    UINT nPort = atoi(strPort);  
  28.   
  29. //socket------------------------------------------------  
  30.             
  31.           //接口对象  
  32.           SOCKET aSocket, serverSocket;  
  33.   
  34.           //寻址相关结构  
  35.    sockaddr_in serverSockaddr;  
  36.    memset(&serverSockaddr, 0, sizeof(serverSockaddr));  
  37.   
  38.   
  39.    aSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  40.   
  41.    if (aSocket == INVALID_SOCKET)  
  42.    {  
  43.  char szError[256] = {0};  
  44.    
  45.  sprintf(szError, "Create Faild: %d", GetLastError());  
  46.    
  47.  AfxMessageBox(szError);  
  48.    
  49.  return 1;   
  50.    }  
  51.   
  52.    //注意,该处非常重要,取值的正确与否决定关闭scoket后端口是否能正常释放  
  53.    BOOL bOptVal = TRUE;  
  54.    int bOptLen = sizeof(BOOL);  
  55.             
  56.            //设置 socket 选项, SOL_SOCKET 和 SO_REUSEADDR 一起使用, 并且后面的参数如上,   
  57.              关闭scoket后端口便能正常释放  
  58.     setsockopt(aSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&bOptVal, bOptLen);   
  59.   
  60.           //寻址相关结构  
  61.    sockaddr_in aSockaddr;  
  62.    memset(&aSockaddr,0,sizeof(aSockaddr));  
  63.   
  64.    aSockaddr.sin_family = AF_INET;  
  65.   
  66.    aSockaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  67.   
  68.    aSockaddr.sin_port = htons((u_short)nPort);  
  69.   
  70.           //绑定: 注意参数的类型转换  
  71.    if(bind(aSocket,(sockaddr *)&aSockaddr, sizeof(aSockaddr)) == SOCKET_ERROR)  
  72.    {  
  73.  char szError[256] = {0};  
  74.    
  75.  sprintf(szError, "Bind Faild: %d", GetLastError());  
  76.    
  77.  AfxMessageBox(szError);  
  78.    
  79.  return 1;   
  80.    }  
  81.   
  82.   
  83.    //监听  
  84.    if(listen(aSocket, 10) == SOCKET_ERROR)  
  85.    {   
  86.  char szError[256] = {0};  
  87.    
  88.  sprintf(szError, "Listen Faild: %d", GetLastError());  
  89.    
  90.  AfxMessageBox(szError);  
  91.    
  92.  return 1;  
  93.    }  
  94.   
  95.    CString strText;  
  96.   
  97.    aDlg->GetDlgItemText(IDC_EDIT_LOG, strText);  
  98.   
  99.    strText += "Server Start!  ";  
  100.   
  101.    aDlg->SetDlgItemText(IDC_EDIT_LOG, strText);  
  102.   
  103.    while(!m_exit)  
  104.    {  
  105.  //接收外部连接, 非阻塞  
  106.  serverSocket = accept(aSocket, (sockaddr *)&serverSockaddr, 0);  
  107.     
  108.  if(serverSocket == INVALID_SOCKET)  
  109.  {  
  110.   continue;  
  111.  }  
  112.  else  
  113.  {  
  114.   char szRecvMsg[256] = {0};  
  115.   char szOutMsg[256] = {0};   
  116.     
  117.   //接收客户端内容: 阻塞  
  118.   recv(serverSocket, szRecvMsg, 256, 0);  
  119.   
  120.   sprintf(szOutMsg, "Receive Msg: %s  ", szRecvMsg);  
  121.     
  122.   aDlg->GetDlgItemText(IDC_EDIT_LOG, strText);  
  123.     
  124.   strText += szOutMsg;  
  125.   
  126.   aDlg->SetDlgItemText(IDC_EDIT_LOG, strText);  
  127.   
  128.                        //发送内容给客户端  
  129.   send(serverSocket, "Have Receive The Msg", 50, 0);  
  130.   
  131.                        //关闭  
  132.   closesocket(serverSocket);  
  133.  }  
  134.    
  135.    }  
  136.   
  137.    //关闭  
  138.    closesocket(aSocket);  
  139.    closesocket(serverSocket);  
  140.   
  141.    aDlg->GetDlgItemText(IDC_EDIT_LOG, strText);  
  142.   
  143.    strText += "Have Close!";  
  144.   
  145.    aDlg->SetDlgItemText(IDC_EDIT_LOG, strText);  
  146.   
  147.           //当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放  
  148.    WSACleanup();  
  149.   
  150.    return 0;  
  151.       }  

3. 总结
   1) MFC进行编程的确比较简单, 用的代码比较少, 又容易管理。唯一不好的地方在于很多细节上的东西在资料上不容易查出来, 关联性非常紧密, 象 AfxSocketInit() 函数就是,函数的实现里包含着很多不容易理解的类, 并且记录了非常多的环境信息, 比如创建的线程等等, 这样在主线程调用后子线程没有调用执行 CSocket 的操作就会出错。还有就是有些接口的设计非常离奇, 象 CSocket::Create 的接口就是, 实现上还执行了 CSocket::Bind , 非常不容易被发现。并且MSDN上对 CSocket::Bind 的说明又明显的提示需要显示执行 CSocket::Bind 操作。

   2) SDK 编程能理解函数的调用顺序和代码的结构就比较容易,省去了MFC下封装了不知道什么东西的部分,使得代码的流程容易控制。但是从上面的例子来看非常明显的并且不是那么容易理解。不仅仅有很多奇怪的结构(微软的命名一直如此, 无所云云), 并且函数相关太过于紧密, 初学者想一下子熟悉使用并不容易, 对开发者来说代码管理起来非常麻烦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值