window下 IOCP網絡模式
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include<mswsock.h>
#pragma comment(lib,"Mswsock.lib")
#include<stdio.h>
#define nClient 10
enum IO_TYPE
{
ACCEPT = 10,
RECV,
SEND
};
//數據緩衝區空間大小
#define DATA_BUFF_SIZE 1024
struct IO_DATA_BASE
{
//重疊體
OVERLAPPED overlapped;
//
SOCKET sockfd;
//數據緩衝區
char buffer[DATA_BUFF_SIZE];
//實際緩衝區數據長度
int length;
//操作類型
IO_TYPE iotype;
};
//向IOCP投遞接受連接的任務
void postAccept(SOCKET sockServer, IO_DATA_BASE* pIO_DATA)
{
pIO_DATA->iotype = IO_TYPE::ACCEPT;
pIO_DATA->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (FALSE == AcceptEx(sockServer
, pIO_DATA->sockfd
, pIO_DATA->buffer
, 0
, sizeof(sockaddr_in) + 16
, sizeof(sockaddr_in) + 16
, NULL
, &pIO_DATA->overlapped
))
{
int err = WSAGetLastError();
if (ERROR_IO_PENDING != err)
{
printf("AcceptEx failed with error %d\n", err);
return;
}
}
}
//向IOCP投遞接收數據的任務
void postRecv(IO_DATA_BASE* pIO_DATA)
{
pIO_DATA->iotype = IO_TYPE::RECV;
WSABUF wsBuff = {};
wsBuff.buf = pIO_DATA->buffer;
wsBuff.len = DATA_BUFF_SIZE;
DWORD flags = 0;
ZeroMemory(&pIO_DATA->overlapped, sizeof(OVERLAPPED));
if (SOCKET_ERROR == WSARecv(pIO_DATA->sockfd, &wsBuff, 1, NULL, &flags, &pIO_DATA->overlapped, NULL))
{
int err = WSAGetLastError();
if (ERROR_IO_PENDING != err)
{
printf("WSARecv failed with error %d\n", err);
return;
}
}
}
//向IOCP投遞發送數據的任務
void postSend(IO_DATA_BASE* pIO_DATA)
{
pIO_DATA->iotype = IO_TYPE::SEND;
WSABUF wsBuff = {};
wsBuff.buf = pIO_DATA->buffer;
wsBuff.len = pIO_DATA->length;
DWORD flags = 0;
ZeroMemory(&pIO_DATA->overlapped, sizeof(OVERLAPPED));
if (SOCKET_ERROR == WSASend(pIO_DATA->sockfd, &wsBuff, 1, NULL, flags, &pIO_DATA->overlapped, NULL))
{
int err = WSAGetLastError();
if (ERROR_IO_PENDING != err)
{
printf("WSASend failed with error %d\n", err);
return;
}
}
}
//-- 用Socket API建立簡易TCP服務端
//-- IOCP Server基礎流程
int main()
{
//啓動Windows socket 2.x環境
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
WSAStartup(ver, &dat);
//------------//
// 1 建立一個socket
// 當使用socket函數創建套接字時,會默認設置WSA_FLAG_OVERLAPPED標誌
//////
// 注意這裏也可以用 WSASocket函數創建socket
// 最後一個參數需要設置爲重疊標誌(WSA_FLAG_OVERLAPPED)
// SOCKET sockServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
//////
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 2.1 設置對外IP與端口信息
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);//host to net unsigned short
_sin.sin_addr.s_addr = INADDR_ANY;
// 2.2 綁定sockaddr與ServerSocket
if (SOCKET_ERROR == bind(sockServer, (sockaddr*)&_sin, sizeof(_sin)))
{
printf("錯誤,綁定網絡端口失敗...\n");
}
else {
printf("綁定網絡端口成功...\n");
}
// 3 監聽ServerSocket
if (SOCKET_ERROR == listen(sockServer, 64))
{
printf("錯誤,監聽網絡端口失敗...\n");
}
else {
printf("監聽網絡端口成功...\n");
}
//-------IOCP Begin-------//
//4 創建完成端口IOCP
HANDLE _completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (NULL == _completionPort)
{
printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
return -1;
}
//5 關聯IOCP與ServerSocket
//完成鍵
auto ret = CreateIoCompletionPort((HANDLE)sockServer, _completionPort, (ULONG_PTR)sockServer, 0);
if (!ret)
{
printf("關聯IOCP與ServerSocket失敗,錯誤碼=%d\n", GetLastError());
return -1;
}
//6 向IOCP投遞接受連接的任務
IO_DATA_BASE ioData[nClient] = {};
for (int n = 0; n < nClient; n++)
{
postAccept(sockServer, &ioData[n]);
}
int msgCount = 0;
while (true)
{
DWORD bytesTrans = 0;
SOCKET sock = INVALID_SOCKET;
IO_DATA_BASE* pIOData;
//在網絡內存中查找可連接的的客戶端socket
if (FALSE == GetQueuedCompletionStatus(_completionPort, &bytesTrans, (PULONG_PTR)&sock, (LPOVERLAPPED*)&pIOData, 1))
{
int err = GetLastError();
if (WAIT_TIMEOUT == err)
{
continue;
}
if (ERROR_NETNAME_DELETED == err)
{
printf("關閉 sockfd=%d\n", pIOData->sockfd);
closesocket(pIOData->sockfd);
continue;
}
printf("GetQueuedCompletionStatus failed with error %d\n", err);
break;
}
//7.1 接受鏈接 完成
if (IO_TYPE::ACCEPT == pIOData->iotype)
{
printf("新客戶端加入 sockfd=%d\n", pIOData->sockfd);
//7.2 關聯IOCP與ClientSocket
auto ret = CreateIoCompletionPort((HANDLE)pIOData->sockfd, _completionPort, (ULONG_PTR)pIOData->sockfd, 0);
if (!ret)
{
printf("關聯IOCP與ClientSocket=%d失敗\n", pIOData->sockfd);
closesocket(pIOData->sockfd);
continue;
}
//7.3 向IOCP投遞接收數據任務
postRecv(pIOData);
}
//8.1 接收數據 完成 Completion
else if (IO_TYPE::RECV == pIOData->iotype)
{
if (bytesTrans <= 0)
{//客戶端斷開處理
printf("關閉 sockfd=%d, RECV bytesTrans=%d\n", pIOData->sockfd, bytesTrans);
closesocket(pIOData->sockfd);
continue;
}
printf("收到數據: sockfd=%d, bytesTrans=%d msgCount=%d\n", pIOData->sockfd, bytesTrans, ++msgCount);
pIOData->length = bytesTrans;
//8.2 向IOCP投遞發送數據任務
postSend(pIOData);
}
//9.1 發送數據 完成 Completion
else if (IO_TYPE::SEND == pIOData->iotype)
{
if (bytesTrans <= 0)
{//客戶端斷開處理
printf("關閉 sockfd=%d, SEND bytesTrans=%d\n", pIOData->sockfd, bytesTrans);
closesocket(pIOData->sockfd);
continue;
}
printf("發送數據: sockfd=%d, bytesTrans=%d msgCount=%d\n", pIOData->sockfd, bytesTrans, msgCount);
//9.2 向IOCP投遞接收數據任務
postRecv(pIOData);
}
else {
printf("未定義行爲 sockfd=%d", sock);
}
}
//------------//
//10.1 關閉ClientSocket
for (int n = 0; n < nClient; n++)
{
closesocket(ioData[n].sockfd);
}
//10.2 關閉ServerSocket
closesocket(sockServer);
//10.3 關閉完成端口
CloseHandle(_completionPort);
//清除Windows socket環境
WSACleanup();
return 0;
}