⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dpsocketmodel.cpp

📁 DarkATLSmtp(SMTP COM 组件原创代码),注册后可在Delphi中发邮件。
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//发送完所有数据或超时
//入口:套接字,缓冲区,缓冲区大小,超时
//出口:返回已经发送的字节个数或SOCKET_ERROR
int CDPSocketModel::DPSendData_Event(SOCKET hSocket, char const * pszBuffer, 
				 int nBufferSize, DWORD dwTimeout)
{

	int nRtxBytes	= 0;
	int nRtxCurrent = 0;

	while (nRtxBytes < nBufferSize)
	{
		nRtxCurrent = DPSend_Event(hSocket, (pszBuffer + nRtxBytes),
			(nBufferSize - nRtxBytes), dwTimeout);
		if (nRtxCurrent < 0)
			return (nRtxBytes);
		nRtxBytes += nRtxCurrent;
	}
	return (nRtxBytes);
}

//一次发送数据,但不一定全部都发送
//入口:套接字,接收方地址信息,地址结构,结构长度,缓冲区,缓冲区长度,超时
//出口:正确返回发送字节数量,错误返回SOCKET_ERROR
int CDPSocketModel::DPSendTo_Block(SOCKET hSocket, const struct sockaddr * pTo,
		int nAddrLen,char const * pszBuffer, int nBufferSize, DWORD dwTimeout)
{
	if(hSocket==NULL||pszBuffer==NULL)
		return SOCKET_ERROR;
	FD_SET fd = {1, hSocket};
	TIMEVAL tv = {dwTimeout, 0};
	int nBytesSent=0;
	if(select(0, NULL, &fd, NULL, &tv) == 0) 
		goto CLEAR;
	nBytesSent = sendto(hSocket, pszBuffer, nBufferSize, 0, pTo, nAddrLen);
	if(nBytesSent == SOCKET_ERROR)
		goto CLEAR;
	return nBytesSent;

CLEAR:
	DPSetLastError(WSAGetLastError());//超时
	return(SOCKET_ERROR);
}

// 数据报发送数据报
int CDPSocketModel::DPSendTo_Event(SOCKET hSocket, const struct sockaddr * pTo,
								 int nAddrLen,char const * pszBuffer, 
								 int nBufferSize, DWORD dwTimeout)
{
	HANDLE hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (hWriteEvent == NULL)
	{
		DPSetLastError( (int)GetLastError() );
		return (SOCKET_ERROR);
	}

	DWORD	dwRtxBytes = 0,
            dwRtxFlags = 0;
	WSABUF	WSABuff;

	ZeroMemory(&WSABuff,sizeof(WSABUF));
	WSABuff.len = nBufferSize;
	WSABuff.buf = (char *) pszBuffer;

	for (;;)
	{
		if (WSASendTo( hSocket, &WSABuff, 1, &dwRtxBytes, dwRtxFlags,
			pTo, nAddrLen, NULL, NULL) == SOCKET_SUCCESS)
			break;

		if (WSAGetLastError() != WSAEWOULDBLOCK)
		{
			CloseHandle(hWriteEvent);
			DPSetLastError(  WSAGetLastError() );
			return (SOCKET_ERROR);
		}

		//////////////////////////////////////////////////////////////////////////
		//	睡眠一段时间
		/////////////////////////////////////////////////////////////////////////
		Sleep(DP_BLOCKED_SNDRCV_SLEEP);

		// 注册FD_WRITE事件  
		if( WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, FD_WRITE) 
			== SOCKET_ERROR)
		{
			CloseHandle(hWriteEvent);
			DPSetLastError( WSAGetLastError() );
			return (SOCKET_ERROR);
		}
		DWORD dwWaitResult = WSAWaitForMultipleEvents
			(1, &hWriteEvent, TRUE,dwTimeout, TRUE);
		
		if( dwWaitResult != WSA_WAIT_EVENT_0 )
		{
			// 注销事件
			WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
			CloseHandle(hWriteEvent);
			DPSetLastError(  WSAGetLastError() );
			return (SOCKET_ERROR);
		}

		////////////////////////////////////////////////////////////// 
		///	注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
		///			进一步检查网络是否发生错误
		///////////////////////////////////////////////////////////////
		WSANETWORKEVENTS NetEvent;
		if(WSAEnumNetworkEvents(hSocket,(WSAEVENT)hWriteEvent,&NetEvent) 
			== SOCKET_ERROR)
		{
			// 注销事件
			WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
			CloseHandle(hWriteEvent);
			DPSetLastError(  WSAGetLastError() );
			return (SOCKET_ERROR);
		}
		if(NetEvent.iErrorCode[FD_WRITE_BIT] !=0 )	// 发生错误
		{
			// 注销事件
			WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
			CloseHandle(hWriteEvent);
			DPSetLastError(NetEvent.iErrorCode[FD_WRITE_BIT]);
			return (SOCKET_ERROR);
		}
		////////////////////////////////////////////////////////////////
		// 注销事件
		WSAEventSelect(hSocket, (WSAEVENT) hWriteEvent, 0);
	}

	CloseHandle(hWriteEvent);
	return ((int) dwRtxBytes);
}



