如何产生ioexception_结合实例论述控制过程

如何产生ioexception_结合实例论述控制过程IOCP结合AcceptEx实例

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

在普通IOCP的基础上注意两点:
1.记得把监听socket绑定到端口
2.在Accept处理过程中,抛出接受连接的AcceptEx请求,绑定客户端socket到端口和抛出recv请求

客户端要断开连接时,只需发送一个大小为0的内容即可。我们在服务器处理时,收到0,就销毁该socket

// IOCP_TCPIP_Socket_Server.cpp
#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#include <iostream>
#include <mswsock.h>
using namespace std;
#pragma comment(lib, "Ws2_32.lib") // Socket编程需用的动态链接库
#pragma comment(lib, "Kernel32.lib") // IOCP需要用到的动态链接库
#define SEND 0
#define RECV 1
#define ACCEPT 2
/** * 结构体名称:PER_IO_DATA * 结构体功能:重叠I/O需要用到的结构体,临时记录IO数据 **/
const int DataBuffSize = 2 * 1024;
typedef struct
{
OVERLAPPED overlapped;
WSABUF databuff;
char buffer[DataBuffSize];
int BufferLen;
int operationType;
SOCKET client;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;
/** * 结构体名称:PER_HANDLE_DATA * 结构体存储:记录单个套接字的数据,包括了套接字的变量及套接字的对应的客户端的地址。 * 结构体作用:当服务器连接上客户端时,信息存储到该结构体中,知道客户端的地址以便于回访。 **/
typedef struct
{
SOCKET socket;
SOCKADDR_STORAGE ClientAddr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;
// 定义全局变量
const int DefaultPort = 5000;
vector < PER_HANDLE_DATA* > clientGroup;        // 记录客户端的向量组
int g_nThread = 0;//开启线程数量
HANDLE hThread[50];//线程句柄
SOCKET srvSocket = NULL;
DWORD dwBytes = 0;
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);
DWORD WINAPI ServerSendThread(LPVOID IpParam);
LPFN_ACCEPTEX lpfnAcceptEx = NULL;//AcceptEx函数指针
GUID guidAcceptEx = WSAID_ACCEPTEX;
GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockAddrs = NULL;
// 开始主函数
int main()
{
// 加载socket动态链接库
WORD wVersionRequested = MAKEWORD(2, 2); // 请求2.2版本的WinSock库
WSADATA wsaData;    // 接收Windows Socket的结构信息
DWORD err = WSAStartup(wVersionRequested, &wsaData);
if (0 != err) { // 检查套接字库是否申请成功
cerr << "Request Windows Socket Library Error!\n";
system("pause");
return -1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
// 检查是否申请了所需版本的套接字库
WSACleanup();
cerr << "Request Windows Socket Version 2.2 Error!\n";
system("pause");
return -1;
}
// 创建IOCP的内核对象
/** * 需要用到的函数的原型: * HANDLE WINAPI CreateIoCompletionPort( * __in HANDLE FileHandle, // 已经打开的文件句柄或者空句柄,一般是客户端的句柄 * __in HANDLE ExistingCompletionPort, // 已经存在的IOCP句柄 * __in ULONG_PTR CompletionKey, // 完成键,包含了指定I/O完成包的指定文件 * __in DWORD NumberOfConcurrentThreads // 真正并发同时执行最大线程数,一般推介是CPU核心数*2 * ); **/
HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (NULL == completionPort) {   // 创建IO内核对象失败
cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;
system("pause");
return -1;
}
// 创建IOCP线程--线程里面创建线程池
// 确定处理器的核心数量
SYSTEM_INFO mySysInfo;
GetSystemInfo(&mySysInfo);
// 基于处理器的核心数量创建线程
for (DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); ++i) {
// 创建服务器工作器线程,并将完成端口传递到该线程
HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);//第一NULL代表默认安全选项,第一个0,代表线程占用资源大小,第二个0,代表线程创建后立即执行
if (NULL == ThreadHandle) {
cerr << "Create Thread Handle failed. Error:" << GetLastError() << endl;
system("pause");
return -1;
}
hThread[i] = ThreadHandle;
++g_nThread;
}
// 建立流式套接字
srvSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
// Associate SOCKET with IOCP 
if (NULL == CreateIoCompletionPort((HANDLE)srvSocket, completionPort, NULL, 0))
{
cout << "CreateIoCompletionPort failed with error code: " << WSAGetLastError() << endl;
if (INVALID_SOCKET != srvSocket)
{
closesocket(srvSocket);
srvSocket = INVALID_SOCKET;
}
return -1;
}
// 绑定SOCKET到本机
SOCKADDR_IN srvAddr;
srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(DefaultPort);
int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));
if (SOCKET_ERROR == bindResult) {
cerr << "Bind failed. Error:" << GetLastError() << endl;
system("pause");
return -1;
}
// 将SOCKET设置为监听模式
int listenResult = listen(srvSocket, 10);
if (SOCKET_ERROR == listenResult) {
cerr << "Listen failed. Error: " << GetLastError() << endl;
system("pause");
return -1;
}
// 开始处理IO数据
cout << "本服务器已准备就绪,正在等待客户端的接入...\n";
 创建用于发送数据的线程
//HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);//第二个0,代表回掉函数参数为0
for (int i = 0; i < 10; ++i)
{
PER_HANDLE_DATA * PerHandleData = NULL;
SOCKET acceptSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
if (INVALID_SOCKET == acceptSocket)
{
cerr << "WSASocket failed with error code: %d/n" << WSAGetLastError() << endl;
return FALSE;
}
// 开始在接受套接字上处理I/O使用重叠I/O机制
// 在新建的套接字上投递一个或多个异步
// WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务 
// 单I/O操作数据(I/O重叠)
LPPER_IO_OPERATION_DATA PerIoData = NULL;
PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));
ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED));
PerIoData->databuff.len = 1024;
PerIoData->databuff.buf = PerIoData->buffer;
PerIoData->operationType = ACCEPT;  // read
PerIoData->client = acceptSocket;
if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof(guidAcceptEx), &lpfnAcceptEx,
sizeof(lpfnAcceptEx), &dwBytes, NULL, NULL))
{
cerr << "WSAIoctl failed with error code: " << WSAGetLastError() << endl;
if (INVALID_SOCKET != srvSocket)
{
closesocket(srvSocket);
srvSocket = INVALID_SOCKET;
}
//goto EXIT_CODE;
return -1;
}
if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs,
sizeof(GuidGetAcceptExSockAddrs), &lpfnGetAcceptExSockAddrs, sizeof(lpfnGetAcceptExSockAddrs),
&dwBytes, NULL, NULL))
{
cerr << "WSAIoctl failed with error code: " << WSAGetLastError() << endl;
if (INVALID_SOCKET != srvSocket)
{
closesocket(srvSocket);
srvSocket = INVALID_SOCKET;
}
//goto EXIT_CODE;
return -1;
}
if (FALSE == lpfnAcceptEx(srvSocket, PerIoData->client, PerIoData->databuff.buf, PerIoData->databuff.len - ((sizeof(SOCKADDR_IN) + 16) * 2),
sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, &(PerIoData->overlapped)))
{
if (WSA_IO_PENDING != WSAGetLastError())
{
cerr << "lpfnAcceptEx failed with error code: " << WSAGetLastError() << endl;
return FALSE;
}
}
}
Sleep(1000 * 60 * 60);
PostQueuedCompletionStatus(completionPort, 0, NULL, NULL);
WaitForMultipleObjects(g_nThread, hThread, TRUE, INFINITE);
WSACleanup();
system("pause");
return 0;
}
// 开始服务工作线程函数
DWORD WINAPI ServerWorkThread(LPVOID IpParam)
{
HANDLE CompletionPort = (HANDLE)IpParam;
DWORD BytesTransferred;
LPOVERLAPPED IpOverlapped;
LPPER_HANDLE_DATA PerHandleData = NULL;
LPPER_IO_DATA PerIoData = NULL;
DWORD RecvBytes = 0;
DWORD Flags = 0;
BOOL bRet = false;
while (true) {
bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&IpOverlapped, INFINITE);
if (bRet == 0) {
if (WAIT_TIMEOUT == GetLastError())
{
continue;
}
// Error
cout << "GetQueuedCompletionStatus failed with error:" << GetLastError() << endl;
continue;
}
PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);
//这个宏的作用是:根据一个结构体实例中的成员的地址,取到整个结构体实例的地址
//PER_IO_DATA的成员overlapped的地址为&IpOverlapped,结果就可以获得PER_IO_DATA的地址
if (NULL == PerIoData)
{
// Exit thread 
break;
}
// 检查在套接字上是否有错误发生
if (0 == BytesTransferred && (PerIoData->operationType == RECV || PerIoData->operationType == SEND))
{
closesocket(PerHandleData->socket);
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
}
switch (PerIoData->operationType)
{
case ACCEPT:
{
SOCKADDR_IN* remote = NULL;
SOCKADDR_IN* local = NULL;
int remoteLen = sizeof(SOCKADDR_IN);
int localLen = sizeof(SOCKADDR_IN);
lpfnGetAcceptExSockAddrs(PerIoData->databuff.buf, PerIoData->databuff.len - ((sizeof(SOCKADDR_IN) + 16) * 2),
sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&local, &localLen, (LPSOCKADDR*)&remote, &remoteLen);
//使用GetAcceptExSockaddrs函数 获得具体的各个地址参数.
if (setsockopt(PerIoData->client, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
(char*)&(PerHandleData->socket), sizeof(PerHandleData->socket)) == SOCKET_ERROR)
cout << "setsockopt..." << endl;
// 创建用来和套接字关联的单句柄数据信息结构
PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));  // 在堆中为这个PerHandleData申请指定大小的内存
PerHandleData->socket = PerIoData->client;
//memcpy(&(perHandleData->clientAddr),raddr,sizeof(raddr));
//将新的客户套接字与完成端口连接
CreateIoCompletionPort((HANDLE)PerHandleData->socket,
CompletionPort, (ULONG_PTR)PerHandleData, 0);
memset(&(PerIoData->overlapped), 0, sizeof(OVERLAPPED));
PerIoData->operationType = RECV;        //将状态设置成接收
//设置WSABUF结构
PerIoData->databuff.buf = PerIoData->buffer;
PerIoData->databuff.len = PerIoData->BufferLen = 1024;
cout << "wait for data arrive(Accept)..." << endl;
Flags = 0;
if (WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1,
&RecvBytes, &Flags, &(PerIoData->overlapped), NULL) == SOCKET_ERROR)
if (WSAGetLastError() == WSA_IO_PENDING)
cout << "WSARecv Pending..." << endl;
continue;
}
break;
case RECV:
// 开始数据处理,接收来自客户端的数据
//WaitForSingleObject(hMutex, INFINITE);
cout << "A Client says: " << PerIoData->databuff.buf << endl;
//ReleaseMutex(hMutex);
// 为下一个重叠调用建立单I/O操作数据
ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空内存
PerIoData->databuff.len = 1024;
PerIoData->databuff.buf = PerIoData->buffer;//buf是个指针,这一过程会清空buffer的内容
PerIoData->operationType = RECV;    // read
WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);
continue;
break;
default:
break;
}
}
return 0;
}
// 发送信息的线程执行函数
DWORD WINAPI ServerSendThread(LPVOID IpParam)
{
while (1) {
if (clientGroup.empty())
{
Sleep(5000);
continue;
}
char talk[200];
cin.get(talk, 200);
int len;
for (len = 0; talk[len] != '\0'; ++len) {
// 找出这个字符组的长度
}
talk[len] = '\n';
talk[++len] = '\0';
printf("I Say:");
cout << talk;
//WaitForSingleObject(hMutex, INFINITE);
for (unsigned i = 0; i < clientGroup.size(); ++i) {
send(clientGroup[i]->socket, talk, 200, 0); // 发送信息
}
//ReleaseMutex(hMutex);
}
return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/187823.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • ajax跨域解决方案_java如何解决跨域问题

    ajax跨域解决方案_java如何解决跨域问题答案:只需要点击标题下面的蓝色字【web前端开发】关注即可。前言从刚接触前端开发起,跨域这个词就一直以很高的频率在身边重复出现,一直到现在,已经调试过N个跨域相关的问题了,16年时也整理过一篇相关文章,但是感觉还是差了点什么,于是现在重新梳理了一下。个人见识有限,如有差错,请多多见谅,欢迎提出issue,另外看到这个标题,请勿喷~题纲关于跨域,有N种类型,本文只专注于ajax请求跨域(ajax跨域只是属于浏览器”同源策略”中的一部分,其它的还有Cookie跨域iframe跨域,Loc

  • linux关闭防火墙或开启防火墙命令_linux重启防火墙命令

    linux关闭防火墙或开启防火墙命令_linux重启防火墙命令Linux还是比较常用的,防火墙的关闭和开启可以通过命令来实现,下面由学习啦小编为大家整理了linux中关闭防火墙的命令,希望对大家有帮助!linux中关闭防火墙1)永久性生效,重启后不会复原开启:chkconfigiptableson关闭:chkconfigiptablesoff2)即时生效,重启后复原开启:serviceiptablesstart关闭:servicei…

  • lua中的weak table及内存回收collectgarbage

    弱表(weaktable)是一个很有意思的东西,像C++/Java等语言是没有的。弱表的定义是:Aweaktableisatablewhoseelementsareweakreferences,元素为弱引用的表就叫弱表。有弱引用那么也就有强引用,有引用那么也就有非引用。我们先要厘这些基本概念:变量、值、类型、对象。(1)变量与值:Lua是一个dynamicallyty

  • Java集合之WeakHashMap[通俗易懂]

    Java集合之WeakHashMap[通俗易懂]WeakHashMap继承于AbstractMap,同时实现了Map接口。和HashMap一样,WeakHashMap也是一个散列表,存储的内容也是键值对key-value映射,并且键和值都可以是null。WeakHashMap的键都是弱键,给定一个键,其映射的存在并不阻止垃圾回收器对该键的丢弃,使该键成为可终止,然后被回收。弱键的原理就是Entry继承了WeakReference接口,当G

  • Angularjs 服务

    Angularjs 服务AngularJS服务(Service):www.runoob.com/angularjs/angularjs-services.htmlAngularJS内建了30多个服务。官网搜索service吧https://docs.angularjs.org/api/ng/service/$window$http是AngularJS应用中最常用的服务。服务向服务器发送请求,应用响应服…

  • 1082. 数字游戏(数位dp)[通俗易懂]

    1082. 数字游戏(数位dp)[通俗易懂]科协里最近很流行数字游戏。某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 123,446。现在大家决定玩一个游戏,指定一个整数闭区间 [a,b],问这个区间内有多少个不降数。输入格式输入包含多组测试数据。每组数据占一行,包含两个整数 a 和 b。输出格式每行给出一组测试数据的答案,即 [a,b] 之间有多少不降数。数据范围1≤a≤b≤231−1输入样例:1 91 19输出样例:918#include<bits/stdc++.h>usin

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号