#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];
};
#include "main.h"
#define WSA_MAX_STR 1024
bool PostRecv(UINT Index);
bool PostSend(UINT Index);
fd_esIo_set allsock;
char _recvstr[WSA_MAX_STR]; //recv消息用的
VOID Server_write_error()
{
}
void clear()
{
/*释放整个结构体,可能有些事件和socket已经被释放过了,不影响*/
for (UINT i = 0; i < allsock.count; i++)
{
//释放stocket
closesocket(allsock.sockall[i]);
//关闭事件对象
WSACloseEvent(allsock.IOeventall[i].hEvent);
}
}
//重叠IO完成回调
void CALLBACK RecvCall(
DWORD dwError, //错误码
DWORD cbTransferred, //接收到的字节,为0代表客户端退出
LPWSAOVERLAPPED lpOverlapped, //接收重叠结构
DWORD dwFlags //函数执行方式
)
{
//获取当前socket和事件//得到当前数组下标
UINT i = lpOverlapped - allsock.IOeventall ;
if (WSAECONNRESET == dwError || 0 == cbTransferred)
{
printf("客户端下线\n");
//释放stocket
closesocket(allsock.sockall[i]);
//关闭事件对象
WSACloseEvent(allsock.IOeventall[i].hEvent);
//从数组中删掉
allsock.sockall[i] = allsock.sockall[allsock.count - 1];//数组从0开始,-1才是正确位置
allsock.IOeventall[i] = allsock.IOeventall[allsock.count - 1];//数组从0开始,-1才是正确位置
allsock.count--;
}
else
{//接收到了数据
printf("通信中:recv:%s\n", _recvstr);
memset(_recvstr, 0, WSA_MAX_STR);
// PostSend(i);
//继续接收当前客户端消息
PostRecv(i);
}
}
//重叠IO完成回调
void CALLBACK SendCall(
DWORD dwError, //错误码
DWORD cbTransferred, //接收到的字节,为0代表客户端退出
LPWSAOVERLAPPED lpOverlapped, //重叠结构
DWORD dwFlags //函数执行方式
)
{
printf("通信中:sendover\n");
}
bool PostSend(UINT Index)
{
string sendmsg = "服务器发送";
WSABUF lpBuffers = { strlen(sendmsg.c_str()) ,(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重叠结构
SendCall //IO重叠回调例程
);
if (0 == ret)
{//立即完成
// printf("send:立即完成\n");
return 1;
}
else
{
int error = WSAGetLastError();
if (ERROR_IO_PENDING == error)
{//等待客户端链接中 延迟处理
return 1;
}
return 0;
}
}
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重叠结构
RecvCall //IO重叠回调例程
);
if (0 == ret)
{//立即完成
printf("PostRecv立即完成:%s\n", lpBuffers.buf);
memset(_recvstr, 0, WSA_MAX_STR);
//继续接收当前客户端消息
PostRecv(Index);
return 1;
}
else
{
int error = WSAGetLastError();
if (ERROR_IO_PENDING == error)
{//等待客户端链接中 延迟处理 ,在主函数循环里
return 1;
}
return 0;
}
}
bool PostAccept()
{
while (1)
{
/*创建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] //服务器的重叠结构
);
if (true == res)
{//立即连接了
//投递recv收信息
PostRecv(allsock.count);
//根据情况投递send
// PostSend(allsock.count);
//链接成功后客户端数组++
allsock.count++;
//再次投递一个PostAccept 递归 我们这用循环替代
// PostAccept();
continue;
}
else
{
int error = WSAGetLastError();
if (ERROR_IO_PENDING == error)
{//等待客户端链接中 延迟处理
return 1;
}
return 0;
}
}
}
/*1.打开网络库
* 2.校验网络库版本
* 3.创建SOCKET
* 4.绑定IP地址和端口
* 5.开始监听
* 6.创建客户端socket/接受链接
* 7.与客户端收发消息
* 8.(6.7)两步的函数accept,send,recv 有堵塞,可以用select解决,这种函数可以处理小型网络
*/
int create(const char* IpAdress)
{
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;
}
/*监听本机(服务器)的套接字*/
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*将服务器socket和事件对象句柄写进结构体*/
allsock.IOeventall[allsock.count].hEvent = WSACreateEvent();;
allsock.sockall[allsock.count] = socketServer;
allsock.count++;
初始化数据
if (0 == PostAccept())
{
clear();
//清理网络库
WSACleanup();
return 0;
}
while (true)
{
{
/*等待事件:有事件产生后返回对应事件的下标*/ //参数6true 将等待事件函数和完成例程机制结合
DWORD RET = WSAWaitForMultipleEvents(1, &allsock.IOeventall[0].hEvent, 0, WSA_INFINITE, true);
/*返回错误*/ /*等待超时检测*/
if (WSA_WAIT_FAILED == RET || WSA_WAIT_IO_COMPLETION == RET)
{
/* string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_WSAWaitForMultipleEvents", ret.c_str(), 0);*/
continue;
}
/*WSAGetOverlappedResult函数没有清空信号功能,故要自己清空*/
WSAResetEvent(allsock.IOeventall[0].hEvent);
{//服务器链接
printf("服务器链接成功\n");
//投递recv收信息
PostRecv(allsock.count);
//根据情况投递send
// PostSend(allsock.count);
//链接成功后客户端数组++
allsock.count++;
//再次投递一个PostAccept 递归
PostAccept();
}
}
}
clear();
//清理网络库
WSACleanup();
return 1;
}
int main()
{
create("150.158.92.28");
// create("127.0.0.1");
return 0;
}
重叠IO事件:还是存在无序的问题,死循环挨个访问事件是否有触发(无用功很多浪费性能),如果事件过多,还需要开多线程优化访问,代码繁杂文章来源:https://www.toymoban.com/news/detail-675914.html
重叠IO回调:不存在无序问题,不存在无用功,但是同样也存在消耗大量线程的问题(高并发时每个客户端都要开启一个回调线程,线程占用比事件还多)文章来源地址https://www.toymoban.com/news/detail-675914.html
到了这里,关于TCP-重叠IO-回调机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!