个人学习总结笔记 对于这一块也不算很通透,如果大家发现错误,一定要及时告诉我
1.基于重叠IO的网络异步模型
有两个写法,一个是基于完成例程的,一个是基于事件的,用完成例程的复杂而且效率不如完成端口,所以在此只介绍基于事件的。
2.windows下的事件
就是你创建一个事件对象,一般来说初始都是将该事件对象设置为无信号的,然后在该事件对象上绑定上你感兴趣的一个操作,然后调用事件等待函数等待该事件的发生,等待过程中线程是被挂起的(也就是说阻塞在了等待处),不耗费CPU的资源,当感兴趣的操作发生的时候,会将事件设置为有信号,内核会解除等待线程的阻塞,使其继续往下运行。
3.重叠结构
windows下所有的异步操作,都需要基于该重叠结构
前四个字段我们不用进行关心,起码初学者现在用不到,最后一个字段event,就是要绑定的事件。
每一个异步操作,都需要一个重叠结构,而每一个重叠结构都需要绑定一个事件。
当绑定了一个事件后,就需要等待该事件的发生。
4.等待事件发生函数
该函数的第一个参数是等待的事件个数,第二个参数是事件数组,一个线程最多等待的事件个数是64个,等需要监听的事件个数多的时候,需要多开线程,如果用户量达到上万的话,那么就需要很多线程,反而损失了效率,建议用完成端口,如果是就几百个的话,这个就足够用了
第三个参数是是否等待全部事件完成之后,才解除阻塞,填写false,不等待
第四个参数是超时时间,一般不要设置为永久等待,因为永久等待可能会造成死锁,因为如果后续有新的socket进来的话,你的等待事件个数还是上次的,后续的就只能等前面的事件发生时候,才能解除阻塞
第五个参数是完成例程使用的,这里直接设为false
5.事件 重新设为有信号
WSAResetEvent();
当WSAWaitForMultipleEvents函数解除阻塞之后,此时事件是有信号的状态,此时需要将该事件设置为无信号,以便进行下一次的等待。
6.获取等待的结果
第一个参数是等待的那个socket
第二个参数是为socket创建的那个重叠结构
第三个参数返回接受到的数据字节数
第四个参数设为true,等待该函数完成之后再返回
第五个参数是获取该结果的flag
7.异步的接收请求
第一个参数是在哪个socket上进行接收
第二个参数是WSABUF,是一个缓冲区结构体
第三个参数是缓冲区个数
第四个参数是要接收的字节数
第五个标志一般给0
第六个参数是给定一个重叠IO结构
第七个参数是完成端口使用的不关心,给NULL
8.封装重叠结构
根据上述异步接收请求来封装
typedef struct
{
WSAOVERLAPPED overlap; //每一个socket连接需要关联一个WSAOVERLAPPED对象
WSABUF Buffer; //与WSAOVERLAPPED对象绑定的缓冲区
char szMessage[BUF_SIZE]; //初始化buffer的缓冲区
DWORD NumberOfBytesRecvd; //指定接收到的字符的数目
DWORD Flags;
}MY_WSAOVERLAPPED, * LPMY_WSAOVERLAPPED;
9.需要的数组
int g_TotalConnCount = 0; //总共的连接计数
sockaddr_in ClientArr[WSA_MAXIMUM_WAIT_EVENTS]; //客户信息的保存
LPMY_WSAOVERLAPPED g_OverLappedArr[WSA_MAXIMUM_WAIT_EVENTS];//重叠结构数组
HANDLE g_EventArr[WSA_MAXIMUM_WAIT_EVENTS];//事件数组
SOCKET g_SockArr[WSA_MAXIMUM_WAIT_EVENTS];//套接字数组
①一个socket的连接计数,记录当前有多少个客户连接到socket上
②全局客户socket信息数组,记录连接而来的客户信息和端口
③重叠结构数组,每来一个客户socket就需要一个重叠结构
④事件数组,每一个重叠结构就需要一个事件
⑤socket数组,记录socket
就是socket-事件-重叠 三个绑在一起
10服务器实现代码
#include <iostream>
#include <ws2tcpip.h>
#include <windows.h>
using namespace std;
#define MY_PORT 5050
#pragma comment (lib, "ws2_32.lib")
#define BUF_SIZE 255
void Cleanup(int index);
typedef struct
{
WSAOVERLAPPED overlap; //每一个socket连接需要关联一个WSAOVERLAPPED对象
WSABUF Buffer; //与WSAOVERLAPPED对象绑定的缓冲区
char szMessage[BUF_SIZE]; //初始化buffer的缓冲区
DWORD NumberOfBytesRecvd; //指定接收到的字符的数目
DWORD Flags;
}MY_WSAOVERLAPPED, * LPMY_WSAOVERLAPPED;
//重叠IO
//每一个套接字都需要一个重叠结构 每个重叠结构需要绑定一个事件
//事件要加入到事件数组中 因为WSAWaitForMultipleEvents
int g_TotalConnCount = 0; //总共的连接计数
sockaddr_in ClientArr[WSA_MAXIMUM_WAIT_EVENTS]; //客户信息的保存
LPMY_WSAOVERLAPPED g_OverLappedArr[WSA_MAXIMUM_WAIT_EVENTS];//重叠结构数组
HANDLE g_EventArr[WSA_MAXIMUM_WAIT_EVENTS];//事件数组
SOCKET g_SockArr[WSA_MAXIMUM_WAIT_EVENTS];//套接字数组
DWORD WINAPI ThreadProc(LPVOID lpParameter);
int main()
{
SOCKET ListenSock;
WSADATA wd;
if (0 != WSAStartup(MAKEWORD(2, 2), &wd))
{
perror("WSAStartup: ");
exit(-1);
}
if (INVALID_SOCKET == (ListenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))
{
perror("socket: ");
exit(-1);
}
sockaddr_in SockAddr;
SockAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
SockAddr.sin_port = htons(MY_PORT);
SockAddr.sin_family = AF_INET;
if (SOCKET_ERROR == bind(ListenSock,(sockaddr *)&SockAddr, sizeof(sockaddr_in)))
{
perror("bind: ");
exit(-1);
}
if (SOCKET_ERROR == listen(ListenSock, 5))
{
perror("listen: ");
exit(-1);
}
int addrLen = sizeof(sockaddr_in);
//创建工作线程
CreateThread(NULL, 0, ThreadProc, NULL, NULL, NULL);
while (1)
{
char szBuf[30];
SOCKET NewSock =
accept(ListenSock, (sockaddr*)&ClientArr[g_TotalConnCount], &addrLen);
if (INVALID_SOCKET == NewSock)
{
continue;
}
g_SockArr[g_TotalConnCount] = NewSock; //保存套接字
//分配新的overlapped对象
g_OverLappedArr[g_TotalConnCount] =
(LPMY_WSAOVERLAPPED)malloc(sizeof(MY_WSAOVERLAPPED));
g_OverLappedArr[g_TotalConnCount]->Flags = 0;
g_OverLappedArr[g_TotalConnCount]->Buffer.len = BUF_SIZE;
g_OverLappedArr[g_TotalConnCount]->Buffer.buf =
g_OverLappedArr[g_TotalConnCount]->szMessage;
//创建事件对象 将重叠对象 与 事件进行绑定
g_EventArr[g_TotalConnCount] =
g_OverLappedArr[g_TotalConnCount]->overlap.hEvent
= WSACreateEvent();
//将套接字 重叠对象进行绑定 并进行投递
WSARecv(NewSock, &g_OverLappedArr[g_TotalConnCount]->Buffer, 1,
&g_OverLappedArr[g_TotalConnCount]->NumberOfBytesRecvd,
&g_OverLappedArr[g_TotalConnCount]->Flags,
&g_OverLappedArr[g_TotalConnCount]->overlap, NULL);
inet_ntop(AF_INET, &(ClientArr[g_TotalConnCount].sin_addr), szBuf, 30);
cout << "Client IP : " << szBuf <<" Port: "
<<ntohs(ClientArr[g_TotalConnCount].sin_port) << endl;
g_TotalConnCount++;
}
closesocket(ListenSock);
WSACleanup();
return 0;
}
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int ret, Index;
DWORD cbTransferred;
while (1)
{
ret = WSAWaitForMultipleEvents(g_TotalConnCount, g_EventArr, FALSE, 100, FALSE);
if (WSA_WAIT_FAILED == ret || WSA_WAIT_TIMEOUT == ret)
continue;
//获取下标
Index = ret - WSA_WAIT_EVENT_0;
//重新设置 无信号
WSAResetEvent(g_EventArr[Index]);
WSAGetOverlappedResult(g_SockArr[Index], &g_OverLappedArr[Index]->overlap,
&cbTransferred, TRUE, &g_OverLappedArr[Index]->Flags);
if (0 == cbTransferred)
{
Cleanup(Index);//关闭客户端连接
}
else
{
//这里直接就转发回去了
send(g_SockArr[Index], g_OverLappedArr[Index]->szMessage, cbTransferred, 0);
// 进行另一个异步操作
WSARecv(g_SockArr[Index],
&g_OverLappedArr[Index]->Buffer,
1,
&g_OverLappedArr[Index]->NumberOfBytesRecvd,
&g_OverLappedArr[Index]->Flags,
&g_OverLappedArr[Index]->overlap, NULL);
}
}
return 0;
}
void Cleanup(int index)
{
//EnterCriticalSection(&g_Data);
char szBuf[30];
inet_ntop(AF_INET, &(ClientArr[index].sin_addr), szBuf, 30);
cout << "Client IP : " << szBuf << " Port: "
<< ntohs(ClientArr[index].sin_port) << "Exit " << endl;
//进行套接字关闭
closesocket(g_SockArr[index]);
//对事件的对象的关闭
WSACloseEvent(g_EventArr[index]);
free(g_OverLappedArr[index]);
if (index < g_TotalConnCount - 1)
{
g_SockArr[index] = g_SockArr[g_TotalConnCount - 1];
g_EventArr[index] = g_EventArr[g_TotalConnCount - 1];
g_OverLappedArr[index] = g_OverLappedArr[g_TotalConnCount - 1];
}
g_OverLappedArr[--g_TotalConnCount] = NULL;
}
11 客户端实现代码
#include <iostream>
#include <windows.h>
using namespace std;
#define MY_PORT 5050
#pragma comment (lib, "ws2_32.lib")
int main()
{
WSADATA wd;
WSAStartup(MAKEWORD(2, 2), &wd);
SOCKET ConnectSock;
ConnectSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == ConnectSock)
{
perror("socket: ");
exit(-1);
}
sockaddr_in SockAddr;
SockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = htons(MY_PORT);
if (SOCKET_ERROR == connect(ConnectSock, (sockaddr*)&SockAddr, sizeof(SockAddr)))
{
perror("connect: ");
exit(-1);
}
while (1)
{
char szBuf[255];
cin >> szBuf;
send(ConnectSock, szBuf, strlen(szBuf)+1, 0);
//recv(ConnectSock, szBuf, 255, 0);
//cout << szBuf << endl;
}
WSACleanup();
return 0;
}