//一次性发送数据(事件IO)
//入口:套接字,缓冲区,缓冲区长度
//出口:正确-返回发送的字节数量,错误-返回错误代码的负数
int CDPSocketModel::DPSendLL(SOCKET hSocket, char const * pszBuffer, 
							 int nBufferSize)
{
	DWORD	dwRtxBytes = 0;
	WSABUF	WSABuff;

	ZeroMemory(&WSABuff,sizeof(WSABUF));
	WSABuff.len = nBufferSize;
	WSABuff.buf = (char *) pszBuffer;

	return ((WSASend(hSocket, &WSABuff, 1, &dwRtxBytes, 0,NULL, NULL)
		== SOCKET_SUCCESS) ? (int) dwRtxBytes : -WSAGetLastError());
}

//关闭套接字
//入口:套接字,是否强行关闭(如果bHardClose==FALSE,那么接收剩余的数据后关闭连接)
//注意:这里的接收剩余数据只是为了让SERVER将数据发送操作执行完毕
//     所以接收的剩余数据不可用
void CDPSocketModel::DPCloseSocket(SOCKET hSocket, BOOL bHardClose)
{
	// 不需要捕获错误
	if (!bHardClose) // 优雅关闭 Graceful close
	{
		// 不再发送数据,对于TCP套接字,在所有的数据都发送完毕之后,
		// 将发送一个 FIN ,通知接收方所有数据已经发送完毕。
		shutdown(hSocket, SD_SEND);

		// 接收缓冲区有可能还有未接收的数据,在关闭套接字之前应该先
		// 读取残留的数据。
		int		nRecvResult;
		HANDLE	hSocketEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		//为残留数据提供的缓冲区
		char	szBuffer[256];
		do
		{
			if (hSocketEvent != NULL)
			{
				//注册网络事件
				WSAEventSelect(hSocket, (WSAEVENT) hSocketEvent, FD_READ | FD_CLOSE);
				WSAWaitForMultipleEvents(1, &hSocketEvent, TRUE,DP_SHUTDOWN_RECV_TIMEOUT, TRUE);
				//清除网络事件
				WSAEventSelect(hSocket, (WSAEVENT) hSocketEvent, 0);
			}
			ZeroMemory(szBuffer,256);
			//接收残留数据
			nRecvResult = DPRecvLL(hSocket, szBuffer, sizeof(szBuffer));
		} while (nRecvResult > 0);

		if (hSocketEvent != NULL)
			CloseHandle(hSocketEvent);
		//不再允许接收和发送
		shutdown(hSocket, SD_BOTH);
	}
	// 关闭套接字
	closesocket(hSocket);
}

