posts - 495,comments - 227,trackbacks - 0
          根據前面一節的說明,服務端套接字應該按照如下順序建立:
          1. 初始化
          2. 創建套接字
          3. 綁定本地地址
          4. 進入偵聽狀態
          5. 處理接受循環

          下面首先創建一個例子來演示服務端套接字的實現,并在以后的各節中優化這個設計。

          這個設計實現的功能如下:允許客戶端(實際上就是telnet程序)登陸,并對客戶端的輸入回顯。

          4.1 準備工程

          為了實現這個demo,我打算使用Visual Studio 6.0提供的對話框模板來實現,請按照下述步驟準備工程:

          1. 啟動Visual C++ 6.0
          2. 選擇File/New/Project
          3. 選擇MFC AppWizard(exe)
          4. 在Project name中輸入demo1, Next
          5. 選擇Dialog Base, Next
          6. 在Windows Sockets前打鉤
          7. 其他都保持缺省值,點擊Next完成向導
          8. 在工程中加入兩個文件:sockutil.h和sockutil.cpp:這兩個文件將保存公共的函數,避免以后重復編寫。

          這樣創建的工程會自動加入WinSock2支持,具體的內容包括:

          1. 在stdafx.h中加入#include <afxsock.h>,該頭文件包含如下語句載入對應LIB:
            #pragma comment(lib, "wsock32.lib")
          2. 在InitInstance中加入如下代碼:
            if(!AfxSocketInit())
            {
            AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
            return FALSE;
            }
          3. 在資源中定義字符串資源IDP_SOCKETS_INIT_FAILED:Windows通訊端口初始化失敗

          如果大家創建工程時沒有加入對應的sock支持,我們可以參照上述列表手工加入。

          對于前文所述的sockutil.h文件,加入如下初始代碼:

          #if !defined(__SOCKET_UTILITY_HEADER__)
          #define __SOCKET_UTILITY_HEADER__
          #if _MSC_VER > 1000
          #pragma once
          #endif // _MSC_VER > 1000
          #endif

          對于sockutil.cpp文件,則加入如下初始代碼:

          #include "stdafx.h"
          #include "sockutil.h"
          #ifdef _DEBUG
          #define new DEBUG_NEW
          #undef THIS_FILE
          static char THIS_FILE[] = __FILE__;
          #endif

          4.2 準備錯誤處理方式

          在編制每個程序之前,我習慣是為錯誤處理定制一個統一的處理方式。在以后的例子中,我會按照如下的方式處理錯誤:在一個log文件中記錄錯誤發生的位置(文件、行號),錯誤號和字符串解釋。為了實現這個目的,我在sockutil.cpp定義如下的函數:

          bool ErrorHandle(LPCTSTR expression, bool bFalse, LPCTSTR file, UINT line)
          {
          if(!bFalse)
          {//沒有錯誤,直接返回
          return false;
          }
          FILE * fp;
          fp = fopen("demo.log","at");
          if(NULL == fp)
          {//如果文件打開失敗,放棄記錄
          return true;
          }
          //獲得錯誤碼
          DWORD ErrorCode = GetLastError();
          //格式化成字符串格式
          LPVOID lpMsgBuf;
          FormatMessage(
          FORMAT_MESSAGE_ALLOCATE_BUFFER |
          FORMAT_MESSAGE_FROM_SYSTEM |
          FORMAT_MESSAGE_IGNORE_INSERTS,
          NULL,
          ErrorCode ,
          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
          (LPTSTR) &lpMsgBuf,
          0,
          NULL
          );
          //輸出到文件
          fprintf(fp,_T("file=%s,line=%u"n%d:%s"n"),file,line,ErrorCode, (LPCTSTR)lpMsgBuf);
          // 釋放空間,該空間由FormatMessage分配
          LocalFree( lpMsgBuf );
          //關閉文件
          fclose(fp);

          return true;
          }

          在sockutil.h中,添加如下代碼:

          bool ErrorHandle(LPCTSTR expression, bool bFalse, LPCTSTR file, UINT line);
          #define ERRORHANDLE(expression) ErrorHandle(#expression,(expression),__FILE__,__LINE__)

          4.3 核心代碼

          根據前文,設計該服務端套接字主函數如下:

          void sockmain(LPCTSTR ip, UINT port)
          {

          SOCKET hSocket;
          hSocket = socket(AF_INET,SOCK_STREAM,0);
          if(ERRORHANDLE(hSocket == INVALID_SOCKET))
          {
          return;
          }
          sockaddr_in addr;
          InitializeAddress(inet_addr(ip), port, addr);
          if(ERRORHANDLE(SOCKET_ERROR == bind(hSocket, (const sockaddr*) & addr, sizeof(addr))))
          {
          closesocket(hSocket);
          return;
          }
          if(ERRORHANDLE(SOCKET_ERROR == listen(hSocket,5)))
          {
          closesocket(hSocket);
          return;
          }
          SOCKET hClient;
          int size;
          char buffer[2048];
          int length;
          size = sizeof(addr);
          while(INVALID_SOCKET != (hClient = accept(hSocket,(sockaddr*)&addr, & size)))
          {
          size = sizeof(addr);
          while((length = recv(hClient, buffer, sizeof(buffer),0)) > 0)
          {
          SendData(hClient,buffer, length);
          }
          closesocket(hClient);
          }
          closesocket(hSocket);

          return;
          }

          其中,InitializeAddress定義如下:

          void InitializeAddress(DWORD ip, UINT port, sockaddr_in & addr)
          {
          memset(&addr,0,sizeof(addr));
          addr.sin_family = AF_INET;
          addr.sin_addr.s_addr= ip;
          addr.sin_port = htons(port);
          }
          SendData定義如下:
          int SendData(SOCKET hSocket, const char * data, int length)
          {
          int result;
          int pos = 0;
          while(pos < length)
          {
          result = send(hSocket, data + pos, length - pos , 0);
          if(result > 0 )
          {
          pos += result;
          }else{
          return result;
          }
          }
          return length;
          }

          4.4 啟動該任務

          為了啟動服務端套接字,我們可以在對話框資源的OK按鈕上雙擊,然后在OnOK中添加如下代碼

             sockmain("0.0.0.0",2000);

          當我們啟動該工程,并點擊OK按鈕,就可以通過telnet來測試是否有回顯功能了。

          posted on 2008-07-03 15:19 SIMONE 閱讀(637) 評論(0)  編輯  收藏 所屬分類: C++
          主站蜘蛛池模板: 九龙坡区| 平谷区| 锡林郭勒盟| 阜南县| 娄烦县| 金平| 嵊州市| 德阳市| 肃北| 万载县| 从化市| 林芝县| 黑山县| 宜城市| 兰考县| 太白县| 山阳县| 麦盖提县| 施甸县| 宜章县| 舞钢市| 苏尼特右旗| 武宁县| 晋州市| 密云县| 宜城市| 富锦市| 东乡| 新泰市| 密山市| 安宁市| 芦溪县| 兴文县| 长汀县| 晋宁县| 兰溪市| 高青县| 昆明市| 洱源县| 阿坝| 湘西|