IOCP九:Client退出后投递WSASend

摘要:
对或connect=GetLastError()){std::cout˂˂“AcceptExFailed:”˂˂GetLastError()˂˂std::endl;returnf

实验过程:

        过程一:
                1.Server等待Client到来
                2.Client进入
                3.Server接受连接,发送"nihaihaoma"
                4.Client接收"nihaihaoma",马上退出
                5.Server进入发送完成处理,Sleep(8000)
                  int ret = WSASend(s,...)再次投递发送请求
        结果:
                WSASend以ret=SOCKET_ERROR, GetLastError()= 10054失败 
                (10054远程主机强迫关闭了一个现有的连接)

        过程二:
                1.Server等待Client到来
                2.Client进入
                3.Server接受连接,发送"nihaihaoma"
                4.Client接收"nihaihaoma",closesocket(s)后马上退出
                5.Server进入发送完成处理,Sleep(8000)
                  int ret = WSASend(s,...)再次投递发送请求
        结果:
                投递成功且发送成功,int ret = WSASend(s,...)第三次投递发送请求
                WSASend以ret=SOCKET_ERROR, GetLastError()= 10053失败 
                (10053您的主机中的软件放弃了一个已建立的连接)


深入探索:
        netstat -ano | findstr "4444" 查看端口状态,显示出异常:
                1.直接退出:连接在Client退出的那刻被暴力关闭,跳过4步握手,不再可用
                2.打声招呼再退出:在发送回调完成之前Client处于FIN_WAIT_2、Server处于CLOSE_WAIT,回调结束连接关闭——>进行了优雅的四步分手

附加:

        使用其他电脑作Client测试结果相同


综上所述:
        1.连接在Client不辞而别的那刻被暴力关闭、跳过4步握手、不再可用——>WSASend投递在一个不可用连接上,失败在所难免
        2.Client调用closesocket(s)再退出,连接在发送回调中仍旧存在、呈Server->Client的单向传输——>WSASend投递在可用连接必定成功
          连接既然可用投递成功的数据发送成功也不足为奇,回调结束后IOCP关闭连接、完成四步分手


实验结果图:

IOCP九:Client退出后投递WSASend第1张

IOCP九:Client退出后投递WSASend第2张

IOCP九:Client退出后投递WSASend第3张


实验代码:

#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <process.h>
#include <string>
#include <MSWSock.h>
#include <set>

#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Kernel32.lib")
#pragma comment(lib, "Mswsock.lib")

#define BUF_LEN 1024

enum OperateType
{
	OP_RECV,
	OP_SEND,
	OP_ACCEPT,
};

typedef struct PER_HANDLE_DATA
{
	SOCKET s;			//记录是哪个socket上的请求
	SOCKADDR_IN addr;	//记录该socket对应的客户端地址和端口
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

typedef struct PER_IO_DATA
{
	OVERLAPPED overlapped;	//第一项必须为OVERLAPPED
	SOCKET cs;				//记录客户端socket
	char buf[BUF_LEN];		//发送:此buf存储待发送数据,接收:此buf存储到来的数据
	int operationType;		//记录完成的请求类型:是接收?是发送? 还是连接?
	int no;
}PER_IO_DATA, *LPPER_IO_DATA;

SOCKET SocketInitBindListen()
{
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	if(INVALID_SOCKET == s)
	{
		std::cout<<"create socket failed : "<<GetLastError()<<std::endl;
		return INVALID_SOCKET;
	}

	SOCKADDR_IN	addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.S_un.S_addr = INADDR_ANY;
	addr.sin_port = htons(4444);

	int ret = bind(s, (sockaddr*)&addr, sizeof(addr));
	if(SOCKET_ERROR == ret)
	{
		std::cout<<"bind failed : "<<GetLastError()<<std::endl;
		return SOCKET_ERROR;
	}

	ret = listen(s, 10);
	if(SOCKET_ERROR == s)
	{
		std::cout<<"listen fail : "<<GetLastError()<<std::endl;
		return SOCKET_ERROR;
	}

	return s;
}

bool PostAccept(SOCKET listenSocket)
{
	SOCKET cs = socket(AF_INET, SOCK_STREAM, 0);
	if(INVALID_SOCKET == cs)
	{
		std::cout<<"Create Socket Failed : "<<GetLastError()<<std::endl;
		return false;
	}

	LPPER_IO_DATA ppiod = new PER_IO_DATA;
	ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
	ppiod->operationType = OP_ACCEPT;
	ppiod->cs = cs;

	DWORD dwRecv;
	int len = sizeof(sockaddr_in) + 16;
	bool ret = AcceptEx(listenSocket, ppiod->cs, ppiod->buf, 0, len, len, &dwRecv, &ppiod->overlapped);
	if(false == ret && ERROR_IO_PENDING != GetLastError())
	{
		std::cout<<"AcceptEx Failed : "<<GetLastError()<<std::endl;
		return false;
	}

	return true;
}

bool PostSend(SOCKET s, const char *buf, int len)
{
	LPPER_IO_DATA ppiod = new PER_IO_DATA;
	ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
	ppiod->operationType = OP_SEND;
	memset(ppiod->buf, 0, BUF_LEN);
	memcpy(ppiod->buf, buf, len);

	WSABUF databuf;
	databuf.buf = ppiod->buf;
	databuf.len = len;

	DWORD dwRecv = 0;
	DWORD dwFlags = 0;
	WSASend(s, &databuf, 1, &dwRecv, dwFlags, &ppiod->overlapped, NULL);

	return true;
}

bool PostRecv(SOCKET s, int n)
{
	LPPER_IO_DATA ppiod = new PER_IO_DATA;
	ZeroMemory(&(ppiod->overlapped), sizeof(OVERLAPPED));
	ppiod->operationType = OP_RECV;
	ppiod->no = n;
	memset(ppiod->buf, 0, BUF_LEN);

	WSABUF databuf;
	databuf.buf = ppiod->buf;
	databuf.len = BUF_LEN;

	DWORD dwRecv = 0;
	DWORD dwFlags = 0;
	int ret = WSARecv(s, &databuf, 1, &dwRecv, &dwFlags, &ppiod->overlapped, NULL);
	if(SOCKET_ERROR == ret && WSA_IO_PENDING != GetLastError())
		return false;

	return true;
}

unsigned int __stdcall Func(void *arg)
{
	SOCKET s = (SOCKET)arg;

	Sleep(3000);

	std::string str = "nihaihaoma";
	PostSend(s, str.c_str(), str.length());

	_endthreadex(0);
	return 0;
}

unsigned int __stdcall ThreadFunc(void *arg)
{
	HANDLE hcp = (HANDLE)arg;
	if(NULL == hcp)
	{
		std::cout<<"thread arg error"<<std::endl;
		return -1;
	}

	DWORD dwNum = 0;
	LPPER_HANDLE_DATA pphd;
	LPPER_IO_DATA ppiod;
	while(true)
	{
		bool ret = GetQueuedCompletionStatus(hcp, &dwNum, (LPDWORD)&pphd, (LPOVERLAPPED*)&ppiod, WSA_INFINITE);

		//线程退出控制,没有释放申请的堆空间,还不完善
		if(-1 == dwNum)
		{
			std::cout<<"Thread Exit"<<std::endl;
			_endthreadex(0);
			return 0;
		}

		int type = ppiod->operationType;

		//连接关闭
		if(dwNum == 0 && type != OP_ACCEPT)
		{
			std::cout<<"The Connection Be Closed: "<<GetLastError()<<std::endl;
			std::cout<<"ret="<<ret<<" operateType="<<ppiod->operationType<<" no="<<ppiod->no<<std::endl;

			closesocket(pphd->s);
			delete pphd;
			delete ppiod;
			continue;
		}

		//其他错误
		if(false == ret)
		{
			std::cout<<"An Error Occurs : "<<GetLastError()<<std::endl;

			closesocket(pphd->s);
			delete pphd;
			delete ppiod;
			continue;
		}

		if(OP_RECV == type)
		{
			//
			std::cout<<"接收完成"<<std::endl;
			std::cout<<"接收端口号 :"<<pphd->s<<std::endl;
			//

			ppiod->buf[dwNum] = '

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇IDA Pro使用(静态分析+动态调试)视频监控系统中的流媒体服务器、直写和全切换三种取流架构方案下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

编程珠玑---读书笔记---堆的实现及堆排序

堆是用来表示元素集合的一种数据结构。与“堆内存”不同。堆的性质,第一:顺序性:任何结点的值都小于或者等于其子结点的值,这意味着最小元素位于根结点。 最大顶堆跟这个相反。第二个性质是形状:一种二叉树,最底层的叶子结点尽可能靠左分布,如果有n个结点,那么所有结点到根的距离不会超过logn。 下面用vector来实现堆: 我们规范的从下标1开始,函数定义如下...

解决 fatal error: Eigen/Core: No such file or directory

确认是否安装了eigen3 sudo apt-get install libeigen3-dev 解决 fatal error: Eigen/Core: No such file or directory 如果已经安装,但当调用 eigen 库时,报错:fatal error: Eigen/Core: No such file or directory...

004.UDP--拼接UDP数据包,构造ip头和udp头通信(使用原始套接字)

一.大致流程: 建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去。 server端收到数据后,打印UDP数据并发送确认消息(yes),client收到yes后将其打印。 二.其中: client端IP:192.168.11.104 端口:8600 ser...

Linux使用PAM锁定多次登陆失败的用户(重置次数)原理后续补充----

  linux上的用户,如果用户连续3次登录失败,就锁定该用户,几分钟后该用户再自动解锁。Linux有一个pam_tally2.so的PAM模块,来限定用户的登录失败次数,如果次数达到设置的阈值,则锁定用户。 1、 vim /etc/pam.d/sshd    (远程ssh)   限制用户远程登录   在#%PAM-1.0的下面,即第二行,添加内容,一定...

linux安装mysql8.0.25

一、安装 准备工作 官网下载链接:https://dev.mysql.com/downloads/mysql/ cd /usr/local # 创建mysql目录 mkdir mysql # 进入目录 cd mysql 将下载之后的包(mysql-8.0.25-linux-glibc2.12-x86_64.tar.xz) 上传到/usr/local/m...

关于序列化:把某个对象序列化成字节流

在网络编程中。一个常常的操作是将本地的数据块转换成字符流并将其发送到远端。远端将这个字符串流恢复成数据库。如果有例如以下一个类CObject。编写两个函数,分别将CObject中的成员变量转换为一个字符流(convert2Stream()函数)。并将字符流的数据又一次恢复到一个CObject对象中(convert2Object()函数): char* c...