#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#define FD_SETSIZE 1024
#include <winsock2.h>
#include <windows.h>
#include <string>
#include <mswsock.h>
#include <iostream>
#pragma comment(lib,"Mswsock.lib")
#pragma comment(lib,"ws2_32.lib")
using namespace std;
//自定义的消息值不能和系统消息冲突
#define WSAAsyncSelectMsg WM_USER +1
#define Sever_Port 9999
struct fd_es_set //事件模型
{
UINT count;
SOCKET sockall[1024];
HANDLE eventall[1024];
};
struct fd_win_set//消息模型
{
UINT count;
SOCKET sockall[1024];
};
struct fd_esIo_set //重叠IO事件模型
{
UINT count;
SOCKET sockall[1024];
OVERLAPPED IOeventall[1024];
};
struct fd_FinshIo_set //完成端口模型
{
UINT count;
SOCKET sockall[1024];
OVERLAPPED IOeventall[1024];
HANDLE Port; //完成端口
PHANDLE Pthread; //保存的线程指针
DWORD ThreadNum; //系统的线程数量
BOOL ThreadOpen; //线程开关
};
#include "main.h"
#define WSA_MAX_STR 1024
bool PostRecv(UINT Index);
bool PostSend(UINT Index, string sendmsg);
bool PostAccept();
fd_FinshIo_set allsock;
char _recvstr[WSA_MAX_STR]; //recv消息用的
VOID Server_write_error()
{
}
void clear()
{
//释放线程句柄
for (UINT i = 0; i < allsock.ThreadNum; i++)
{
CloseHandle(allsock.Pthread[i]);
}
//释放内存
free(allsock.Pthread);
/*释放整个结构体,可能有些事件和socket已经被释放过了,不影响*/
for (UINT i = 0; i < allsock.count; i++)
{
if (0 == allsock.sockall[i])
continue;
//释放stocket
closesocket(allsock.sockall[i]);
//关闭事件对象
WSACloseEvent(allsock.IOeventall[i].hEvent);
}
//清理网络库
WSACleanup();
}
//线程用于接收回调
DWORD WINAPI PTHREAD_ROUTINE(LPVOID lpThreadParameter)
{
_Out_ DWORD lpNumberOfBytes; //字节数
_Out_ ULONG_PTR lpCompletionKey; //绑定函数CreateIoCompletionPort传来的参数3
_Out_ LPOVERLAPPED lpOverlapped; //重叠结构
//取通知信息,无信息会自动挂起线程
while (allsock.ThreadOpen)
{// 参数3是传入参数,发给函数GetQueuedCompletionStatus的参数3
bool ret = GetQueuedCompletionStatus(allsock.Port, &lpNumberOfBytes, &lpCompletionKey, &lpOverlapped, INFINITE);
//if (false == ret)
//{
// printf("error_GetQueuedCompletionStatus\n");
// continue;
//}
if (0 == lpCompletionKey)
{//0是服务器的数组下标,服务器链接
//绑定完成端口
//绑定sorket 和 完成端口 参数3是传入参数,发给函数GetQueuedCompletionStatus的参数3
if (0 == CreateIoCompletionPort((HANDLE)allsock.sockall[allsock.count], allsock.Port, allsock.count, 0))
{
printf("error");
//关闭客户端链接
closesocket(allsock.sockall[allsock.count]);
WSACloseEvent(allsock.IOeventall[allsock.count].hEvent);
}
/*接收客户端的连接消息*/
PostRecv(allsock.count);
/*返回给服务器消息*/
string sedmsg = "服务器已链接";
PostSend(allsock.count, sedmsg);
allsock.count++;
/*重新投递accept*/
PostAccept();
}
else
{//客户端消息
if (0 != _recvstr[0])
{//recv
printf("recv:%s\n", _recvstr);
memset(_recvstr, 0, sizeof(_recvstr));
string sedmsg = "服务器收到";
PostSend(lpCompletionKey, sedmsg);
//继续传递
PostRecv(lpCompletionKey);
}
else
{//send
//printf("sendok\n");
//PostSend(lpCompletionKey);
}
//客户端关闭
if (0 == lpNumberOfBytes)
{
printf("close\n");
//关闭socket 和事件
closesocket(allsock.sockall[lpCompletionKey]);
WSACloseEvent(allsock.IOeventall[lpCompletionKey].hEvent);
//从数组清空 ,由于完成端口和下标指定了,所以不能用之前的方法
allsock.sockall[lpCompletionKey] = 0;
allsock.IOeventall[lpCompletionKey].hEvent = 0;
}
}
}
return 1;
}
bool PostSend(UINT Index, string sendmsg)
{
WSABUF lpBuffers = { (ULONG)sendmsg.length() ,(char*)sendmsg.c_str() };
DWORD lpNumberOfBytesRecvd;
DWORD flag = 0;
int ret = WSASend(allsock.sockall[Index], //接受的客户端socket
&lpBuffers, //接收的缓冲区
1, //参数二的个数
&lpNumberOfBytesRecvd, //接收成功的话保存接收字节数量
flag, //设置flag,默认0
&allsock.IOeventall[Index], //IO重叠结构
0 //IO重叠回调例程
);
int error = WSAGetLastError();
if (ERROR_IO_PENDING != error)
{//函数执行出错
return 0;
}
return 1;
}
bool PostRecv(UINT Index)
{
WSABUF lpBuffers = { sizeof(_recvstr) ,_recvstr };
DWORD lpNumberOfBytesRecvd;
DWORD flag = 0;
int ret = WSARecv(allsock.sockall[Index], //接受的客户端socket
&lpBuffers, //接收的缓冲区
1, //参数二的个数
&lpNumberOfBytesRecvd, //接收成功的话保存接收字节数量
&flag, //recv参数5,默认0
&allsock.IOeventall[Index], //IO重叠结构
0 //IO重叠回调例程
);
int error = WSAGetLastError();
if (ERROR_IO_PENDING != error)
{//函数执行出错
return 0;
}
return 1;
}
bool PostAccept()
{
/*创建socket和event,给客户端Socket和event加入数组*/
allsock.sockall[allsock.count] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
allsock.IOeventall[allsock.count].hEvent = WSACreateEvent();
char getstr[WSA_MAX_STR];
DWORD lpdwBytesReceived;
/*返回true代表客户端立即连上了服务器*/
bool res = AcceptEx(allsock.sockall[0], //投递参数1服务器socket,
allsock.sockall[allsock.count], // 异步接收服务器socket
getstr,//缓冲区制作,接收新链接发送的第一条数据,之后数据用WSArecv接收了,鸡肋
0, //设置0 参数3无效了;
sizeof(sockaddr_in) + 16,//为本地地址信息保留的字节数,此值至少比传输协议大16字节
sizeof(sockaddr_in) + 16,//为客户端地址信息保留的字节数,此值至少比传输协议大16字节
&lpdwBytesReceived, //接受参数3数据的长度
&allsock.IOeventall[0] //服务器的重叠结构
);
int error = WSAGetLastError();
if (ERROR_IO_PENDING != error)
{//函数执行出错
return 0;
}
return 1;
}
/*1.打开网络库
* 2.校验网络库版本
* 3.创建SOCKET
* 4.绑定IP地址和端口
* 5.创建完成端口,并绑定客户端socket/和完成端口
* 6.开始监听
* 7.开始异步接收accept recv
*/
int create()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* 使用Windef.h中声明的MAKEWORD(低字节、高字节)宏 */
wVersionRequested = MAKEWORD(2, 2);
/*启用网络链接库,调用的封装库命令*/
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return -1;
}
/*确认WinSock DLL支持2.2*/
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
//清理网络库
WSACleanup();
return -1;
}
//创建套接字。 创建网络类型 tcp或者upd
//SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
if (INVALID_SOCKET == socketServer)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_socket", ret.c_str(), 0);
//清理网络库
WSACleanup();
return -1;
}
//设置sockaddr结构
sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = INADDR_ANY;
saServer.sin_port = htons(Sever_Port);
// 绑定本机(服务器)IP和端口
//sockaddr结构中的信息
if (SOCKET_ERROR == bind(socketServer, (SOCKADDR*)&saServer, sizeof(saServer)))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_bind", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*将服务器socket和事件对象句柄写进结构体*/
allsock.IOeventall[allsock.count].hEvent = WSACreateEvent();;
allsock.sockall[allsock.count] = socketServer;
allsock.count++;
//创建一个完成端口
allsock.Port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
if ( 0 == allsock.Port)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
//绑定sorket 和 完成端口 参数3是传入参数,发给函数GetQueuedCompletionStatus的参数3
if (0 == CreateIoCompletionPort((HANDLE)socketServer, allsock.Port, 0, 0))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
CloseHandle(allsock.Port);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*监听本机(服务器)的套接字*/
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
CloseHandle(allsock.Port);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
//创建线程,查询CPU是几核就创建几核
SYSTEM_INFO lpSystemInfo;
GetSystemInfo(&lpSystemInfo);
allsock.ThreadNum = lpSystemInfo.dwNumberOfProcessors;
allsock.Pthread = (PHANDLE)malloc(allsock.ThreadNum * sizeof(HANDLE));
allsock.ThreadOpen = true;
/*创建线程来用于消息接收*/
for (UINT i = 0; i < allsock.ThreadNum; i++)
{
allsock.Pthread[i] = CreateThread(0, 0, PTHREAD_ROUTINE, 0, 0, 0);
}
异步接收客户端
if (0 == PostAccept())
{
clear();
return 0;
}
while (allsock.ThreadOpen)
{
Sleep(1000);
}
return 1;
}
BOOL WINAPI HANDLER_ROUTINE(_In_ DWORD CtrlType)
{
switch (CtrlType)
{
case CTRL_CLOSE_EVENT:
{
allsock.ThreadOpen = false;
clear();
}
default:
break;
}
return true;
}
int main()
{
//创建个回调监视控制台关闭
SetConsoleCtrlHandler(HANDLER_ROUTINE,true);
create();
// create("127.0.0.1");
return 0;
}
上面有数组内存的问题,下面是完美方案
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#define FD_SETSIZE 1024
#include <winsock2.h>
#include <windows.h>
#include <string>
#include <mswsock.h>
#include <iostream>
#include <vector>
#pragma comment(lib,"Mswsock.lib")
#pragma comment(lib,"ws2_32.lib")
using namespace std;
//自定义的消息值不能和系统消息冲突
#define WSAAsyncSelectMsg WM_USER +1
#define Sever_Port 12345
struct fd_es_set //事件模型
{
UINT count;
SOCKET sockall[1024];
HANDLE eventall[1024];
};
struct fd_win_set//消息模型
{
UINT count;
SOCKET sockall[1024];
};
struct fd_esIo_set //重叠IO事件模型
{
UINT count;
SOCKET sockall[1024];
OVERLAPPED IOeventall[1024];
};
enum class Socket_type
{
Socket_APPECT,
Socket_SEND,
Socket_RECV,
Socket_CONNET,
Socket_CLOSE
};
#define WSA_MAX_STR 1024
//接收和保存客户端信息
typedef class OnlineClientMsg
{
public:
OVERLAPPED lpOverlapped; //重叠结构
SOCKET sockall;
Socket_type TYPE;
char _recvstr[WSA_MAX_STR]; //recv消息用的
}ClientMsg;
class set_system
{
public:
HANDLE Port; //完成端口
PHANDLE Pthread; //保存的线程指针
DWORD ThreadNum; //系统的线程数量
BOOL ThreadOpen; //线程开关
SOCKET Seversockall;
};
#include "main.h"
bool PostRecv(OnlineClientMsg* msg);
bool PostSend(OnlineClientMsg* msg, string sendmsg);
bool PostAccept();
set_system set;
VOID Server_write_error()
{
}
void clear()
{
//释放线程句柄
for (UINT i = 0; i < set.ThreadNum; i++)
{
CloseHandle(set.Pthread[i]);
}
//释放内存
free(set.Pthread);
//释放stocket
closesocket(set.Seversockall);
//清理网络库
WSACleanup();
}
//线程用于接收回调
DWORD WINAPI PTHREAD_ROUTINE(LPVOID lpThreadParameter)
{
_Out_ DWORD lpNumberOfBytes; //字节数
_Out_ ULONG_PTR lpCompletionKey; //绑定函数CreateIoCompletionPort传来的参数3
_Out_ OnlineClientMsg* NewclientMsg ; //用自己的结构体替代LPOVERLAPPED,这样能接收到更多数据
//取通知信息,无信息会自动挂起线程
while (set.ThreadOpen)
{// 参数3是传入参数,发给函数GetQueuedCompletionStatus的参数3
bool ret = GetQueuedCompletionStatus(set.Port, &lpNumberOfBytes, &lpCompletionKey, (LPOVERLAPPED*)&NewclientMsg, INFINITE);
if (false == ret)
{
printf("Socket_CLOSE\n");
closesocket(NewclientMsg->sockall);
delete NewclientMsg;
continue;
}
{
//绑定完成端口
switch (NewclientMsg->TYPE)
{//接收到连接请求
case Socket_type::Socket_APPECT:
{
//绑定sorket 和 完成端口 参数3是传入参数,发给函数GetQueuedCompletionStatus的参数3
if (0 == CreateIoCompletionPort((HANDLE)NewclientMsg->sockall, set.Port, 1, 0))
{
printf("error CreateIoCompletionPort");
//关闭客户端链接
closesocket(NewclientMsg->sockall);
// WSACloseEvent(allsock.IOeventall[allsock.count].hEvent);
delete NewclientMsg;
}
/*接收到请求链接后给客户端发送个 通信成功的消息*/
PostSend(NewclientMsg , "OK");
/*重新投递accept*/
PostAccept();
break;
}
case Socket_type::Socket_RECV:
{
{
// printf("recv:%s\n ", NewclientMsg->_recvstr);
cout <<"recv:" << NewclientMsg->_recvstr << endl;
/*返回给服务器消息*/
string sedmsg = "服务器收到";
PostSend(NewclientMsg, sedmsg);
}
break;
}
case Socket_type::Socket_SEND:
{
//投递接收这个客户端消息的线程
PostRecv(NewclientMsg);
break;
}
default:
break;
}
}
}
return 1;
}
bool PostSend(OnlineClientMsg* msg,string sendmsg)
{
msg->TYPE = Socket_type::Socket_SEND;
WSABUF lpBuffers = { (ULONG)sendmsg.length() ,(char*)sendmsg.c_str() };
DWORD lpNumberOfBytesRecvd;
DWORD flag = 0;
int ret = WSASend(msg->sockall, //接受的客户端socket
&lpBuffers, //接收的缓冲区
1, //参数二的个数
&lpNumberOfBytesRecvd, //接收成功的话保存接收字节数量
flag, //设置flag,默认0
&msg->lpOverlapped, //IO重叠结构
0 //IO重叠回调例程
);
int error = WSAGetLastError();
if (ERROR_IO_PENDING != error)
{//函数执行出错
return 0;
}
return 1;
}
bool PostRecv(OnlineClientMsg* msg)
{
msg->TYPE = Socket_type::Socket_RECV;
ZeroMemory(&msg->_recvstr, sizeof(msg->_recvstr));
WSABUF lpBuffers = { sizeof(msg->_recvstr) , msg->_recvstr };
DWORD lpNumberOfBytesRecvd;
DWORD flag = 0;
int ret = WSARecv(msg->sockall, //接受的客户端socket
&lpBuffers, //接收的缓冲区
1, //参数二的个数
&lpNumberOfBytesRecvd, //接收成功的话保存接收字节数量
&flag, //recv参数5,默认0
&msg->lpOverlapped, //IO重叠结构
0 //IO重叠回调例程
);
int error = WSAGetLastError();
if (ERROR_IO_PENDING != error)
{//函数执行出错
return 0;
}
return 1;
}
bool PostAccept()
{
ClientMsg* msg = new ClientMsg;
if (msg)
ZeroMemory(msg,sizeof(ClientMsg));
msg->TYPE = Socket_type::Socket_APPECT;
msg->sockall = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
char getstr[WSA_MAX_STR];
DWORD lpdwBytesReceived;
/*返回true代表客户端立即连上了服务器*/
bool res = AcceptEx(set.Seversockall, //投递参数1服务器socket,
msg->sockall, // 异步接收客户端socket
getstr,//缓冲区制作,接收新链接发送的第一条数据,之后数据用WSArecv接收了,鸡肋
0, //设置0 参数3无效了;
sizeof(sockaddr_in) + 16,//为本地地址信息保留的字节数,此值至少比传输协议大16字节
sizeof(sockaddr_in) + 16,//为客户端地址信息保留的字节数,此值至少比传输协议大16字节
&lpdwBytesReceived, //接受参数3数据的长度
(LPOVERLAPPED)msg //重叠结构 跟着参数2 socket一起传入GetQueuedCompletionStatus
);
int error = WSAGetLastError();
if (ERROR_IO_PENDING != error)
{//函数执行出错
return 0;
}
return 1;
}
/*1.打开网络库
* 2.校验网络库版本
* 3.创建SOCKET
* 4.绑定IP地址和端口
* 5.创建完成端口,并绑定客户端socket/和完成端口
* 6.开始监听
* 7.开始异步接收accept recv
*/
int create()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* 使用Windef.h中声明的MAKEWORD(低字节、高字节)宏 */
wVersionRequested = MAKEWORD(2, 2);
/*启用网络链接库,调用的封装库命令*/
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return -1;
}
/*确认WinSock DLL支持2.2*/
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
//清理网络库
WSACleanup();
return -1;
}
//创建套接字。 创建网络类型 tcp或者upd
//SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
if (INVALID_SOCKET == socketServer)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_socket", ret.c_str(), 0);
//清理网络库
WSACleanup();
return -1;
}
set.Seversockall = socketServer;
//设置sockaddr结构
sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = INADDR_ANY;//inet_addr("192.168.31.55");
saServer.sin_port = htons(Sever_Port);
// 绑定本机(服务器)IP和端口
//sockaddr结构中的信息
if (SOCKET_ERROR == bind(socketServer, (SOCKADDR*)&saServer, sizeof(saServer)))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_bind", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*监听本机(服务器)的套接字*/
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
CloseHandle(set.Port);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
//创建一个完成端口
set.Port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
if ( 0 == set.Port)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
//绑定sorket 和 完成端口 参数3是传入参数,发给函数GetQueuedCompletionStatus的参数3
if (0 == CreateIoCompletionPort((HANDLE)socketServer, set.Port, 0, 0))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
CloseHandle(set.Port);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
//创建线程,查询CPU是几核就创建几核
SYSTEM_INFO lpSystemInfo;
GetSystemInfo(&lpSystemInfo);
set.ThreadNum = lpSystemInfo.dwNumberOfProcessors;
set.Pthread = (PHANDLE)malloc(set.ThreadNum * sizeof(HANDLE));
set.ThreadOpen = true;
/*创建线程来用于消息接收*/
for (UINT i = 0; i < set.ThreadNum; i++)
{
set.Pthread[i] = CreateThread(0, 0, PTHREAD_ROUTINE, 0, 0, 0);
}
异步接收客户端
if (0 == PostAccept())
{
clear();
return 0;
}
while (set.ThreadOpen)
{
Sleep(1000);
}
return 1;
}
BOOL WINAPI HANDLER_ROUTINE(_In_ DWORD CtrlType)
{
switch (CtrlType)
{
case CTRL_CLOSE_EVENT:
{
set.ThreadOpen = false;
clear();
}
default:
break;
}
return true;
}
#include <fstream>
#include <string>
class person
{
public:
int a;
string b;
int c;
};
int main()
{
//创建个回调监视控制台关闭
SetConsoleCtrlHandler(HANDLER_ROUTINE,true);
create();
// create("127.0.0.1");
return 0;
}