日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

Windows socket之最简单的socket程序

發(fā)布時(shí)間:2023/12/9 windows 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windows socket之最简单的socket程序 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Windows socket之最簡(jiǎn)單的socket程序 原文:Windows socket之最簡(jiǎn)單的socket程序

最簡(jiǎn)單的服務(wù)器的socket程序流程如下(面向連接的TCP連接 ):

1. WSAStartup(); 初始化網(wǎng)絡(luò)庫(kù)的使用。

2. socket(); 獲得一個(gè)socket。

3. bind(); 把獲得的socket綁定到一個(gè)ip 和端口。既然作為服務(wù)器, ip通常為本地IP127.0.0.1。

4. listen(); 監(jiān)聽(tīng)已經(jīng)綁定了指定端口的socket。

5. accept(); 接受一個(gè)來(lái)自客戶(hù)端的連接。


accept()返回一個(gè)新的socket,該socket代表著本地服務(wù)器與某一個(gè)連接過(guò)來(lái)的客戶(hù)端的鏈接。以該socket為參數(shù),可以調(diào)用send函數(shù)往客戶(hù)端發(fā)送數(shù)據(jù),也可以調(diào)用recv函數(shù)接受客戶(hù)端發(fā)送過(guò)來(lái)的函數(shù)。


最后服務(wù)器程序結(jié)束的時(shí)候調(diào)用closesocket()關(guān)閉socket, WSACleanup()終止網(wǎng)絡(luò)庫(kù)的使用,清理資源。



最簡(jiǎn)單的客戶(hù)端的socket程序流程如下(同樣是面向連接的TCP連接):

1. WSAStartup();初始化網(wǎng)絡(luò)庫(kù)的使用。

2. socket(); 獲得一個(gè)socket。

3. connect(); 連接到一個(gè) 服務(wù)器。


連接成功后就可以收發(fā)數(shù)據(jù)了。收發(fā)完畢后調(diào)用closesocket()關(guān)閉socket,最后程序結(jié)束前調(diào)用 WSACleanup()清理資源。


下面直接上代碼

需包含以下頭文件和定義

#include <stdlib.h>
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

#define SERVE_ADDRESS "127.0.0.1"
#define SERVE_PORT?? ?7001



// ---------------------------- WSAStartup() ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");goto Main_End;}//------------------------------------------------------------------------------//// ---------------------------- socket() ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");goto Main_End;}// ------------------------------------------------------------------------------------////---------------------------- bind() ----------------------------//// 初始化 struct sockaddr 結(jié)構(gòu)體, SOCKADDR_IN就是 struct sockaddr的宏定義SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS); localAddr.sin_port = htons(SERVE_PORT); memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");goto Main_End;}//------------------------------------------------------------------------------------////---------------------------- listen() ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");goto Main_End;}printf("the server is listening now!\n");//------------------------------------------------------------------------------------////---------------------------- accept() ----------------------------//SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);if(INVALID_SOCKET == acceptedSocket){printf("accept error!\n");goto Main_End;}printf("a client has connected to the server!\n");//------------------------------------------------------------------------------------//char recvBuffer[256];char sendBuffer[256];strcpy(sendBuffer,"server:Welcome to connect !");int sendBufLen = strlen(sendBuffer);int resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);while(true){if(resSend != sendBufLen) //發(fā)送的長(zhǎng)度與需要發(fā)送的長(zhǎng)度不等{printf("send data error!!\n");break;}int recvLen = recv(acceptedSocket,recvBuffer,sizeof(recvBuffer),0);if(0 == recvLen){printf("a client close the socket!\n");break;}else if(recvLen < 0){printf("an error has happen when receiving\n");break;}recvBuffer[recvLen] = '\0';printf("client:%s\n",recvBuffer);//在客戶(hù)發(fā)過(guò)來(lái)的數(shù)據(jù)前面加上server:再發(fā)回給客戶(hù)端strcpy(sendBuffer,"server:");strcat(sendBuffer,recvBuffer);sendBufLen = strlen(sendBuffer);resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);}closesocket(acceptedSocket);closesocket(serverSocket);Main_End:WSACleanup();system("pause");return 0;



客戶(hù)端代碼:

