• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 45  文章 - 129  trackbacks - 0
            <2010年4月>
            28293031123
            45678910
            11121314151617
            18192021222324
            2526272829301
            2345678

            專注于C++ P2P STL GP OpenSource等
            Google

            常用鏈接

            留言簿(10)

            隨筆分類

            隨筆檔案

            相冊(cè)

            朋友

            • .NET

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            轉(zhuǎn)貼自:http://blog.csdn.net/dylgsy/

            查找了很多資料都找不到select模型的詳細(xì)用法,《Windows網(wǎng)絡(luò)編程》這本書上也只是寫了一個(gè)簡(jiǎn)單的回應(yīng)服務(wù)器,就連writefds的用法都沒講,也不知道什么時(shí)候利用“可寫”來發(fā)文件。這些都是我的疑問,相信很多研究網(wǎng)絡(luò)編程的同路人也碰到了我的這些問題。這些疑問在這篇文章中都解決了!耗費(fèi)了偶很多的精力去猜測(cè)去思考!?

            感覺一些已經(jīng)得道的高人都不肯把這些問題說透徹點(diǎn),唉,只能靠自己去摸索了,希望這篇文章能對(duì)你有用,也希望一些高人能出來指點(diǎn)指點(diǎn)!

            SOCKET模型的出現(xiàn)是為了解決“一個(gè)客戶端一線程”的問題,為了WINDOWS的線程切換不要太頻繁,我們可以使用WINDOWS的SOCKET模型。但在論壇里又看到了一些文章說現(xiàn)在的計(jì)算機(jī)硬件相當(dāng)發(fā)達(dá),成萬的線程切換根本不是什么問題。無論怎么樣,既然這些模型被MS發(fā)明了出來,就有它的道理,我們還是好好來學(xué)習(xí)一下吧。

            對(duì)于select模型,大家都知道用select函數(shù),然后判斷讀集、寫集。但如何使用select,最終寫好的Server又是一個(gè)怎么樣的結(jié)構(gòu)呢?

            select可以這樣用,寫成兩個(gè)函數(shù),SelectSend和SelectRecv,這兩個(gè)函數(shù)和一般的send\recv不同的地方在于它是有超時(shí)值的,這樣就不會(huì)把程序完全阻塞了。這兩個(gè)函數(shù)大概如下(參考《PC網(wǎng)絡(luò)游戲編程》):

            SelectRecv(...)

            int ?SelectRecv(SOCKET?hSocket,? char ? * pszBuffer,? int ?nBufferSize,?
            ????????DWORD?dwTimeout)
            {
            ????ASSERT(hSocket?
            != ?NULL);
            ????
            if (hSocket == NULL)
            ????????
            return ?(?SOCKET_ERROR?);
            ????FD_SET?fd?
            = ? { 1 ,?hSocket} ;
            ????TIMEVAL?tv?
            = ? {dwTimeout,? 0 } ;
            ????
            int ?nBytesReceived = 0 ;
            ????
            if (select( 0 ,? & fd,?NULL,?NULL,? & tv)? == ? 0 )?
            ????????
            goto ?CLEAR;
            ????
            if ((nBytesReceived? = ?recv(hSocket,?pszBuffer,?nBufferSize,? 0 ))? == ?SOCKET_ERROR)
            ????????
            goto ?CLEAR;
            ????
            return ?nBytesReceived;

            CLEAR:
            ????SetLastError(WSAGetLastError());
            // 超時(shí)
            ???? return (SOCKET_ERROR);
            }

            SelectSend(...)

            int ?SelectSend(SOCKET?hSocket, char ? const ? * ?pszBuffer,?
            ????????
            int ?nBufferSize,?DWORD?dwTimeout)

            {
            ????
            if (hSocket == NULL)
            ????????
            return (SOCKET_ERROR);
            ????
            int ?nBytesSent? = ? 0 ;
            ????
            int ?nBytesThisTime;
            ????
            const ? char * ?pszTemp? = ?pszBuffer;
            ????
            do ? {
            ????????nBytesThisTime?
            = ?Send_Block(hSocket,pszTemp,?nBufferSize - nBytesSent,?dwTimeout);
            ????????
            if (nBytesThisTime < 0 )
            ????????????
            return (SOCKET_ERROR);
            ????????
            // 如果一次沒有發(fā)送成功
            ????????nBytesSent? += ?nBytesThisTime;
            ????????
            // 改變當(dāng)前字符指針
            ????????pszTemp? += ?nBytesThisTime;
            ????}
            ? while (nBytesSent? < ?nBufferSize);
            ????
            return ?nBytesSent;
            }

            這樣就可以利用上面兩個(gè)函數(shù)寫發(fā)送和接收程序了!我們下面來看看如何使用select模型建立我們的Server,下面分了4個(gè)文件,大家可以拷貝下來實(shí)際編譯一下。首先有兩個(gè)公用的文件:CommonSocket.h、CommonCmd.h。他們都是放在 Common Include 目錄中的!然后就是SelectServer.cpp和SocketClient.cpp這兩個(gè)文件,可以分別建立兩個(gè)工程把他們加進(jìn)去編譯!有些關(guān)鍵的地方我都寫在文件注釋里了,可以自己看看,這些是我寫的。為了方便大家拷貝,我就不用“代碼插入”的方式了。直接貼到下面!

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

            第一個(gè)文件

            /*/
            文件:SelectServer.cpp
            說明:

            ?此文件演示了如何使用select模型來建立服務(wù)器,難點(diǎn)是select的writefds在什么時(shí)候使用。
            ?好好看看代碼就能很明白的了,可以說我寫這些代碼就是為了探索這個(gè)問題的!找了很多資料都找不到!!

            ?在這里我懷疑是否可以同時(shí)讀寫同一個(gè)SOCKET,結(jié)果發(fā)現(xiàn)是可以的,但是最好別這樣做。因?yàn)闀?huì)導(dǎo)致包的順序不一致。

            ??? 這里說一下SELECT模型的邏輯:
            ?我們?nèi)绻皇褂胹elect模型,在調(diào)用recv或者send時(shí)候會(huì)導(dǎo)致程序阻塞。如果使用了select
            ?就給我們?cè)黾恿艘粚颖Wo(hù),就是說在調(diào)用了select函數(shù)之后,對(duì)處于讀集合的socket進(jìn)行recv操作
            ?是一定會(huì)成功的(這是操作系統(tǒng)給我們保證的)。對(duì)于判斷SOCKET是否可寫時(shí)也一樣。
            ?而且就算不可讀或不可寫,使用了select也不會(huì)鎖 死!因?yàn)?select 函數(shù)提供了超時(shí)!利用這個(gè)特性還可以
            ?做異步connect,也就是可以掃描主機(jī),看哪個(gè)主機(jī)開了服務(wù)(遠(yuǎn)程控制軟件經(jīng)常這樣干哦!)

            ?我們?nèi)绾卫眠@種邏輯來設(shè)計(jì)我們的server呢?
            ?這里用的方法是建立一個(gè)SocketInfo,這個(gè)SocketInfo包括了對(duì)Socket當(dāng)前進(jìn)行的操作,我把它分為:
            ?{RecvCmd, RecvData, ExecCmd} 一開始socket是處于一個(gè)RecvCmd的狀態(tài),
            ?然后取到了CMD(也就是取到了指令,可想象一下CPU得到了指令后干什么),然后就要取數(shù)據(jù)了,取得指令
            ?知道要干什么,取得了數(shù)據(jù)就可以實(shí)際開始干了。實(shí)際開始干就是ExecCmd,在這個(gè)狀態(tài)之后都是需要
            ?發(fā)送數(shù)據(jù)的了,所以把他們都放在判斷SOCKET可寫下面<就是 if(FD_ISSET(vecSocketInfo[i].sock, &fdWrite)) >,
            ?即當(dāng)Socket可寫就可以發(fā)送信息給客戶端了。

            ?發(fā)送的根本協(xié)議是這樣的:先發(fā)一個(gè)SCommand的結(jié)構(gòu)體過去,這個(gè)結(jié)構(gòu)體說明了指令和數(shù)據(jù)的長(zhǎng)度。
            ?然后就根據(jù)這個(gè)長(zhǎng)度接收數(shù)據(jù)。最后再給客戶端做出相應(yīng)的響應(yīng)!

            ??? 根據(jù)這種代碼結(jié)構(gòu),可以很方便的添加新的功能。

            ? ?錯(cuò)誤處理做得不太好,以后再補(bǔ)充了。

            ?其他的如注釋,結(jié)構(gòu),命名等的編碼規(guī)范都用了個(gè)人比較喜歡的方式。

            輸出:
            ?..\Bin\SelectServer.exe

            用法:
            ?直接啟動(dòng)就可以了

            Todo:
            ?下一步首先完成各個(gè)SOCKET的模型,然后公開自己的研究代碼。
            ?功能方面就是:
            ?1、服務(wù)器可以指定共享文件夾
            ?2、客戶端可以列出服務(wù)器共享了哪些文件
            ?3、客戶端可以列出哪些用戶在線,并可以發(fā)命令和其他用戶聊天
            ?4、加上界面
            /*/

            #include <winsock2.h>
            #pragma comment(lib, "WS2_32")

            #include <windows.h>

            #pragma warning(disable: 4786)
            #include <iostream>
            #include <vector>
            #include <map>
            #include <string>
            #include <algorithm>
            using namespace std;

            #include "..\Common Include\CommonSocket.h"
            #include "..\Common Include\CommonCmd.h"

            typedef struct tagSocketInfo
            {
            ?SOCKET sock;
            ?ECurOp eCurOp;
            ?SCommand cmd;
            ?char *data;
            }SSocketInfo;

            // 登錄用戶的列表
            map<string, SOCKET> g_LoginUsers;

            // 注冊(cè)用戶的列表(用戶名,密碼)
            map<string, string> g_RegUSers;

            // 用于退出服務(wù)器
            bool g_bExit = false;

            void DoRecvCmd(vector<SSocketInfo> &vecSockInfo, int idx);
            void DoRecvData(vector<SSocketInfo> &vecSockInfo, int idx);
            void DoExecCmd(vector<SSocketInfo> &vecSockInfo, int idx);

            bool DoAuthen(SOCKET sock, char *data, DWORD len);
            bool DoGetFile(SOCKET sock, char *data, DWORD len);
            bool DoRegister(SOCKET sock, char *data, DWORD len);

            void GetRegUsers();

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : RemoveByIndex
            // 功能描述???? : 根據(jù) index 來刪除 VECTOR 里的元素
            // 參數(shù)???????? : vector<T> &vec [in]
            // 參數(shù)???????? : int nIdx?? [in]
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            template<class T>
            void EraseByIndex(vector<T> &vec, int nIdx)
            {
            ?vector<T>::iterator it;
            ?it = vec.begin() + nIdx;
            ?vec.erase(it);
            }

            void main()
            {
            ?InitWinsock();

            ?vector<SSocketInfo> vecSocketInfo;

            ?SOCKET sockListen = BindServer(PORT);
            ?ULONG NonBlock = 1;
            ?ioctlsocket(sockListen, FIONBIO, &NonBlock);
            ?
            ?SOCKET sockClient;

            ?GetRegUsers();

            ?FD_SET fdRead;
            ?FD_SET fdWrite;
            ?
            ?while(!g_bExit)
            ?{
            ??// 每次調(diào)用select之前都要把讀集和寫集清空
            ??FD_ZERO(&fdRead);
            ??FD_ZERO(&fdWrite);
            ??
            ??// 設(shè)置好讀集和寫集
            ??FD_SET(sockListen, &fdRead);
            ??for(int i = 0; i < vecSocketInfo.size(); i++)
            ??{
            ???FD_SET(vecSocketInfo[i].sock, &fdRead);
            ???FD_SET(vecSocketInfo[i].sock, &fdWrite);
            ??}

            ??// 調(diào)用select函數(shù)
            ??if(select(0, &fdRead, &fdWrite, NULL, NULL) == SOCKET_ERROR)
            ??{
            ???OutErr("select() Failed!");
            ???break;
            ??}

            ??// 說明可以接受連接了
            ??if(FD_ISSET(sockListen, &fdRead))
            ??{
            ???char szClientIP[50];
            ???sockClient = AcceptClient(sockListen, szClientIP);
            ???cout << szClientIP << " 連接上來" << endl;

            ???ioctlsocket(sockClient, FIONBIO, &NonBlock);

            ???SSocketInfo sockInfo;
            ???sockInfo.sock = sockClient;
            ???sockInfo.eCurOp = RecvCmd;
            ???// 把接收到的這個(gè)socket加入自己的隊(duì)列中
            ???vecSocketInfo.push_back(sockInfo);
            ??}

            ??for(i = 0; i < vecSocketInfo.size(); i++)
            ??{
            ???// 如果可讀
            ???if(FD_ISSET(vecSocketInfo[i].sock, &fdRead))
            ???{
            ????switch(vecSocketInfo[i].eCurOp)
            ????{
            ????case RecvCmd:
            ?????DoRecvCmd(vecSocketInfo, i);
            ?????break;

            ????case RecvData:
            ?????DoRecvData(vecSocketInfo, i);
            ?????break;
            ?????
            ????default:
            ?????break;
            ????}
            ???}

            ???// 如果可寫
            ???if(FD_ISSET(vecSocketInfo[i].sock, &fdWrite))
            ???{
            ????switch(vecSocketInfo[i].eCurOp)
            ????{
            ????case ExecCmd:
            ?????DoExecCmd(vecSocketInfo, i);
            ?????break;
            ????
            ????default:
            ?????break;
            ????}
            ???}
            ??}
            ?}
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : DoRecvCmd
            // 功能描述???? : 獲取客戶端傳過來的cmd
            // 參數(shù)???????? : vector<SSocketInfo> &vecSockInfo
            // 參數(shù)???????? : int idx
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void DoRecvCmd(vector<SSocketInfo> &vecSockInfo, int idx)
            {
            ?SSocketInfo *sockInfo = &vecSockInfo[idx];
            ?int nRet = RecvFix(sockInfo->sock, (char *)&(sockInfo->cmd), sizeof(sockInfo->cmd));

            ?// 如果用戶正常登錄上來再用 closesocket 關(guān)閉 socket 會(huì)返回0
            ?// 如果用戶直接關(guān)閉程序會(huì)返回 SOCKET_ERROR,強(qiáng)行關(guān)閉
            ?if(nRet == SOCKET_ERROR || nRet == 0)
            ?{
            ??OutMsg("客戶端已退出。");
            ??closesocket(sockInfo->sock);
            ??sockInfo->sock = INVALID_SOCKET;?????
            ??EraseByIndex(vecSockInfo, idx);
            ??return;
            ?}
            ?sockInfo->eCurOp = RecvData;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : DoRecvData
            // 功能描述???? : DoRecvCmd 已經(jīng)獲得了指令,接下來就要獲得執(zhí)行指令所需要的數(shù)據(jù)
            // 參數(shù)???????? : vector<SSocketInfo> &vecSockInfo
            // 參數(shù)???????? : int idx
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void DoRecvData(vector<SSocketInfo> &vecSockInfo, int idx)
            {
            ?SSocketInfo *sockInfo = &vecSockInfo[idx];
            ?// 為數(shù)據(jù)分配空間,分配多一位用來放最后的0
            ?sockInfo->data = new char[sockInfo->cmd.DataSize + 1];
            ?memset(sockInfo->data, 0, sockInfo->cmd.DataSize + 1);
            ?
            ?// 接收數(shù)據(jù)
            ?int nRet = RecvFix(sockInfo->sock, sockInfo->data, sockInfo->cmd.DataSize);
            ?if(nRet == SOCKET_ERROR || nRet == 0)
            ?{
            ??OutMsg("客戶端已退出。");
            ??closesocket(sockInfo->sock);
            ??sockInfo->sock = INVALID_SOCKET;?????
            ??EraseByIndex(vecSockInfo, idx);
            ??return;
            ?}
            ??
            ?sockInfo->eCurOp = ExecCmd;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : DoExecCmd
            // 功能描述???? : 指令和執(zhí)行指令所需數(shù)據(jù)都已經(jīng)準(zhǔn)備好了,接下來就可以執(zhí)行命令
            // 參數(shù)???????? : vector<SSocketInfo> &vecSockInfo
            // 參數(shù)???????? : int idx
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void DoExecCmd(vector<SSocketInfo> &vecSockInfo, int idx)
            {
            ?SSocketInfo *sockInfo = &vecSockInfo[idx];
            ?switch(sockInfo->cmd.CommandID)
            ?{
            ?case CMD_AUTHEN:
            ??DoAuthen(sockInfo->sock, sockInfo->data, sockInfo->cmd.DataSize);
            ???break;
            ?case CMD_GETFILE:
            ??DoGetFile(sockInfo->sock, sockInfo->data, sockInfo->cmd.DataSize);
            ??break;
            ?case CMD_REGISTER:
            ??DoRegister(sockInfo->sock, sockInfo->data, sockInfo->cmd.DataSize);
            ??break;
            ?default:
            ??break;
            ?}

            ?// 執(zhí)行完命令后就設(shè)置回接收指令狀態(tài)
            ?sockInfo->eCurOp = RecvCmd;
            }

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : DoAuthen
            // 功能描述???? : 對(duì)用戶名和密碼做驗(yàn)證
            // 參數(shù)???????? : SOCKET sock
            // 參數(shù)???????? : char *data
            // 參數(shù)???????? : DWORD len
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool DoAuthen(SOCKET sock, char *data, DWORD len)
            {
            ?// 取得用戶名和密碼的字符串
            ?// 格式為 "dyl 123"

            ?char *pBuf = data;
            ?int nIdx = 0;
            ?char szName[10];
            ?memset(szName, 0, 10);
            ?char szPass[10];
            ?memset(szPass, 0, 10);
            ?
            ?while (*pBuf != ' ')
            ?{
            ??szName[nIdx++] = *pBuf++;
            ?}
            ?szName[nIdx] = '\0';

            ?*pBuf++;

            ?nIdx = 0;
            ?while (*pBuf != '\0')
            ?{
            ??szPass[nIdx++] = *pBuf++;
            ?}
            ?szPass[nIdx] = '\0';


            ?char szSend[30];
            ?memset(szSend, 0, 30);
            ?bool bUserExist = false;

            ?if( g_RegUSers.find(string(szName)) != g_RegUSers.end() )
            ?{
            ??if(strcmp(g_RegUSers[szName].c_str(), szPass) == 0)
            ??{
            ???strcpy(szSend, "UP OK!");
            ???g_LoginUsers[szName] = sock;
            ??}
            ??else
            ??{
            ???strcpy(szSend, "P Err!");
            ??}??
            ?}
            ?else
            ?{
            ?// 不存在這個(gè)用戶
            ??strcpy(szSend, "U Err!");
            ?}
            ?
            ?int nRet = SendFix(sock, szSend, strlen(szSend));

            ?if(nRet == SOCKET_ERROR)
            ??return false;

            ?// 執(zhí)行完了就釋放data
            ?delete []data;

            ?return true;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : DoGetFile
            // 功能描述???? : 為用戶提供文件
            // 參數(shù)???????? : SOCKET sock
            // 參數(shù)???????? : char *data
            // 參數(shù)???????? : DWORD len
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool DoGetFile(SOCKET sock, char *data, DWORD len)
            {
            ?// 打開文件,判斷文件是否存在
            ?HANDLE hFile = CreateFile(data, GENERIC_READ, FILE_SHARE_READ,
            ??NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            ?
            ?if(hFile == INVALID_HANDLE_VALUE)
            ?{
            ??OutMsg("文件不存在!");
            ??DWORD dwSize = 0;
            ??SendFix(sock, (char *)&dwSize, sizeof(dwSize));
            ??return false;
            ?}
            ?else
            ?{// 發(fā)送文件信息

            ??// 發(fā)送文件大小,發(fā)送過去
            ??DWORD dwFileSize = GetFileSize(hFile, NULL);
            ??int nRet = SendFix(sock, (char *)&dwFileSize, sizeof(dwFileSize));
            ??if(nRet == SOCKET_ERROR)
            ???return false;
            ??
            ??// 讀文件記錄并發(fā)送
            ??DWORD nLeft = dwFileSize;
            ??char szBuf[1024];
            ??DWORD nCurrRead = 0;
            ??while(nLeft > 0)
            ??{
            ???if(!ReadFile(hFile, szBuf, 1024, &nCurrRead, NULL))
            ???{
            ????OutErr("ReadFile failed!");
            ????return false;
            ???}
            ???SendFix(sock, szBuf, nCurrRead);
            ???nLeft -= nCurrRead;
            ??}
            ??
            ??CloseHandle(hFile);
            ?}
            ?
            ?delete []data;
            ?return true;
            }

            bool DoRegister(SOCKET sock, char *data, DWORD len)
            {
            ?// 取得用戶名和密碼的字符串
            ?// 格式為 "dyl 123"

            ?bool bReturn = true;
            ?char *pBuf = data;
            ?int nIdx = 0;
            ?char szName[10];
            ?memset(szName, 0, 10);
            ?char szPass[20];
            ?memset(szPass, 0, 20);
            ?
            ?while (*pBuf != ' ')
            ?{
            ??szName[nIdx++] = *pBuf++;
            ?}
            ?szName[nIdx] = '\0';

            ?*pBuf++;

            ?nIdx = 0;
            ?while (*pBuf != '\0')
            ?{
            ??szPass[nIdx++] = *pBuf++;
            ?}
            ?szPass[nIdx] = '\0';

            ?char szSend[30];
            ?memset(szSend, 0, 30);?

            ?HANDLE hFile = CreateFile("Users.lst", GENERIC_WRITE, FILE_SHARE_READ, NULL,
            ??OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            ?if(hFile == INVALID_HANDLE_VALUE)
            ?{
            ??hFile = CreateFile("Users.lst", GENERIC_WRITE, FILE_SHARE_READ, NULL,
            ???CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
            ??if(hFile == INVALID_HANDLE_VALUE)
            ??{
            ???OutMsg("創(chuàng)建文件Users.lst失敗!");
            ???strcpy(szSend, "REG ERR!");
            ???bReturn = false;
            ??}
            ??else
            ??{
            ???// 在開始加
            ???SetFilePointer(hFile, 0, 0, FILE_BEGIN);
            ???DWORD dwWritten = 0;
            ???if(!WriteFile(hFile, szName, 10, &dwWritten, NULL))
            ???{
            ????OutMsg("WriteFile failed!");
            ????strcpy(szSend, "REG ERR!");
            ????bReturn = false;
            ???}
            ???if(!WriteFile(hFile, szPass, 20, &dwWritten, NULL))
            ???{
            ????OutMsg("WriteFile failed!");
            ????strcpy(szSend, "REG ERR!");
            ????bReturn = false;
            ???}
            ???
            ???CloseHandle(hFile);

            ???// 讀回到已注冊(cè)用戶列表中
            ???GetRegUsers();

            ???strcpy(szSend, "REG OK!");
            ??}
            ?}
            ?else
            ?{
            ??// 移動(dòng)到最后追加
            ??SetEndOfFile(hFile);
            ??DWORD dwWritten = 0;
            ??if(!WriteFile(hFile, szName, 10, &dwWritten, NULL))
            ??{
            ???OutMsg("WriteFile failed!");
            ???strcpy(szSend, "REG ERR!");
            ???bReturn = false;
            ??}
            ??if(!WriteFile(hFile, szPass, 20, &dwWritten, NULL))
            ??{
            ???OutMsg("WriteFile failed!");
            ???strcpy(szSend, "REG ERR!");
            ???bReturn = false;
            ??}

            ??CloseHandle(hFile);

            ??// 讀回到已注冊(cè)用戶列表中
            ??GetRegUsers();

            ??strcpy(szSend, "REG OK!");??
            ?}
            ?int nRet = SendFix(sock, szSend, strlen(szSend));
            ?if(nRet == SOCKET_ERROR)
            ??bReturn = false;

            ?// 執(zhí)行完了就釋放data
            ?delete []data;?

            ?return bReturn;
            }

            void GetRegUsers()
            {
            ?g_RegUSers.clear();

            ?char szName[10];
            ?char szPwd[20];
            ?
            ?HANDLE hFile = CreateFile("Users.lst", GENERIC_READ, FILE_SHARE_READ, NULL,
            ??OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            ?if(hFile == INVALID_HANDLE_VALUE)
            ?{
            ??OutMsg("用戶列表不存在!");
            ?}
            ?else
            ?{
            ??DWORD dwFileSize = 0;
            ??dwFileSize = GetFileSize(hFile, NULL);
            ??
            ??SetFilePointer(hFile, 0, 0, FILE_BEGIN);

            ??DWORD dwRead = 0;
            ??
            ??
            ??DWORD dwLeft = dwFileSize;
            ??while(dwLeft > 0)
            ??{
            ???memset(szName, 0, 10);
            ???memset(szPwd, 0, 20);
            ???if(!ReadFile(hFile, szName, 10, &dwRead, NULL))
            ???{
            ????DWORD dwErr = GetLastError();
            ????OutMsg("ReadFile failed!");
            ???}
            ???dwLeft -= dwRead;
            ???if(!ReadFile(hFile, szPwd, 20, &dwRead, NULL))
            ???{
            ????DWORD dwErr = GetLastError();
            ????OutMsg("ReadFile failed!");
            ???}
            ???dwLeft -= dwRead;
            ???g_RegUSers[szName] = szPwd;
            ??}?
            ?}

            ?CloseHandle(hFile);
            }

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

            第二個(gè)文件

            /*/
            文件:SocketClient.cpp

            說明:
            ?此文件是作為測(cè)試的客戶端,實(shí)現(xiàn)了登錄和取文件的功能。
            ?和服務(wù)端的交互就是采用了發(fā)送命令、數(shù)據(jù)長(zhǎng)度,然后發(fā)送具體的數(shù)據(jù)這樣的順序。
            ?詳細(xì)可看服務(wù)端的說明。

            ?基本邏輯是這樣的,客戶端要先登錄服務(wù)端,然后登錄成功之后,才能進(jìn)行相應(yīng)的操作。

            ?錯(cuò)誤處理做得不太好,以后再補(bǔ)充了。

            ?其他的如注釋,結(jié)構(gòu),命名等的編碼規(guī)范都用了個(gè)人比較喜歡的方式。

            輸出:
            ?..\Bin\SocketClient.exe

            用法:
            ?可以 SocketClient Server_IP
            ?或者直接啟動(dòng)SocketClient,會(huì)提示你輸入服務(wù)端的IP

            Todo:
            ?下一步首先完成各個(gè)SOCKET的模型,然后公開自己的研究代碼。
            ?功能方面就是:
            ?1、服務(wù)器可以指定共享文件夾
            ?2、客戶端可以列出服務(wù)器共享了哪些文件
            ?3、客戶端可以列出哪些用戶在線,并可以發(fā)命令和其他用戶聊天
            /*/

            #include <winsock2.h>
            #pragma comment(lib, "WS2_32")

            #include <iostream>
            using namespace std;

            #include <stdlib.h>

            #include "..\Common Include\CommonSocket.h"
            #include "..\Common Include\CommonCmd.h"

            bool g_bAuth = false;

            void GetFile(SOCKET sock);
            bool Auth(SOCKET sock, char *szName, char *szPwd);
            bool RegisterUser(SOCKET sock, char *szName, char *szPwd);


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : Usage
            // 功能描述???? : 提示程序用法
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void Usage()
            {
            ?printf("*******************************************\n");
            ?printf("Socket Client??????????????????????????? \n");
            ?printf("Written by DYL???????????????????????? \n");
            ?printf("Email: dylgsy@163.com???????????????? \n");
            ?printf("Usage: SocketClient.exe Server_IP????????? \n");
            ?printf("*******************************************\n");
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : Menu
            // 功能描述???? : 選擇服務(wù)的界面
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void Menu()
            {
            ?system("cls");
            ?printf("********************************************\n");
            ?printf("請(qǐng)選擇操作:????????\n\n");
            ?printf("1、取得文件?????????\n");
            ?printf("2、退出??????????\n");
            ?printf("********************************************\n");
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : LoginMenu
            // 功能描述???? : 用戶登錄的界面
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void LoginMenu()
            {
            ?cout << "請(qǐng)按任意鍵繼續(xù)操作." <<endl;
            ?getchar();
            ?system("cls");
            ?printf("********************************************\n");
            ?printf("請(qǐng)選擇操作:????????\n\n");
            ?printf("1、登錄??????????\n");
            ?printf("2、注冊(cè)??????????\n");
            ?printf("3、退出??????????\n");
            ?printf("********************************************\n");
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : Login
            // 功能描述???? : 用戶登錄的界面邏輯
            // 參數(shù)???????? : SOCKET sock
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool Login(SOCKET sock)
            {
            ?bool bGoOn = true;
            ?while(bGoOn)
            ?{
            ??LoginMenu();
            ??int nChoose = 0;
            ??cin >> nChoose;

            ??char szName[10];
            ??char szPwd[20];
            ??char szConfirmPwd[20];
            ??memset(szName, 0, 10);
            ??memset(szPwd, 0, 20);
            ??memset(szConfirmPwd, 0, 20);

            ??bool bGoOnLogin = true;

            ??switch(nChoose)
            ??{
            ??case 1:
            ???while(bGoOnLogin)
            ???{
            ????cout << "請(qǐng)輸入你的用戶名:";
            ????cin >> szName;
            ????cout << "請(qǐng)輸入你的密碼:";
            ????cin >> szPwd;
            ????if(Auth(sock, szName, szPwd))
            ????{
            ?????return true;?
            ????}
            ????else
            ????{
            ?????char c;
            ?????cout << "繼續(xù)登錄?y/n" << endl;
            ?????cin >> c;
            ?????switch(c)
            ?????{
            ?????case 'y':
            ??????bGoOnLogin = true;
            ??????break;
            ?????case 'n':
            ??????bGoOnLogin = false;
            ??????break;
            ?????default:
            ??????break;
            ?????}
            ????}
            ???}
            ???break;
            ???
            ??case 2:
            ???cout << "請(qǐng)輸入你的用戶名:";
            ???cin >> szName;
            ???cout << "請(qǐng)輸入你的密碼:";
            ???cin >> szPwd;
            ???cout << "請(qǐng)?jiān)俅屋斎肽愕拿艽a:";
            ???cin >> szConfirmPwd;
            ???if(strcmp(szPwd, szConfirmPwd) != 0)
            ???{
            ????cout << "前后密碼不一致" << endl;
            ???}
            ???else
            ???{
            ????if(!RegisterUser(sock, szName, szPwd))
            ????{
            ?????cout << "注冊(cè)用戶失敗!" << endl;
            ????}
            ???}
            ???break;

            ??case 3:
            ???bGoOn = false;
            ???return false;
            ??default:
            ???break;
            ??}
            ?}

            ?return false;
            }

            void main(int argc, char *argv[])
            {
            ?system("cls");
            ?char szServerIP[20];
            ?memset(szServerIP, 0, 20);

            ?if(argc != 2)
            ?{
            ??cout << "請(qǐng)輸入服務(wù)器IP:";
            ??cin >> szServerIP;
            ?}
            ?else
            ?{
            ??strcpy(szServerIP, argv[1]);
            ?}
            ?InitWinsock();
            ?SOCKET sockServer;
            ?sockServer = ConnectServer(szServerIP, PORT, 1);
            ?if(sockServer == NULL)
            ?{
            ??OutErr("連接服務(wù)器失敗!");
            ??return;
            ?}
            ?else
            ?{
            ??OutMsg("已和服務(wù)器建立連接!");
            ?}

            ?// 要求用戶登錄
            ?if(!Login(sockServer))
            ??return;

            ?// 登錄成功,讓用戶選擇服務(wù)
            ?int nChoose = 0;
            ?bool bExit = false;
            ?while(!bExit)
            ?{
            ??Menu();
            ??cin >> nChoose;
            ??switch(nChoose)
            ??{
            ??case 1:??// 獲取文件
            ???GetFile(sockServer);
            ???break;
            ??case 2:
            ???bExit = true;
            ???break;???
            ??default:
            ???break;
            ??}
            ?}
            ?shutdown(sockServer, SD_BOTH);
            ?closesocket(sockServer);
            ?
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : Auth
            // 功能描述???? : 用戶登錄認(rèn)證
            // 參數(shù)???????? : SOCKET sock
            // 參數(shù)???????? : char *szName
            // 參數(shù)???????? : char *szPwd
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool Auth(SOCKET sock, char *szName, char *szPwd)
            {
            ?char szCmd[50];
            ?memset(szCmd, 0, 50);
            ?strcpy(szCmd, szName);
            ?strcat(szCmd, " ");
            ?strcat(szCmd, szPwd);
            ?
            ?SCommand cmd;
            ?cmd.CommandID = CMD_AUTHEN;
            ?cmd.DataSize = strlen(szCmd);

            ?int nRet;
            ?nRet = SendFix(sock, (char *)&cmd, sizeof(cmd));
            ?if(nRet == SOCKET_ERROR)
            ?{
            ??OutErr("SendFix() failed!");
            ??return false;
            ?}
            ?else
            ?{
            ??SendFix(sock, szCmd, strlen(szCmd));
            ??char szBuf[10];
            ??memset(szBuf, 0, 10);
            ??recv(sock, szBuf, 10, 0);
            ??if(strcmp(szBuf, "UP OK!") == 0)
            ??{
            ???cout << "登錄成功。" << endl;
            ???g_bAuth = true;
            ??}
            ??else if(strcmp(szBuf, "U Err!") == 0)
            ??{
            ???cout << "此用戶不存在。" << endl;
            ???g_bAuth = false;
            ??}
            ??else if(strcmp(szBuf, "P Err!") == 0)
            ??{
            ???cout << "密碼錯(cuò)誤。" << endl;
            ???g_bAuth = false;
            ??}
            ?}
            ?return g_bAuth;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : GetFile
            // 功能描述???? : 取得服務(wù)器的文件
            // 參數(shù)???????? : SOCKET sock
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void GetFile(SOCKET sock)
            {
            ?if(!g_bAuth)
            ?{
            ??OutMsg("用戶還沒登錄!請(qǐng)先登錄");
            ??return;
            ?}
            ?
            ?char szSrcFile[MAX_PATH];
            ?char szDstFile[MAX_PATH];
            ?memset(szSrcFile, 0, MAX_PATH);
            ?memset(szDstFile, 0, MAX_PATH);

            ?cout << "你要取得Server上的文件:";
            ?cin >> szSrcFile;

            ?cout << "你要把文件存在哪里:";
            ?cin >> szDstFile;

            ?SCommand cmd;
            ?cmd.CommandID = CMD_GETFILE;
            ?cmd.DataSize = strlen(szSrcFile);

            ?// 發(fā)送命令
            ?SendFix(sock, (char *)&cmd, sizeof(cmd));
            ?
            ?// 發(fā)送文件名
            ?SendFix(sock, szSrcFile, strlen(szSrcFile));

            ?// 接收文件長(zhǎng)度
            ?DWORD dwFileSize = 0;
            ?RecvFix(sock, (char*)&dwFileSize, sizeof(dwFileSize));
            ?
            ?if(dwFileSize == 0)
            ?{
            ??OutMsg("文件不存在");
            ??return;
            ?}

            ?// 接收文件內(nèi)容
            ?DWORD dwLeft = dwFileSize;
            ?char szBuf[1024];
            ?HANDLE hFile = CreateFile(szDstFile, GENERIC_WRITE, FILE_SHARE_READ,
            ??NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            ?if(hFile == INVALID_HANDLE_VALUE)
            ?{
            ??hFile = CreateFile(szDstFile, GENERIC_WRITE, FILE_SHARE_READ,
            ???NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
            ??if(hFile == INVALID_HANDLE_VALUE)
            ??{
            ???OutErr("CreateFile failed!");
            ???return;
            ??}
            ?}
            ?while(dwLeft > 0)
            ?{
            ??memset(szBuf, 0, 1024);
            ??// 這里是不確定文件內(nèi)容的,所以要用recv,不能用RecvFix
            ??int nRead = recv(sock, szBuf, 1024, 0);
            ??if(nRead == SOCKET_ERROR)
            ???OutErr("RecvFix Error!");

            ??DWORD dwWritten = 0;
            ??if(!WriteFile(hFile, szBuf, nRead, &dwWritten, NULL))
            ??{
            ???OutErr("WriteFile error!");
            ???return;
            ??}
            ??dwLeft -= dwWritten;
            ?}

            ?CloseHandle(hFile);

            ?OutMsg("接收文件成功!");
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : RegisterUser
            // 功能描述???? : 注冊(cè)新用戶
            // 參數(shù)???????? : SOCKET sock
            // 參數(shù)???????? : char *szName
            // 參數(shù)???????? : char *szPwd
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool RegisterUser(SOCKET sock, char *szName, char *szPwd)
            {
            ?char szCmd[50];
            ?memset(szCmd, 0, 50);
            ?strcpy(szCmd, szName);
            ?strcat(szCmd, " ");
            ?strcat(szCmd, szPwd);
            ?
            ?SCommand cmd;
            ?cmd.CommandID = CMD_REGISTER;
            ?cmd.DataSize = strlen(szCmd);

            ?// 發(fā)送命令
            ?int nRet = SendFix(sock, (char *)&cmd, sizeof(cmd));
            ?if(nRet == SOCKET_ERROR)
            ?{
            ??OutErr("SendFix() failed!");
            ??return false;
            ?}
            ?else
            ?{
            ??// 發(fā)送用戶名和密碼串
            ??SendFix(sock, szCmd, strlen(szCmd));
            ??char szBuf[10];
            ??memset(szBuf, 0, 10);
            ??
            ??recv(sock, szBuf, 10, 0);
            ??if(strcmp(szBuf, "REG OK!") == 0)
            ??{
            ???cout << "注冊(cè)成功。" << endl;
            ???return true;
            ??}
            ??else if(strcmp(szBuf, "REG ERR!") == 0)
            ??{
            ???cout << "注冊(cè)失敗." << endl;
            ???return false;
            ??}
            ?}
            ?
            ?return false;
            }

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

            第三個(gè)文件,公用的

            /*/
            文件: CommonSocket.h
            說明:
            ?實(shí)現(xiàn)了服務(wù)端和客戶端一些公用的函數(shù)!
            /*/

            #ifndef __COMMONSOCKET_H__
            #define __COMMONSOCKET_H__

            #include <iostream>
            using namespace std;

            #define OutErr(a) cout << (a) << endl \
            ??????<< "出錯(cuò)代碼:" << WSAGetLastError() << endl \
            ??????<< "出錯(cuò)文件:" << __FILE__ << endl??\
            ??????<< "出錯(cuò)行數(shù):" << __LINE__ << endl \

            #define OutMsg(a) cout << (a) << endl;

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : InitWinsock
            // 功能描述???? : 初始化WINSOCK
            // 返回值?????? : void
            //
            ///////////////////////////////////////////////////////////////////////
            void InitWinsock()
            {
            ?// 初始化WINSOCK
            ?WSADATA wsd;
            ?if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
            ?{
            ??OutErr("WSAStartup()");
            ?}
            }

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : ConnectServer
            // 功能描述???? : 連接SERVER
            // 參數(shù)???????? : char *lpszServerIP?IP地址
            // 參數(shù)???????? : int nPort????端口
            // 返回值?????? : SOCKET????SERVER 的 Socket
            //
            ///////////////////////////////////////////////////////////////////////
            SOCKET ConnectServer(char *lpszServerIP, int nPort, ULONG NonBlock)
            {
            ?SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            ?//ioctlsocket(sServer, FIONBIO, &NonBlock);
            ?
            ?struct hostent *pHost = NULL;
            ?struct sockaddr_in servAddr;
            ?servAddr.sin_family = AF_INET;
            ?servAddr.sin_port = htons(nPort);
            ?servAddr.sin_addr.s_addr = inet_addr(lpszServerIP);


            ?// 如果給的是主機(jī)的名字而不是IP地址
            ?if(servAddr.sin_addr.s_addr == INADDR_NONE)
            ?{
            ??pHost = gethostbyname( lpszServerIP );
            ??if(pHost == NULL)
            ??{
            ???OutErr("gethostbyname Failed!");
            ???return NULL;
            ??}
            ??memcpy(&servAddr.sin_addr, pHost->h_addr_list[0], pHost->h_length);
            ?}

            ?int nRet = 0;
            ?nRet = connect(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr));
            ?if( nRet == SOCKET_ERROR )
            ?{
            ??OutErr("connect failed!");
            ??return NULL;
            ?}
            ??
            ?return sServer;
            }

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : BindServer
            // 功能描述???? : 綁定端口
            // 參數(shù)???????? : int nPort
            // 返回值?????? : SOCKET
            //
            ///////////////////////////////////////////////////////////////////////
            SOCKET BindServer(int nPort)
            {
            ?// 創(chuàng)建socket
            ?SOCKET sServer = socket(AF_INET, SOCK_STREAM, 0);

            ?// 綁定端口
            ?struct sockaddr_in servAddr;
            ?servAddr.sin_family = AF_INET;
            ?servAddr.sin_port = htons(nPort);
            ?servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

            ?if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
            ?{
            ??OutErr("bind Failed!");
            ??return NULL;
            ?}

            ?// 設(shè)置監(jiān)聽隊(duì)列為200
            ?if(listen(sServer, 200) != 0)
            ?{
            ??OutErr("listen Failed!");
            ??return NULL;
            ?}
            ?return sServer;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : AcceptClient
            // 功能描述???? :
            // 參數(shù)???????? : SOCKET sServer [in]
            // 參數(shù)???????? : LPSTR lpszIP? [out] 返回客戶端的IP地址?
            // 返回值?????? : SOCKET?? [out] 返回客戶端的socket
            //
            ///////////////////////////////////////////////////////////////////////
            SOCKET AcceptClient(SOCKET sListen, LPSTR lpszIP)
            {
            ?struct sockaddr_in cliAddrTmp;
            ?int cliAddrSize = sizeof(struct sockaddr_in);
            ?SOCKET sClient = accept(sListen, (struct sockaddr *)&cliAddrTmp, &cliAddrSize);
            ?if(sClient == INVALID_SOCKET)
            ?{
            ??OutErr("accept failed!");
            ??return NULL;
            ?}
            ?sprintf(lpszIP, "%s", inet_ntoa(cliAddrTmp.sin_addr));

            ?return sClient;
            }

            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : RecvFix
            // 功能描述???? : 接收指定長(zhǎng)度的數(shù)據(jù),考慮非阻塞socket的情況
            // 參數(shù)???????? : SOCKET socket?[in]
            // 參數(shù)???????? : char *data?[in]
            // 參數(shù)???????? : DWORD len??[in]
            // 參數(shù)???????? : DWORD *retlen [out]
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            int RecvFix(SOCKET socket, char *data, DWORD len)
            {
            ?int retlen = 0;
            ?int nLeft = len;
            ?int nRead = 0;
            ?char *pBuf = data;
            ?while(nLeft > 0)
            ?{
            ??nRead = recv(socket, pBuf, nLeft, 0);
            ??if(nRead == SOCKET_ERROR || nRead == 0)
            ??{
            ???if(WSAEWOULDBLOCK == WSAGetLastError())
            ????continue;
            ???else
            ????return nRead;
            ??}
            ??
            ??nLeft -= nRead;
            ??retlen += nRead;
            ??pBuf += nRead;
            ?}
            ?return nRead;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : SendFix
            // 功能描述???? : 發(fā)送指定長(zhǎng)度的數(shù)據(jù),考慮非阻塞socket的情況
            // 參數(shù)???????? : SOCKET socket
            // 參數(shù)???????? : char *data
            // 參數(shù)???????? : DWORD len
            // 參數(shù)???????? : DWORD *retlen
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            int SendFix(SOCKET socket, char *data, DWORD len)
            {
            ?int retlen = 0;
            ?int nLeft = len;
            ?int nWritten = 0;
            ?const char *pBuf = data;
            ?while(nLeft > 0)
            ?{
            ??nWritten = send(socket, data, nLeft, 0);
            ??if(nWritten == SOCKET_ERROR || nWritten == 0)
            ??{
            ???if(WSAEWOULDBLOCK == WSAGetLastError())
            ????continue;
            ???else
            ????return nWritten;
            ??}

            ??
            ??nLeft -= nWritten;
            ??retlen += nWritten;
            ??pBuf += nWritten;
            ?}
            ?return nWritten;
            }


            /*
            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : SelectSend
            // 功能描述???? : 使用select模型來發(fā)送數(shù)據(jù),沒完成,所以注釋掉了
            // 參數(shù)???????? : SOCKET sock
            // 參數(shù)???????? : FD_SET *wfds
            // 參數(shù)???????? : char *data
            // 參數(shù)???????? : DWORD len
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool SelectSend(SOCKET sock, FD_SET *wfds, char *data, DWORD len)
            {
            ?FD_ZERO(wfds);
            ?FD_SET(sock, wfds);
            ?
            ?if(select(0, NULL, wfds, NULL, NULL) == SOCKET_ERROR)
            ?{
            ??OutErr("select() Failed!");
            ??return false;
            ?}
            ?// 如果是可以寫的SOCKET,就一直寫,直到返回WSAEWOULDBLOCK
            ?if( FD_ISSET(sock, wfds) )
            ?{
            ??int nLeft = len;
            ??while(nLeft > 0)
            ??{
            ???int nRet = send(sock, data, len, 0);
            ???if(nRet == SOCKET_ERROR)
            ????return false;
            ???nLeft -= nRet;
            ??}
            ?}

            ?return true;
            }


            ///////////////////////////////////////////////////////////////////////
            //
            // 函數(shù)名?????? : SelectRecv
            // 功能描述???? : 使用select模型來接收數(shù)據(jù),沒完成,所以注釋掉了
            // 參數(shù)???????? : SOCKET sock
            // 參數(shù)???????? : FD_SET *rfds
            // 參數(shù)???????? : char *data
            // 參數(shù)???????? : DWORD len
            // 返回值?????? : bool
            //
            ///////////////////////////////////////////////////////////////////////
            bool SelectRecv(SOCKET sock, FD_SET *rfds, char *data, DWORD len)
            {
            ?FD_ZERO(rfds);
            ?FD_SET(sock, rfds);
            ?
            ?if(select(0, rfds, NULL, NULL, NULL) == SOCKET_ERROR)
            ?{
            ??OutErr("select() Failed!");
            ??return false;
            ?}
            ?
            ?if( FD_ISSET(sock, rfds) )
            ?{
            ??int nLeft = len;
            ??while(nLeft > 0)
            ??{
            ???int nRet = recv(sock, data, len, 0);
            ???if(nRet == SOCKET_ERROR)
            ????return false;
            ???nLeft -= nRet;
            ??}
            ?}
            ?return true;
            }
            */


            #endif //__COMMONSOCKET_H__

            ?

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

            第四個(gè)文件,公用的

            /*/
            文件: CommonCmd.h
            說明:
            ?實(shí)現(xiàn)了服務(wù)端和客戶端一些公用的數(shù)據(jù)結(jié)構(gòu),所以服務(wù)端和客戶端都要包含。
            ?其中有命令、SOCKET的當(dāng)前狀態(tài)等的定義。
            /*/

            #ifndef __COMMONCMD_H__
            #define __COMMONCMD_H__

            #define PORT 5050

            // 命令定義
            #define CMD_AUTHEN 1?// 登錄認(rèn)證
            #define CMD_GETFILE 2?// 獲取文件
            #define CMD_REGISTER 3? // 注冊(cè)用戶

            typedef struct tagCommand
            {
            ?int CommandID;??// 命令I(lǐng)D
            ?DWORD DataSize;??// 后接數(shù)據(jù)的大小
            }SCommand;

            // 標(biāo)志目前的SOCKET該做什么
            enum ECurOp
            {RecvCmd, RecvData, ExecCmd};


            #endif //__COMMONCMD_H__

            ?好了,上面四個(gè)文件都搞好了,有興趣的朋友自己去弄吧,希望對(duì)你們有用,好累啊(寫文章是一件很累的事情)。有什么問題歡迎探討!

            posted on 2007-01-19 16:44 CPP&&設(shè)計(jì)模式小屋 閱讀(2077) 評(píng)論(1)  編輯 收藏 引用 所屬分類: Network

            FeedBack:
            # re: WinSocket模型的探討——select模型(轉(zhuǎn) 收藏)[未登錄] 2007-02-20 11:54 walkspeed
            有些意識(shí)。自己的想法和總結(jié),還是不錯(cuò)的。
            要是能進(jìn)一步設(shè)計(jì)下,寫成相互協(xié)作的類組就更有意識(shí)了。

            ace中accept-connect框架完成了這類應(yīng)用的,不過就是框架很大。研究起來還是很煩的。你這個(gè)思想明確,架構(gòu)簡(jiǎn)單,可以發(fā)展一下。  回復(fù)  更多評(píng)論
              
            狠狠色丁香婷综合久久| 伊人色综合久久天天网| 精品国产一区二区三区久久久狼| 久久国产高潮流白浆免费观看| 久久香蕉国产线看观看99| 久久涩综合| 久久精品亚洲中文字幕无码麻豆| 久久美女人爽女人爽| 欧美色综合久久久久久| 久久精品国产清高在天天线| 久久露脸国产精品| 国产精品久久久天天影视| 久久夜色撩人精品国产小说| 99久久久精品免费观看国产| 久久人人添人人爽添人人片牛牛| 久久久精品免费国产四虎| 久久综合亚洲欧美成人| 中文字幕久久精品| 国产精品美女久久久久AV福利| 久久久久久精品成人免费图片| 国内精品久久久久久久coent| 国产精品免费看久久久 | 久久精品国产亚洲77777| 伊人久久精品影院| 色综合久久天天综线观看| 超级碰久久免费公开视频| 久久久久99精品成人片直播| 欧美牲交A欧牲交aⅴ久久 | 91精品国产91久久久久久| 国产成人精品综合久久久久| 久久亚洲精品无码aⅴ大香| 久久久久久国产精品无码下载| 99久久国产免费福利| 国内精品久久久久影院免费 | 亚洲午夜精品久久久久久人妖| 久久久久亚洲av无码专区导航 | 久久91精品综合国产首页| 99久久精品费精品国产| 99久久精品无码一区二区毛片 | 亚洲国产精品无码久久久不卡| 伊人久久五月天|