📄 dpsocketmodel.cpp
字号:
//发送完所有数据或超时
//入口:套接字,缓冲区,缓冲区大小,超时
//出口:返回已经发送的字节个数或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 + -