//---------------------------- WSAStartup() ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");goto Main_End;}//------------------------------------------------------------------------------------////---------------------------- socket() ----------------------------//SOCKET connSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == connSocket){printf("the socket returned is invalid!\n");goto Main_End;}//------------------------------------------------------------------------------------////---------------------------- connect() ----------------------------////初始化struct sockaddr 結(jié)構(gòu)體 SOCKADDR_IN serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);serverAddr.sin_port = htons(SERVE_PORT);memset(serverAddr.sin_zero,0x0,sizeof(serverAddr.sin_zero));//connectint resConn = connect(connSocket,(sockaddr*)&serverAddr,sizeof(serverAddr));if(0 != resConn){printf("failed to connect to server!!\n");goto Main_End;}//------------------------------------------------------------------------------------//char sendBuffer[256];char recvBuffer[256];while(true){int recvLen = recv(connSocket,recvBuffer,256,0);if(recvLen < 0){printf("receive error!!\n");break;}else if(0 == recvLen){printf("the server close the socket!\n");}recvBuffer[recvLen] = '\0';printf("the data recv:%s\n\n\n",recvBuffer);printf("please input what you want to send:\n");gets(sendBuffer);if(0 == strcmp(sendBuffer,"exit")){break;}int sendDataLen = strlen(sendBuffer);int nDataSent = send(connSocket,sendBuffer,sendDataLen,0);if(nDataSent != sendDataLen){printf("failed to send data!!\n");break;}}closesocket(connSocket);printf("the connection is closed!\n");Main_End:WSACleanup();system("pause");return 0;



客戶(hù)端連接到服務(wù)端后,每次給服務(wù)端發(fā)送一段內(nèi)容,服務(wù)器在內(nèi)容前面加上server:再發(fā)送給客戶(hù)端。

當(dāng)客戶(hù)端發(fā)送的內(nèi)容是exit時(shí),客戶(hù)端程序跳出循環(huán),關(guān)閉socket斷開(kāi)連接。服務(wù)端發(fā)現(xiàn)客戶(hù)端斷開(kāi)連接后也關(guān)閉套接字結(jié)束程序。


當(dāng)然上面程序只為了演示最簡(jiǎn)單的網(wǎng)絡(luò)編程。有若干漏洞。

1. 服務(wù)器只能接受一個(gè)客戶(hù)端連接。當(dāng)然加一個(gè)循環(huán)語(yǔ)句進(jìn)去可以重復(fù)地接受客戶(hù)端的連接,但是仍然是每次只處理一個(gè)客戶(hù)端連接。

2.accept, connect,send,recv函數(shù)默認(rèn)均是阻塞函數(shù)。當(dāng)沒(méi)有客戶(hù)連接到服務(wù)端時(shí),服務(wù)端阻塞在accept函數(shù),無(wú)法退出程序。當(dāng)服務(wù)器在接受客戶(hù)端的數(shù)據(jù)時(shí),如果客戶(hù)端不發(fā)送數(shù)據(jù),也不斷開(kāi)連接,那么服務(wù)端阻塞在recv函數(shù),無(wú)法退出程序。




改進(jìn)該程序,使得服務(wù)端隨時(shí)都可以停止服務(wù)退出程序,無(wú)論有多少個(gè)用戶(hù)已經(jīng)在連接。

為了多個(gè)客戶(hù)端可以同時(shí)連接,最容易理解的便是利用多線(xiàn)程。每一個(gè)連接的客戶(hù)端都用一個(gè)線(xiàn)程去處理它的通信。

至于為了隨時(shí)可以退出服務(wù)端,不能再調(diào)用永久阻塞的函數(shù)了。利用select函數(shù),可以阻塞指定的時(shí)間,阻塞期間不占CPU。


int select( __in int nfds, __in_out fd_set*readfds, __in_out fd_set*writefds, __in_out fd_set*exceptfds, __in const struct timeval*timeout);

nfds

用于兼容Berkeley sockets.不用理會(huì),隨便給個(gè)0值就OK。

readfds

用于檢查是否存在可讀socket的的一個(gè)socket集合。可為空。


writefds

用于檢查是否存在可寫(xiě)socket的一個(gè)socket集合。可為空。

exceptfds

用于檢查是否存在有錯(cuò)誤的socket的一個(gè) socket集合,可為空。

timeout

TIMEVAL結(jié)構(gòu)體,用于指定該函數(shù)阻塞多長(zhǎng)時(shí)間。

在 調(diào)用select時(shí),當(dāng)readfds不為空時(shí),當(dāng)readfds中任何一個(gè)socket就緒可讀時(shí),或者當(dāng)writefds不為空且writefds中任何一個(gè)socket準(zhǔn)備就緒可寫(xiě),或者當(dāng)exceptfds不為空且任何一個(gè)socket發(fā)生socket錯(cuò)誤時(shí),select就立即返回。否則,直到timeout指定的時(shí)間過(guò)去以后才返回。


返回值,返回準(zhǔn)備就緒的socket的個(gè)數(shù)。如果為0,說(shuō)明該函數(shù)超時(shí)了,如果大于0,說(shuō)明至少有一個(gè)socket就緒。如果小于0,說(shuō)明發(fā)生錯(cuò)誤了。


fd_set 是一種集合類(lèi)型。

typedef struct fd_set {
??????? u_int fd_count;?????????????? /* how many are SET? */
??????? SOCKET? fd_array[FD_SETSIZE];?? /* an array of SOCKETs */
} fd_set;

記錄著一個(gè)socket數(shù)組,以及里面的socket個(gè)數(shù)。


struct timeval是一個(gè)表示等待時(shí)間的結(jié)構(gòu)體。

struct timeval {
??????? long??? tv_sec;???????? /* seconds */
??????? long??? tv_usec;??????? /* and microseconds */
};


tv_sec表示有多少秒,tv_usec表示有多少毫秒。


對(duì)于fd_set類(lèi)型,用到幾個(gè)宏定義函數(shù)。

FD_ZERO(fd_set*), 清空f(shuō)d_set集合

FD_SET(SOCKET,fd_set*),把socket加入fd_set集合。

FD_ISSET(SOCKET,fd_set*),判斷socket是否在集合fd_set中,并且socket準(zhǔn)備就緒。

FD_CLR(SOCKET,fd_set*),如果fd_set存在該SOCKET,則移除它。


下面是改進(jìn)后的服務(wù)端代碼

typedef struct _ThreadInfo {HANDLE hThread;bool bRunning;SOCKET sock; }ThreadInfo;typedef struct _AcceptThreadParam {bool bRunning;SOCKET listeningSocket; }AcceptThreadParam;std::list<ThreadInfo*> g_threadInfoList; CRITICAL_SECTION g_csForList;DWORD WINAPI ListeningThread(LPVOID lpParameter); DWORD WINAPI CommunicationThread(LPVOID lpParameter);int _tmain(int argc, _TCHAR* argv[]) {_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// ---------------------------- WSAStartup() ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");return -1;}//------------------------------------------------------------------------------//InitializeCriticalSection(&g_csForList);// ---------------------------- socket() ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");goto Main_End;}// ------------------------------------------------------------------------------------////---------------------------- bind() ----------------------------//// 初始化 struct sockaddr 結(jié)構(gòu)體, SOCKADDR_IN就是 struct sockaddr的宏定義SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS); localAddr.sin_port = htons(SERVE_PORT); memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");goto Main_End;}//------------------------------------------------------------------------------------////---------------------------- listen() ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");goto Main_End;}//------------------------------------------------------------------------------------//AcceptThreadParam threadParam;threadParam.bRunning = true;threadParam.listeningSocket = serverSocket;HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);if(0 == hListeningThread){printf("failed to create the listening thread!\n");goto Main_End;}else{printf("the server is listening now!pass any key to close the server!\n");}while(true){char ch = getchar();threadParam.bRunning = false;DWORD resWait = WaitForSingleObject(hListeningThread,3000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for the listening thread exiting!\n");}else{printf("the listening thread has exited!\n");}break;}Main_End:if(INVALID_SOCKET != serverSocket){closesocket(serverSocket);serverSocket = INVALID_SOCKET;}WSACleanup();DeleteCriticalSection(&g_csForList);system("pause");return 0; }DWORD WINAPI ListeningThread(LPVOID lpParameter) {AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;SOCKET serverSocket = pAcceptThreadParam->listeningSocket;while(pAcceptThreadParam->bRunning){//---------------------------- accept() ----------------------------//fd_set fdAccept;FD_ZERO(&fdAccept);FD_SET(serverSocket,&fdAccept);TIMEVAL acceptTimeVal;acceptTimeVal.tv_sec = 1;acceptTimeVal.tv_usec = 0;int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);if(selRes > 0){SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);if(INVALID_SOCKET == acceptedSocket){printf("accept error!\n");break;}printf("a client has connected to the server!\n");ThreadInfo* pTI = new ThreadInfo;pTI->bRunning = true;pTI->sock = acceptedSocket;pTI->hThread = CreateThread(0,0,CommunicationThread,(LPVOID)pTI,0,0);if(0 == pTI->hThread){printf("failed to create a thread!\n");delete pTI;pTI = 0;}else{EnterCriticalSection(&g_csForList);g_threadInfoList.push_back(pTI);LeaveCriticalSection(&g_csForList);}}else if(selRes < 0){printf("an error has occured when listening !\n");break;}}std::list<ThreadInfo*> tempList;EnterCriticalSection(&g_csForList);std::list<ThreadInfo*>::iterator listIter;for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++){(*listIter)->bRunning = false;tempList.push_back(*listIter);}g_threadInfoList.clear();LeaveCriticalSection(&g_csForList);int nSuccessfullyExit = 0;for(listIter = tempList.begin(); listIter != tempList.end(); listIter++){DWORD resWait = WaitForSingleObject((*listIter)->hThread,2000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for a communication thread exiting!\n");}else{nSuccessfullyExit++;}delete (*listIter);}printf("succeed waiting for %d thread exiting!\n",nSuccessfullyExit);tempList.clear();printf("listening thread is exiting!\n");return 0; }DWORD WINAPI CommunicationThread(LPVOID lpParameter) {ThreadInfo* pThreadInfo = (ThreadInfo*)lpParameter;SOCKET clientSocket = pThreadInfo->sock;fd_set fdRead,fdWrite;FD_ZERO(&fdRead);FD_ZERO(&fdWrite);FD_SET(clientSocket,&fdRead);FD_SET(clientSocket,&fdWrite);TIMEVAL sendTimeVal;sendTimeVal.tv_sec = 0;sendTimeVal.tv_usec = 500;int selRes = select(0,0,&fdWrite,0,&sendTimeVal);if(selRes <= 0){goto ThreadOver;}char recvBuffer[256];char sendBuffer[256];strcpy(sendBuffer,"server:Welcome to connect !");int sendBufLen = strlen(sendBuffer);int resSend = send(clientSocket,sendBuffer,sendBufLen,0);if(resSend != sendBufLen){printf("there are %d bytes to send, but it just succeeded sending %d bytes!\n",sendBufLen,resSend);goto ThreadOver;}while(pThreadInfo->bRunning){ FD_ZERO(&fdRead);FD_SET(pThreadInfo->sock,&fdRead);TIMEVAL recvTimeVal;recvTimeVal.tv_sec = 0;recvTimeVal.tv_usec = 500;int recvSelRes = select(0,&fdRead,0,0,&recvTimeVal);if(recvSelRes < 0){printf("socket error when receiving!\n");break;}else if(recvSelRes > 0){int recvLen = recv(clientSocket,recvBuffer,sizeof(recvBuffer),0);if(0 == recvLen){printf("a client close the socket!\n");break;}else if(recvLen < 0){printf("an error has happen when recving\n");break;}else{recvBuffer[recvLen] = '\0';printf("a client:%s\n",recvBuffer);strcpy(sendBuffer,"server:");strcat(sendBuffer,recvBuffer);sendBufLen = strlen(sendBuffer);FD_ZERO(&fdWrite);FD_SET(pThreadInfo->sock,&fdWrite);sendTimeVal.tv_sec = 0;sendTimeVal.tv_usec = 500;int sendSelRes = select(0,0,&fdWrite,0,&sendTimeVal);if(sendSelRes > 0){int bytesSent = send(clientSocket,sendBuffer,sendBufLen,0);if(bytesSent != sendBufLen){printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);break;}}else{printf("failed to send in 500 ms!\n");break;}}}}ThreadOver:closesocket(pThreadInfo->sock);bool bMainThreadWaiting = true;EnterCriticalSection(&g_csForList);std::list<ThreadInfo*>::iterator listIter;for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++){if(pThreadInfo == (*listIter)){bMainThreadWaiting = false;g_threadInfoList.erase(listIter);break;}}LeaveCriticalSection(&g_csForList);if(false == bMainThreadWaiting){CloseHandle(pThreadInfo->hThread);delete pThreadInfo;pThreadInfo = 0;}return 0; }


前面的代碼與之前的一樣,改變的地方在于accept的地方。對(duì)于一個(gè)監(jiān)聽(tīng)的socket,如果該socket可讀,說(shuō)明有用戶(hù)連接過(guò)來(lái)了。

全局維護(hù)了一個(gè)紀(jì)錄創(chuàng)建的線(xiàn)程的信息的鏈表,每創(chuàng)建一個(gè)線(xiàn)程都有一個(gè)標(biāo)識(shí)該線(xiàn)程是否應(yīng)該繼續(xù)循環(huán)執(zhí)行的bool變量。當(dāng)bRunning變?yōu)閒alse的時(shí)候,線(xiàn)程函數(shù)跳出循環(huán),返回。


當(dāng)需要停止服務(wù)端運(yùn)行時(shí),服務(wù)端只需要按任何一個(gè)鍵和回車(chē),就會(huì)通知線(xiàn)程退出,并且調(diào)用WaitForSingleObject(),來(lái)確認(rèn)線(xiàn)程已退出。還有利用了 EnterCriticalSection()和LeaveCriticalSection()臨界區(qū)函數(shù)來(lái)保證只有一個(gè)線(xiàn)程在操作全局的鏈表。




使用多線(xiàn)程要消耗一定的資源。對(duì)于fd_set,默認(rèn)最多可以容納64個(gè)socket.所以可以用1個(gè)線(xiàn)程去處理64個(gè)客戶(hù)端的連接。而不必每個(gè)客戶(hù)端都創(chuàng)建一個(gè)線(xiàn)程。

代碼如下:

typedef struct _AcceptThreadParam {bool bRunning;SOCKET listeningSocket; }AcceptThreadParam;#define SOCKET_ARRAY_SIZE 64SOCKET g_socketArray[SOCKET_ARRAY_SIZE]; int g_socketCount = 0; CRITICAL_SECTION g_csForSocketArray;DWORD WINAPI ListeningThread(LPVOID lpParameter); DWORD WINAPI CommunicationThread(LPVOID lpParameter);int _tmain(int argc, _TCHAR* argv[]) {_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// ---------------------------- WSAStartup() ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");return -1;}//------------------------------------------------------------------------------//InitializeCriticalSection(&g_csForSocketArray);g_socketCount = 0;// ---------------------------- socket() ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");goto Main_End;}// ------------------------------------------------------------------------------------////---------------------------- bind() ----------------------------//// 初始化 struct sockaddr 結(jié)構(gòu)體, SOCKADDR_IN就是 struct sockaddr的宏定義SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS); localAddr.sin_port = htons(SERVE_PORT); memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");goto Main_End;}//------------------------------------------------------------------------------------////---------------------------- listen() ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");goto Main_End;}//------------------------------------------------------------------------------------//AcceptThreadParam threadParam;threadParam.bRunning = true;threadParam.listeningSocket = serverSocket;bool bCommunicationThreadRunning = true;HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);HANDLE hCommunicationThread = CreateThread(0,0,CommunicationThread,&bCommunicationThreadRunning,0,0);if(0 == hListeningThread || 0 == hCommunicationThread){printf("failed to create a thread!\n");if(0 != hListeningThread){threadParam.bRunning = false;WaitForSingleObject(hListeningThread,2000);CloseHandle(hListeningThread);}if(0 != hCommunicationThread){bCommunicationThreadRunning = false;WaitForSingleObject(hCommunicationThread,2000);CloseHandle(hCommunicationThread);}goto Main_End;}else{printf("the server is listening now!pass any key to close the server!\n");}while(true){char ch = getchar();threadParam.bRunning = false;bCommunicationThreadRunning = false;DWORD resWait = WaitForSingleObject(hListeningThread,3000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for the listening thread exiting!\n");}else{printf("the listening thread has exited!\n");}CloseHandle(hListeningThread);resWait = WaitForSingleObject(hCommunicationThread,3000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for the communication thread exiting!\n");}else{printf("the communication thread has exited!\n");}CloseHandle(hCommunicationThread);break;}Main_End:if(INVALID_SOCKET != serverSocket){closesocket(serverSocket);serverSocket = INVALID_SOCKET;}WSACleanup();DeleteCriticalSection(&g_csForSocketArray);system("pause");return 0; }DWORD WINAPI ListeningThread(LPVOID lpParameter) {AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;SOCKET serverSocket = pAcceptThreadParam->listeningSocket;while(pAcceptThreadParam->bRunning){//---------------------------- accept() ----------------------------//fd_set fdAccept;FD_ZERO(&fdAccept);FD_SET(serverSocket,&fdAccept);TIMEVAL acceptTimeVal;acceptTimeVal.tv_sec = 1;acceptTimeVal.tv_usec = 0;int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);if(selRes > 0){SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);if(INVALID_SOCKET == acceptedSocket){printf("accept error!\n");break;}printf("a client has connected to the server!\n");fd_set fdWrite;FD_ZERO(&fdWrite);FD_SET(acceptedSocket,&fdWrite);TIMEVAL writeTimeVal;writeTimeVal.tv_sec = 0;writeTimeVal.tv_usec = 500;int writeSelRes = select(0,0,&fdWrite,0,&writeTimeVal);if(writeSelRes > 0){int sendBufferLen = strlen("server:Welcome to connect!");int bytesSent = send(acceptedSocket,"server:Welcome to connect!",sendBufferLen,0);if(bytesSent == sendBufferLen){EnterCriticalSection(&g_csForSocketArray);if(g_socketCount < 64){g_socketArray[g_socketCount] = acceptedSocket;g_socketCount++;}else{printf("the server has accepted more than 64 clients!\n");closesocket(acceptedSocket);}LeaveCriticalSection(&g_csForSocketArray);}else{printf("send error, there are %d bytes to be sent, but only %d bytes are sent!\n",sendBufferLen,bytesSent);closesocket(acceptedSocket);}}else{printf("select error of can not wait for sending data when select!\n");closesocket(acceptedSocket);}}else if(selRes < 0){printf("an error has occured when listening !\n");break;}}printf("listening thread is exiting!\n");return 0; }DWORD WINAPI CommunicationThread(LPVOID lpParameter) {bool* pBRunning = (bool*)lpParameter;char recvBuffer[256];char tempBuffer[256];while(true == *pBRunning){int currentSocketCount = 0;EnterCriticalSection(&g_csForSocketArray);if(0 == g_socketCount){LeaveCriticalSection(&g_csForSocketArray);Sleep(200);continue;}currentSocketCount = g_socketCount;LeaveCriticalSection(&g_csForSocketArray);fd_set fdRead;FD_ZERO(&fdRead);for(int i = 0; i < currentSocketCount; i++){FD_SET(g_socketArray[i],&fdRead);}TIMEVAL readTimeVal;readTimeVal.tv_sec = 1;readTimeVal.tv_usec = 0;int selRes = select(0,&fdRead,0,0,&readTimeVal);if(selRes > 0){for(int i = 0; i < currentSocketCount; i++){if(FD_ISSET(g_socketArray[i],&fdRead) != 0){int bytesRecv = recv(g_socketArray[i],recvBuffer,sizeof(recvBuffer),0);if(bytesRecv > 0){recvBuffer[bytesRecv] = '\0';printf("the %d client: %s\n",i + 1,recvBuffer);sprintf(tempBuffer,"the server:%s",recvBuffer);fd_set fdWrite;FD_ZERO(&fdWrite);FD_SET(g_socketArray[i],&fdWrite);TIMEVAL writeTimeVal;writeTimeVal.tv_sec = 0;writeTimeVal.tv_usec = 500;int writeSelRes = select(g_socketArray[i],0,&fdWrite,0,&writeTimeVal);if(writeSelRes > 0){int sendBufLen = strlen(tempBuffer);int bytesSent = send(g_socketArray[i],tempBuffer,sendBufLen,0);if(bytesSent == sendBufLen){break;}else{printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);}}else{printf("select error!\n");}}else if(0 == bytesRecv){printf("the %d client has closed the socket!\n",i + 1);}else{printf("recv error!\n");}closesocket(g_socketArray[i]);EnterCriticalSection(&g_csForSocketArray);g_socketArray[i] = g_socketArray[g_socketCount - 1];g_socketCount--;LeaveCriticalSection(&g_csForSocketArray);}}}else if(selRes < 0){printf("select error in communication thread!\n");}}EnterCriticalSection(&g_csForSocketArray);for(int i = 0; i < g_socketCount; i++){closesocket(g_socketArray[i]);}LeaveCriticalSection(&g_csForSocketArray);printf("the communication thread is exiting!\n");return 0; }

完成的功能一樣。只需要一個(gè)線(xiàn)程就可以處理多個(gè)客戶(hù)端了。




還可以用異步IO來(lái)實(shí)現(xiàn)該服務(wù)器,以下是用完成端口來(lái)實(shí)現(xiàn)同樣功能的服務(wù)器。

typedef struct _RepeatAcceptingThreadParam {SOCKET listeningSocket;bool* pBRunning; }RepeatAcceptingThreadParam;typedef struct _CompletionPortWorkerThreadParam {HANDLE hCompletionPort;bool* pBRunning; }CompletionPortWorkerThreadParam;#define MESSAGE_BUF_SIZE 1024enum OPERATION_TYPE {OPERATION_SEND,OPERATION_RECV };typedef struct {SOCKET sock;WSAOVERLAPPED overlap;WSABUF wsaBuf;char message[1024];DWORD bytesRecv;DWORD flags;OPERATION_TYPE operationType; }PER_IO_OPERATION_DATA;//global vector, which saves the information of the client sockets connected to the server std::vector<PER_IO_OPERATION_DATA*> g_perIoDataPointerVec;//accept sockets connected to the server's listening socket in a recycle - while DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter); //the worker thread that deal with the communications between the server and the clients. DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter);int _tmain(int argc,_TCHAR* argv[]) {_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// ---------------------------- WSAStartup() ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");return -1;}//------------------------------------------------------------------------------//// ---------------------------- socket() ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");return -1;}// ------------------------------------------------------------------------------------////---------------------------- bind() ----------------------------//// 初始化 struct sockaddr 結(jié)構(gòu)體, SOCKADDR_IN就是 struct sockaddr的宏定義SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS); localAddr.sin_port = htons(SERVE_PORT); memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");closesocket(serverSocket);return -1;}//------------------------------------------------------------------------------------////---------------------------- listen() ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");closesocket(serverSocket);return -1;}//------------------------------------------------------------------------------------//bool bRepeatAcceptingThreadRunning = true; // a bool variable that take control of terminating the RepeatAcceptingThread.//init the parameter for the RepeatAcceptingThread.RepeatAcceptingThreadParam rtp;rtp.listeningSocket = serverSocket;rtp.pBRunning = &bRepeatAcceptingThreadRunning;HANDLE hRepeatAcceptingThread = CreateThread(0,0,RepeatAcceptingThread,&rtp,0,0);if(0 == hRepeatAcceptingThread){printf("failed to create the repeat-accepting thread!\n");closesocket(serverSocket);return -1;}printf("the repeat-accepting thread has run!\n");while(true){// pass any key char ch = getchar();bRepeatAcceptingThreadRunning = false;//to notify the RepeatAcceptingThread to exit safely DWORD waitRes = WaitForSingleObject(hRepeatAcceptingThread,3000);if(WAIT_TIMEOUT == waitRes){printf("failed to wait for the repeatAcceptingThread exiting!\n");}else{printf("the repeat accepting thread has exited!\n");}CloseHandle(hRepeatAcceptingThread);break;}system("pause");return 0; }DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter) {//get the parameters passed by the creator of the thread.RepeatAcceptingThreadParam* pParam = (RepeatAcceptingThreadParam*)lpParameter;SOCKET listeningSocket = pParam->listeningSocket;bool* pStillRun = pParam->pBRunning;// create a completion portHANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);if(0 == hCompletionPort){printf("failed to CreateIoCompletionPort!\n");return -1;}// a bool variable for notifying the worker threads of exiting.bool bWorkThreadRunning = true;// a vector of HANDLEs,which will be used for synchronization of waiting the worker threads to exit.std::vector<HANDLE> threadHandlesVec;SYSTEM_INFO systemInfo;GetSystemInfo(&systemInfo);//the parameter to be passed to the worker thread.CompletionPortWorkerThreadParam cpwtp;cpwtp.pBRunning = &bWorkThreadRunning;cpwtp.hCompletionPort = hCompletionPort;for(int i = 0; i < systemInfo.dwNumberOfProcessors; i++){HANDLE hThread = CreateThread(0,0,CompletionPortWorkerThread,&cpwtp,0,0);if(0 == hThread) {printf("failed to create a completion port worker thread!\n");bWorkThreadRunning = false;// terminate all threads created safely.std::vector<HANDLE>::iterator vecIter;for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++){DWORD waitRes = WaitForSingleObject(*vecIter,2000);if(WAIT_TIMEOUT == waitRes){printf("failed the wait for the completion port worker thread!\n");}CloseHandle(*vecIter);}threadHandlesVec.clear();CloseHandle(hCompletionPort);return -1;}else{threadHandlesVec.push_back(hThread); //add the handle to the vector}}printf("succeed creating completion port worker threads!\n");while(true == *pStillRun){fd_set fdAccept;FD_ZERO(&fdAccept);FD_SET(listeningSocket,&fdAccept);TIMEVAL acceptTimeVal;acceptTimeVal.tv_sec = 1;acceptTimeVal.tv_usec = 0;int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);if(selRes > 0) // a client connected{SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = WSAAccept(listeningSocket,(struct sockaddr*)&clientAddr,&addrLen,0,0);if(0 == acceptedSocket){printf("failed to accept a connection!\n");}else{printf("a clent %s:%d has connected!\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));PER_IO_OPERATION_DATA* perIoData = new PER_IO_OPERATION_DATA;if(0 == perIoData){closesocket(acceptedSocket);printf("failed to new a struct! there is not enough memory!\n\n");}else{//associate the newly connected client socket with the completion port.if(0 == CreateIoCompletionPort((HANDLE)acceptedSocket,hCompletionPort,(ULONG_PTR)perIoData,0)){printf("failed to associate the newly connected client socket with the completion port!\n");closesocket(acceptedSocket);delete perIoData;perIoData = 0;}else{//associated successfully, Set the information of the client socket in A PER_IO_OPERATION_DATA struct.//when a IO operation is completed, we can get notified with the struct to be one of the parameters.perIoData->sock = acceptedSocket;perIoData->operationType = OPERATION_SEND;perIoData->wsaBuf.buf = perIoData->message;perIoData->overlap.hEvent = INVALID_HANDLE_VALUE;strcpy(perIoData->message,"Welcome to connect to the server!");perIoData->wsaBuf.len = strlen(perIoData->message);int sendRes = WSASend(acceptedSocket,&(perIoData->wsaBuf),1,&(perIoData->bytesRecv),0,0,0);if(0 == sendRes) //finished immediately{// asynchronously invoke a receive operation. When the reception finished,we can get its information by// invoking GetQueuedCompletionStatus()perIoData->wsaBuf.buf = perIoData->message;perIoData->wsaBuf.len = MESSAGE_BUF_SIZE;perIoData->flags = 0;perIoData->operationType = OPERATION_RECV;ZeroMemory(&perIoData->overlap,sizeof(perIoData->overlap));int recvRes = WSARecv(acceptedSocket,&perIoData->wsaBuf,1,&perIoData->bytesRecv,&perIoData->flags,&perIoData->overlap,0);if(0 == recvRes) //the receiving operation finished immediately , the information of the operation has been queued.{g_perIoDataPointerVec.push_back(perIoData);}else if(SOCKET_ERROR == recvRes && WSA_IO_PENDING == WSAGetLastError()) //the receiving operation will finish later{g_perIoDataPointerVec.push_back(perIoData);}else{printf("failed to WSARecv!\n");closesocket(acceptedSocket);delete perIoData;perIoData = 0;}}else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError()) //the sending operation will finish later{g_perIoDataPointerVec.push_back(perIoData);}else{//int lastErr = WSAGetLastError();printf("send data error!\n");closesocket(acceptedSocket);delete perIoData;perIoData = 0;}}}}}else if(selRes < 0){printf("select error!\n");}}bWorkThreadRunning = false; //notifies the worker threads of exiting// terminate all threads created safely.std::vector<HANDLE>::iterator vecIter;for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++){DWORD waitRes = WaitForSingleObject(*vecIter,2000);if(WAIT_TIMEOUT == waitRes){printf("failed the wait for the completion port worker thread!\n");}CloseHandle(*vecIter);}threadHandlesVec.clear();CloseHandle(hCompletionPort);//delete the structs of PER_IO_OPERATION_DATA newed for clients connected.std::vector<PER_IO_OPERATION_DATA*>::iterator pIoDataPointerIter;for(pIoDataPointerIter = g_perIoDataPointerVec.begin(); pIoDataPointerIter != g_perIoDataPointerVec.end(); pIoDataPointerIter++){closesocket((*pIoDataPointerIter)->sock);delete (*pIoDataPointerIter);*pIoDataPointerIter = 0;}g_perIoDataPointerVec.clear();printf(" the repeat accepting thread is exiting!\n");return 0; }bool ReleaseIOOperationData(PER_IO_OPERATION_DATA* & pDataToBeDeleted) {bool retVal = false;std::vector<PER_IO_OPERATION_DATA*>::iterator vecIter;for(vecIter = g_perIoDataPointerVec.begin(); vecIter != g_perIoDataPointerVec.end(); vecIter++){if(pDataToBeDeleted == (*vecIter)){g_perIoDataPointerVec.erase(vecIter);closesocket(pDataToBeDeleted->sock);delete pDataToBeDeleted;pDataToBeDeleted = 0;retVal = true;break;}}return retVal; }DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter) {CompletionPortWorkerThreadParam* pParam = (CompletionPortWorkerThreadParam*)lpParameter;bool* pStillRun = pParam->pBRunning;HANDLE hCompletionPort = pParam->hCompletionPort;DWORD dwBytesTransfered;PER_IO_OPERATION_DATA* pIoData;WSAOVERLAPPED* pOverlap;while(true == *pStillRun){dwBytesTransfered = 0;pIoData = 0;pOverlap = 0;BOOL bGetStatus = GetQueuedCompletionStatus(hCompletionPort,&dwBytesTransfered,(PULONG_PTR)&pIoData,&pOverlap,500);if(FALSE == bGetStatus){if(0 == pOverlap) //did not get a packet from the queue.{continue; }else{//get a packet for a failed I/O operation.}}if(OPERATION_SEND == pIoData->operationType){if(0 == dwBytesTransfered) //a packet for a failed I/O operation.{printf("the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}else{// receive operation.pIoData->operationType = OPERATION_RECV;pIoData->wsaBuf.buf = pIoData->message;pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;pIoData->flags = 0;ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError()){printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}}}else if(OPERATION_RECV == pIoData->operationType){if(0 == dwBytesTransfered) //a packet for a failed I/O operation.{printf("the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}else{// show the data receivedpIoData->message[dwBytesTransfered] = '\0';printf("the client %d:%s \n",pIoData->sock,pIoData->message);//send back the data received add a "server:" in the frontchar tempBuf[MESSAGE_BUF_SIZE];sprintf(tempBuf,"server:%s",pIoData->message);strcpy(pIoData->message,tempBuf);pIoData->operationType = OPERATION_SEND;pIoData->wsaBuf.buf = pIoData->message;pIoData->wsaBuf.len = strlen(pIoData->message);int sendRes = WSASend(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,0,0,0);if(0 == sendRes){pIoData->operationType = OPERATION_RECV;pIoData->wsaBuf.buf = pIoData->message;pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;pIoData->flags = 0;ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError()){printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}}else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError()){}else{printf("send error, maybe the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}}}}printf("a completion port thread is exiting!\n");return 0; }

posted on 2014-12-16 08:17 NET未來(lái)之路 閱讀(...) 評(píng)論(...) 編輯 收藏

轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/4166277.html

總結(jié)

以上是生活随笔為你收集整理的Windows socket之最简单的socket程序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。