//接受套接字连接
//入口:侦听端口号,本地地址
//出口:返回一个可用的套接字或INVALID_SOCKET
//注意:如果为阻塞等待,就是在DWTIMEOUT的时间设置为无限的时候
//     如果我们关闭该套接字会出现的问题
SOCKET CDPSocketModel::DPAccept_Event(SOCKET hSocket, struct sockaddr * pSocketAddress, 
								   int *nAddrLen,DWORD dwTimeout
								   /*= DP_DEFAULT_TIMEOUT*/)
{
	HANDLE hAcceptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (hAcceptEvent == NULL)
	{
		DPSetLastError( (int)GetLastError() );
		return (INVALID_SOCKET);
	}

	//异步事件选择
	// 注册FD_ACCEPT事件
	if( WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, FD_ACCEPT) == SOCKET_ERROR)
	{
		CloseHandle(hAcceptEvent);
		DPSetLastError( WSAGetLastError() );
		return (INVALID_SOCKET);
	}

	//进行非阻塞接收
	SOCKET hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
	int	nConnectError = WSAGetLastError();
	//如果返回的端口是没有定义的,并且接收的错误码为"代决"状态
	if ((hSocketAccept == INVALID_SOCKET) && (nConnectError == WSAEWOULDBLOCK))
	{

		//阻塞,这个函数如果正确返回那么我们在使用WSAACCEPT重新接收一次,
		//就可以获得用户的SOCKET
		//因为这个函数返回代表ACCPETEVENT成为传信状态,如果网络没有错误那么
		//代表有用户的正确端口接入
		DWORD dwWaitResult = WSAWaitForMultipleEvents
			(1,&hAcceptEvent,TRUE,dwTimeout,TRUE);
		if (dwWaitResult == WSA_WAIT_EVENT_0)
		{
			////////////////////////////////////////////////////////////// 
			///	注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
			///			进一步检查网络是否发生错误
			///////////////////////////////////////////////////////////////
			WSANETWORKEVENTS NetEvent;
			if(WSAEnumNetworkEvents(hSocket,hAcceptEvent,&NetEvent) == SOCKET_ERROR)
				DPSetLastError( WSAGetLastError() );
			else if(NetEvent.iErrorCode[FD_ACCEPT_BIT] !=0 )	// 发生错误
				DPSetLastError( NetEvent.iErrorCode[FD_ACCEPT_BIT] );
			else
				//接收到真正的用户SOCKET
				hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
		}
		else
			DPSetLastError(WSAGetLastError());
	}
	
	// 注销网络事件
	WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, 0);
	CloseHandle(hAcceptEvent);

	if (hSocketAccept != INVALID_SOCKET)
	{
		// 设置套接字的属性为地址可重用并且为非阻塞的
		if ((DPBlockSocket(hSocketAccept, 0)  == SOCKET_ERROR ) ||
				(DPSetSocketOption(hSocketAccept) == SOCKET_ERROR ) )
		{
			DPCloseSocket(hSocketAccept,TRUE);
			return (INVALID_SOCKET);
		}
	}
	return (hSocketAccept);
}

// 接受套接字连接(允许中断)
//入口:结束事件,其他入口都和Accept一样
//出口:返回一个可用的套接字或INVALID_SOCKET
//注意:超时值默认为DP_DEFAULT_TIMEOUT,如果结束事件没有的话,
//     那么跟调用上面的函数没有区别
SOCKET CDPSocketModel::DPAcceptEx_Event(SOCKET hSocket, struct sockaddr * pSocketAddress, 
									  int *nAddrLen,HANDLE hEndEvent,DWORD dwTimeout 
									  /*= DP_DEFAULT_TIMEOUT*/)
{
	if( hEndEvent == NULL)
		return DPAccept_Event(hSocket,pSocketAddress,nAddrLen,dwTimeout);

	HANDLE	hAcceptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (hAcceptEvent == NULL)
	{
		DPSetLastError( (int)GetLastError() );
		return (INVALID_SOCKET);
	}

	WSAEVENT hEvent[2]; 
	hEvent[0] = (WSAEVENT)hAcceptEvent;
	hEvent[1] = (WSAEVENT)hEndEvent;

	// 注册FD_ACCEPT事件
	if( WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, FD_ACCEPT) == SOCKET_ERROR)
	{
		CloseHandle(hAcceptEvent);
		DPSetLastError( WSAGetLastError() );
		return (INVALID_SOCKET);
	}

	SOCKET hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
	int	   nConnectError = WSAGetLastError();

	if ((hSocketAccept == INVALID_SOCKET) && (nConnectError == WSAEWOULDBLOCK))
	{
		// 阻塞,等待两个事件,如果其中一个发生那么我们向下继续执行
		//所以这意味着指定结束事件后,如果程序想中断ACCEPT的执行
		//那么就激活该结束事件,程序将按照错误一样的处理
		DWORD dwWaitResult = WSAWaitForMultipleEvents(2, hEvent, FALSE,dwTimeout, TRUE);
		if (dwWaitResult == WSA_WAIT_EVENT_0)
		{
			////////////////////////////////////////////////////////////// 
			///	注意:即使 dwWaitResult == WSA_WAIT_EVENT0 ,也应该 
			///			进一步检查网络是否发生错误
			///////////////////////////////////////////////////////////////
			WSANETWORKEVENTS NetEvent;
			if(WSAEnumNetworkEvents(hSocket,hAcceptEvent,&NetEvent) == SOCKET_ERROR)
				DPSetLastError(WSAGetLastError());
			else if(NetEvent.iErrorCode[FD_ACCEPT_BIT] !=0 )	// 发生错误
				DPSetLastError( NetEvent.iErrorCode[FD_ACCEPT_BIT] );
			else
				hSocketAccept = WSAAccept(hSocket, pSocketAddress, nAddrLen, NULL, 0);
		}
		else
			DPSetLastError( WSAGetLastError() );
	}
	
	// 注销网络事件
	WSAEventSelect(hSocket, (WSAEVENT) hAcceptEvent, 0);
	CloseHandle(hAcceptEvent);

	if (hSocketAccept != INVALID_SOCKET)
	{
		// 设置套接字的属性为地址可重用并且为非阻塞的
		if ((DPBlockSocket(hSocketAccept, 0) < 0) ||
				(DPSetSocketOption(hSocketAccept) < 0) )
		{
			DPCloseSocket(hSocketAccept,TRUE);
			return (INVALID_SOCKET);
		}
	}
	return (hSocketAccept);
}

