SOCKET模型的出現(xiàn)是為了解決“一個客戶端一線程”的問題,為了WINDOWS的線程切換不要太頻繁,我們可以使用WINDOWS的SOCKET模型。但在論壇里又看到了一些文章說現(xiàn)在的計算機硬件相當發(fā)達,成萬的線程切換根本不是什么問題。無論怎么樣,既然這些模型被MS發(fā)明了出來,就有它的道理,我們還是好好來學習一下吧。
select可以這樣用,寫成兩個函數(shù),SelectSend和SelectRecv,這兩個函數(shù)和一般的send\recv不同的地方在于它是有超時值的,這樣就不會把程序完全阻塞了。這兩個函數(shù)大概如下(參考《PC網(wǎng)絡游戲編程》):
這樣就可以利用上面兩個函數(shù)寫發(fā)送和接收程序了!我們下面來看看如何使用select模型建立我們的Server,下面分了4個文件,大家可以拷貝下來實際編譯一下。首先有兩個公用的文件:CommonSocket.h、CommonCmd.h。他們都是放在 Common Include 目錄中的!然后就是SelectServer.cpp和SocketClient.cpp這兩個文件,可以分別建立兩個工程把他們加進去編譯!有些關鍵的地方我都寫在文件注釋里了,可以自己看看,這些是我寫的。為了方便大家拷貝,我就不用“代碼插入”的方式了。直接貼到下面!
///////////////////////////////////////////////////////////////////////
//
// 函數(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 關閉 socket 會返回0
?// 如果用戶直接關閉程序會返回 SOCKET_ERROR,強行關閉
?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í)行命令
// 參數(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í)行完命令后就設置回接收指令狀態(tài)
?sockInfo->eCurOp = RecvCmd;
}
///////////////////////////////////////////////////////////////////////
//
// 函數(shù)名?????? : DoAuthen
// 功能描述???? : 對用戶名和密碼做驗證
// 參數(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
?{
?// 不存在這個用戶
??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);
???// 讀回到已注冊用戶列表中
???GetRegUsers();
???strcpy(szSend, "REG OK!");
??}
?}
?else
?{
??// 移動到最后追加
??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);
??// 讀回到已注冊用戶列表中
??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);
}
/**********************************************************************************************************************/
第二個文件
/*/
文件:SocketClient.cpp
說明:
?此文件是作為測試的客戶端,實現(xiàn)了登錄和取文件的功能。
?和服務端的交互就是采用了發(fā)送命令、數(shù)據(jù)長度,然后發(fā)送具體的數(shù)據(jù)這樣的順序。
?詳細可看服務端的說明。
?基本邏輯是這樣的,客戶端要先登錄服務端,然后登錄成功之后,才能進行相應的操作。
?錯誤處理做得不太好,以后再補充了。
?其他的如注釋,結構,命名等的編碼規(guī)范都用了個人比較喜歡的方式。
輸出:
?..\Bin\SocketClient.exe
用法:
?可以 SocketClient Server_IP
?或者直接啟動SocketClient,會提示你輸入服務端的IP
Todo:
?下一步首先完成各個SOCKET的模型,然后公開自己的研究代碼。
?功能方面就是:
?1、服務器可以指定共享文件夾
?2、客戶端可以列出服務器共享了哪些文件
?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
// 功能描述???? : 選擇服務的界面
// 返回值?????? : void
//
///////////////////////////////////////////////////////////////////////
void Menu()
{
?system("cls");
?printf("********************************************\n");
?printf("請選擇操作:????????\n\n");
?printf("1、取得文件?????????\n");
?printf("2、退出??????????\n");
?printf("********************************************\n");
}
///////////////////////////////////////////////////////////////////////
//
// 函數(shù)名?????? : LoginMenu
// 功能描述???? : 用戶登錄的界面
// 返回值?????? : void
//
///////////////////////////////////////////////////////////////////////
void LoginMenu()
{
?cout << "請按任意鍵繼續(xù)操作." <<endl;
?getchar();
?system("cls");
?printf("********************************************\n");
?printf("請選擇操作:????????\n\n");
?printf("1、登錄??????????\n");
?printf("2、注冊??????????\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 << "請輸入你的用戶名:";
????cin >> szName;
????cout << "請輸入你的密碼:";
????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 << "請輸入你的用戶名:";
???cin >> szName;
???cout << "請輸入你的密碼:";
???cin >> szPwd;
???cout << "請再次輸入你的密碼:";
???cin >> szConfirmPwd;
???if(strcmp(szPwd, szConfirmPwd) != 0)
???{
????cout << "前后密碼不一致" << endl;
???}
???else
???{
????if(!RegisterUser(sock, szName, szPwd))
????{
?????cout << "注冊用戶失敗!" << 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 << "請輸入服務器IP:";
??cin >> szServerIP;
?}
?else
?{
??strcpy(szServerIP, argv[1]);
?}
?InitWinsock();
?SOCKET sockServer;
?sockServer = ConnectServer(szServerIP, PORT, 1);
?if(sockServer == NULL)
?{
??OutErr("連接服務器失??!");
??return;
?}
?else
?{
??OutMsg("已和服務器建立連接!");
?}
?// 要求用戶登錄
?if(!Login(sockServer))
??return;
?// 登錄成功,讓用戶選擇服務
?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
// 功能描述???? : 用戶登錄認證
// 參數(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 << "密碼錯誤。" << endl;
???g_bAuth = false;
??}
?}
?return g_bAuth;
}
///////////////////////////////////////////////////////////////////////
//
// 函數(shù)名?????? : GetFile
// 功能描述???? : 取得服務器的文件
// 參數(shù)???????? : SOCKET sock
// 返回值?????? : void
//
///////////////////////////////////////////////////////////////////////
void GetFile(SOCKET sock)
{
?if(!g_bAuth)
?{
??OutMsg("用戶還沒登錄!請先登錄");
??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));
?// 接收文件長度
?DWORD dwFileSize = 0;
?RecvFix(sock, (char*)&dwFileSize, sizeof(dwFileSize));
?
?if(dwFileSize == 0)
?{
??OutMsg("文件不存在");
??return;
?}
?// 接收文件內容
?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);
??// 這里是不確定文件內容的,所以要用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
// 功能描述???? : 注冊新用戶
// 參數(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 << "注冊成功。" << endl;
???return true;
??}
??else if(strcmp(szBuf, "REG ERR!") == 0)
??{
???cout << "注冊失敗." << endl;
???return false;
??}
?}
?
?return false;
}
/**********************************************************************************************************************/
第三個文件,公用的
/*/
文件: CommonSocket.h
說明:
?實現(xiàn)了服務端和客戶端一些公用的函數(shù)!
/*/
#ifndef __COMMONSOCKET_H__
#define __COMMONSOCKET_H__
#include <iostream>
using namespace std;
#define OutErr(a) cout << (a) << endl \
??????<< "出錯代碼:" << WSAGetLastError() << endl \
??????<< "出錯文件:" << __FILE__ << endl??\
??????<< "出錯行數(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);
?// 如果給的是主機的名字而不是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;
?}
?// 設置監(jiān)聽隊列為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
// 功能描述???? : 接收指定長度的數(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ā)送指定長度的數(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__
?
/**********************************************************************************************************************/
第四個文件,公用的
/*/
文件: CommonCmd.h
說明:
?實現(xiàn)了服務端和客戶端一些公用的數(shù)據(jù)結構,所以服務端和客戶端都要包含。
?其中有命令、SOCKET的當前狀態(tài)等的定義。
/*/
#ifndef __COMMONCMD_H__
#define __COMMONCMD_H__
#define PORT 5050
// 命令定義
#define CMD_AUTHEN 1?// 登錄認證
#define CMD_GETFILE 2?// 獲取文件
#define CMD_REGISTER 3? // 注冊用戶
typedef struct tagCommand
{
?int CommandID;??// 命令ID
?DWORD DataSize;??// 后接數(shù)據(jù)的大小
}SCommand;
// 標志目前的SOCKET該做什么
enum ECurOp
{RecvCmd, RecvData, ExecCmd};
#endif //__COMMONCMD_H__
?好了,上面四個文件都搞好了,有興趣的朋友自己去弄吧,希望對你們有用,好累?。▽懳恼率且患芾鄣氖虑椋?。有什么問題歡迎探討!