//阻塞ACCEPT,没有响应不返回
//入口:套接字,主机地址,长度
//出口:正确返回端口号,否则返回INVALID_SOCKET
SOCKET CDPSocketModel::DPAccept_Block(SOCKET hSocket, struct sockaddr * pSocketAddress,
									int *nAddrLen)
{
	ASSERT(hSocket!=NULL);
	//int nLengthAddr = sizeof(SOCKADDR);
	SOCKET hAccept = accept(hSocket, pSocketAddress, nAddrLen);
	//如果该端口错误
	if(hAccept == INVALID_SOCKET) {
		DPSetLastError(WSAGetLastError());
	}
	return hAccept;
}

// 绑定套接字
//入口:套接字,绑定的地址信息,长度
//出口:正确0,错误-1
int CDPSocketModel::DPBindSocket(SOCKET hSocket, struct sockaddr * pSocketAddress, 
								int nAddrLen)
{
	if (bind(hSocket, pSocketAddress, nAddrLen) == SOCKET_ERROR)
	{
		DPSetLastError( WSAGetLastError() );
		return (SOCKET_ERROR);
	}
	return (SOCKET_SUCCESS);
}

// 绑定套接字
//入口:套接字,端口号
//出口:正确0,错误-1
//注意:这个函数不同于上个函数,调用这个函数只需要给定一个端口号
//     至于地址信息在函数内部默认处理(本机IP的该端口)
int CDPSocketModel::DPBindSocketEx(SOCKET hSocket,int nPort)
{
	SOCKADDR_IN sockAddr;
	ZeroMemory(&sockAddr,sizeof(sockAddr));

	sockAddr.sin_family			= AF_INET;
	sockAddr.sin_addr.s_addr	= htonl(INADDR_ANY);
	sockAddr.sin_port			= htons((u_short)nPort);

	return DPBindSocket(hSocket,(SOCKADDR *)&sockAddr, sizeof(sockAddr));
}

//释放Winsock2动态连接库
//入口:无
//出口:无
void CDPSocketModel::DPCleanupLibrary(void)
{
    WSACleanup();
}

//注册WINSOCK2.2DLL
//入口:无
//出口:无
int CDPSocketModel::DPInitLibrary(void)
{
	WSADATA         WSD;
	WORD wVersionRequired = MAKEWORD( DP_SOCKET_MAJOR_VERSION,DP_SOCKET_MINOR_VERSION );
	
	ZeroMemory(&WSD,sizeof(WSADATA));

	int nErrorNo = WSAStartup(wVersionRequired, &WSD);

	if ( SOCKET_SUCCESS != nErrorNo )
	{
		DPSetLastError( nErrorNo );
		return ( SOCKET_ERROR );
	}
	if ( LOBYTE( WSD.wVersion ) != DP_SOCKET_MINOR_VERSION ||
		 HIBYTE( WSD.wVersion ) != DP_SOCKET_MAJOR_VERSION ) 
	{
		WSACleanup( );
		DPSetLastError( WSAVERNOTSUPPORTED );
		return (SOCKET_ERROR); 
	}
	//成功初始化
	return (SOCKET_SUCCESS);
}


int CDPSocketModel::DPConnectEx_Event(SOCKET hSocket, char const * pszServer, int nPort, 
							  DWORD dwTimeout,BOOL fFixNtDNS/*=FALSE*/)
{
	/////////////////////////////////////////////////////////////////////////////
	SOCKADDR_IN sockAddr;
	ZeroMemory(&sockAddr,sizeof(sockAddr));
	//Make A SOCKADDR_IN struct That Use Some Server Info.
	sockAddr.sin_family			= AF_INET;
	sockAddr.sin_port			= htons((u_short)nPort);
	sockAddr.sin_addr.s_addr	= DPGetIP(pszServer,fFixNtDNS);
	
	if (sockAddr.sin_addr.s_addr == INADDR_NONE)
	{
		DPSetLastError( WSAGetLastError() );
		return (SOCKET_ERROR);
	}
	//////////////////////////////////////////////////////////////////////
	return DPConnect_Event(hSocket, (SOCKADDR *)&sockAddr,sizeof(sockAddr),dwTimeout);
}

// 建立连接
//入口:套接字,地址结构,结构长度,超时

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -