WINSOCK 非套接字上的套接字操作
这是类方法(所有方法和变量都声明为公共) 注意:DiscoverWinsockLib 负责 WSAStartup 的工作,我知道该部分可以工作。它还执行 LoadLibrary 并查找以下函数的 DLL 入口点,并使用 lpfn_ 前缀重命名。
无论我做什么,我似乎都无法解决为什么我会收到线程中非套接字上的套接字操作的错误条件。
非常感谢任何帮助。
bool __fastcall TRANSPORT::Establish (const char *host, int port, int timeout)
{
bool rFlag;
int rval;
rFlag = false;
DiscoverWinsockLib();
if( WSH )
{
hostEntry = gethostbyname(host);
if( !hostEntry )
{
LastErrorCode = lpfn_WSAGetLastError();
lpfn_WSACleanup();
SetErrorMsg();
}
else
{
CSocket = lpfn_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (CSocket == INVALID_SOCKET)
{
LastErrorCode = lpfn_WSAGetLastError();
SetErrorMsg();
lpfn_WSACleanup();
}
else
{
RxDataBuf.len = RXDATA_BUFSIZE;
RxDataBuf.buf = RxBuffer;
RxOverlapped.hEvent = lpfn_WSACreateEvent();
if(RxOverlapped.hEvent == WSA_INVALID_EVENT)
{
LastErrorCode = lpfn_WSAGetLastError();
SetErrorMsg();
SocketShutdown();
//lpfn_closesocket(CSocket);
}
else
{
if(lpfn_WSAEventSelect(CSocket, RxOverlapped.hEvent, FD_ALL_EVENTS) == SOCKET_ERROR)
{
LastErrorCode = lpfn_WSAGetLastError();
SetErrorMsg();
lpfn_closesocket(CSocket);
lpfn_WSACloseEvent(RxOverlapped.hEvent);
}
else
{
hThreadIO = CreateThread(0, 0, ProcessEvents, (void *)this, 0, &ThreadID);
// Try to connect to the server
serverInfo.sin_family = AF_INET;
serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
serverInfo.sin_port = htons(port);
if( lpfn_connect(CSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)) == SOCKET_ERROR )
{
LastErrorCode = lpfn_WSAGetLastError();
if( LastErrorCode != WSAEWOULDBLOCK )
{
SetErrorMsg();
SocketShutdown();
}
else
rFlag = true;
}
else
rFlag = true;
}
}
}
}
}
return(rFlag);
}
这是上面引用的线程:
DWORD WINAPI ProcessEvents(void *pParam)
{
TRANSPORT *T;
WSANETWORKEVENTS NetEvents;
DWORD EIndex;
DWORD rc;
bool Failure;
T = (TRANSPORT *)pParam;
Failure = false;
do
{
EIndex = T->lpfn_WSAWaitForMultipleEvents( 1, &T->RxOverlapped.hEvent, false, 3000, true );
if( EIndex == WSA_WAIT_FAILED )
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
Failure = true;
}
if( EIndex == WSA_WAIT_TIMEOUT )
{
if( T->OnConnectTimeout )
T->OnConnectTimeout(T);
Failure = true;
}
}while( EIndex != WAIT_IO_COMPLETION && !Failure );
if( Failure )
return(EIndex);
// Get the event(s) that occurred and their associated error array
if(T->lpfn_WSAEnumNetworkEvents(T->CSocket, T->RxOverlapped.hEvent, &NetEvents) == SOCKET_ERROR)
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
return(EIndex);
}
// =================================================================
// Code above fetches the event that woke this thread up
// Code below deals with the events as they are detected
// =================================================================
if( NetEvents.lNetworkEvents & FD_CONNECT )
{
if( T->OnConnect )
T->OnConnect(T);
T->lpfn_WSAResetEvent(T->RxOverlapped.hEvent);
}
else if( ( NetEvents.lNetworkEvents & FD_READ) )
{
rc = T->lpfn_WSARecv(T->CSocket, &T->RxDataBuf, 1, &T->RxBytes, &T->Flags, &T->RxOverlapped, NULL);
EIndex = T->lpfn_WSAGetLastError();
if( (rc == SOCKET_ERROR) && (EIndex != WSA_IO_PENDING || EIndex != WSAEWOULDBLOCK ) )
{
T->LastErrorCode = EIndex;
T->SetErrorMsg();
}
else
{
rc = T->lpfn_WSAWaitForMultipleEvents(1, &T->RxOverlapped.hEvent, true, INFINITE, true);
if(rc == WSA_WAIT_FAILED)
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
}
else
{
rc = T->lpfn_WSAGetOverlappedResult(T->CSocket, &T->RxOverlapped, &T->RxBytes, false, &T->Flags);
if(!rc)
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
}
else
{
if( T->RxBytes > 0 && T->OnReceive )
T->OnReceive(T);
T->lpfn_WSAResetEvent(T->RxOverlapped.hEvent);
}
}
}
}
return(rc);
}
**更新** 我发现调用 EIndex = T->lpfn_WSAWaitForMultipleEvents( 1, &T->RxOverlapped.hEvent, false, 3000, true ); 线程函数内部返回 WSA_WAIT_FAILED
如果我禁用线程函数并检查返回值 lpfn_WSAWaitForMultipleEvents,在线程外部,在 connect 调用之后,我得到了 WSAEWOULDBLOCK 的有效返回值。
考虑到我可能在从线程内部获取地址时遇到问题,我将线程代码更改为如下:
DWORD WINAPI ProcessEvents(void *pParam)
{
...
WSAEVENT events[1];
T = (TRANSPORT *)pParam;
Failure = false;
do
{
events[0] = T->RxOverlapped.hEvent;
EIndex = T->lpfn_WSAWaitForMultipleEvents( 1, events, false, 3000, true );
....
但我仍然无法使 WSAWaitForMultipleEvents 满意!
对此有什么想法/建议吗?
Here is the Class Method (all methods and vars are declared public)
NOTE: DiscoverWinsockLib does the WSAStartup stuff and I know that part works. It also does a LoadLibrary and finds the DLLs entry points for the functions below, renamed with lpfn_ prefix.
No matter what I do, I can't seem to resolve why I get the error condition of socket operation on non-socket in the Thread.
Any help is greatly appreciated.
bool __fastcall TRANSPORT::Establish (const char *host, int port, int timeout)
{
bool rFlag;
int rval;
rFlag = false;
DiscoverWinsockLib();
if( WSH )
{
hostEntry = gethostbyname(host);
if( !hostEntry )
{
LastErrorCode = lpfn_WSAGetLastError();
lpfn_WSACleanup();
SetErrorMsg();
}
else
{
CSocket = lpfn_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (CSocket == INVALID_SOCKET)
{
LastErrorCode = lpfn_WSAGetLastError();
SetErrorMsg();
lpfn_WSACleanup();
}
else
{
RxDataBuf.len = RXDATA_BUFSIZE;
RxDataBuf.buf = RxBuffer;
RxOverlapped.hEvent = lpfn_WSACreateEvent();
if(RxOverlapped.hEvent == WSA_INVALID_EVENT)
{
LastErrorCode = lpfn_WSAGetLastError();
SetErrorMsg();
SocketShutdown();
//lpfn_closesocket(CSocket);
}
else
{
if(lpfn_WSAEventSelect(CSocket, RxOverlapped.hEvent, FD_ALL_EVENTS) == SOCKET_ERROR)
{
LastErrorCode = lpfn_WSAGetLastError();
SetErrorMsg();
lpfn_closesocket(CSocket);
lpfn_WSACloseEvent(RxOverlapped.hEvent);
}
else
{
hThreadIO = CreateThread(0, 0, ProcessEvents, (void *)this, 0, &ThreadID);
// Try to connect to the server
serverInfo.sin_family = AF_INET;
serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
serverInfo.sin_port = htons(port);
if( lpfn_connect(CSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)) == SOCKET_ERROR )
{
LastErrorCode = lpfn_WSAGetLastError();
if( LastErrorCode != WSAEWOULDBLOCK )
{
SetErrorMsg();
SocketShutdown();
}
else
rFlag = true;
}
else
rFlag = true;
}
}
}
}
}
return(rFlag);
}
And here is the Thread referenced above:
DWORD WINAPI ProcessEvents(void *pParam)
{
TRANSPORT *T;
WSANETWORKEVENTS NetEvents;
DWORD EIndex;
DWORD rc;
bool Failure;
T = (TRANSPORT *)pParam;
Failure = false;
do
{
EIndex = T->lpfn_WSAWaitForMultipleEvents( 1, &T->RxOverlapped.hEvent, false, 3000, true );
if( EIndex == WSA_WAIT_FAILED )
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
Failure = true;
}
if( EIndex == WSA_WAIT_TIMEOUT )
{
if( T->OnConnectTimeout )
T->OnConnectTimeout(T);
Failure = true;
}
}while( EIndex != WAIT_IO_COMPLETION && !Failure );
if( Failure )
return(EIndex);
// Get the event(s) that occurred and their associated error array
if(T->lpfn_WSAEnumNetworkEvents(T->CSocket, T->RxOverlapped.hEvent, &NetEvents) == SOCKET_ERROR)
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
return(EIndex);
}
// =================================================================
// Code above fetches the event that woke this thread up
// Code below deals with the events as they are detected
// =================================================================
if( NetEvents.lNetworkEvents & FD_CONNECT )
{
if( T->OnConnect )
T->OnConnect(T);
T->lpfn_WSAResetEvent(T->RxOverlapped.hEvent);
}
else if( ( NetEvents.lNetworkEvents & FD_READ) )
{
rc = T->lpfn_WSARecv(T->CSocket, &T->RxDataBuf, 1, &T->RxBytes, &T->Flags, &T->RxOverlapped, NULL);
EIndex = T->lpfn_WSAGetLastError();
if( (rc == SOCKET_ERROR) && (EIndex != WSA_IO_PENDING || EIndex != WSAEWOULDBLOCK ) )
{
T->LastErrorCode = EIndex;
T->SetErrorMsg();
}
else
{
rc = T->lpfn_WSAWaitForMultipleEvents(1, &T->RxOverlapped.hEvent, true, INFINITE, true);
if(rc == WSA_WAIT_FAILED)
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
}
else
{
rc = T->lpfn_WSAGetOverlappedResult(T->CSocket, &T->RxOverlapped, &T->RxBytes, false, &T->Flags);
if(!rc)
{
T->LastErrorCode = T->lpfn_WSAGetLastError();
T->SetErrorMsg();
}
else
{
if( T->RxBytes > 0 && T->OnReceive )
T->OnReceive(T);
T->lpfn_WSAResetEvent(T->RxOverlapped.hEvent);
}
}
}
}
return(rc);
}
** UPDATE **
I'm finding that the call to
EIndex = T->lpfn_WSAWaitForMultipleEvents( 1, &T->RxOverlapped.hEvent, false, 3000, true );
inside the Thread function is returning WSA_WAIT_FAILED
And if I disable the thread function and check the return value of
lpfn_WSAWaitForMultipleEvents, outside the thread, right after the connect call, I get a valid return value of WSAEWOULDBLOCK.
Thinking I might have a problem taking the address-of from inside the thread, I changed the thread code to read as follows:
DWORD WINAPI ProcessEvents(void *pParam)
{
...
WSAEVENT events[1];
T = (TRANSPORT *)pParam;
Failure = false;
do
{
events[0] = T->RxOverlapped.hEvent;
EIndex = T->lpfn_WSAWaitForMultipleEvents( 1, events, false, 3000, true );
....
and I still cannot manage to make WSAWaitForMultipleEvents happy!
any thoughts/suggestions on this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果 connect() 失败,则您将调用 SocketShutdown()。据推测,它会关闭套接字(您没有显示该代码),但是如果线程正在运行,它会首先终止线程吗?否则,您可能会在线程后面关闭套接字。
If connect() fails, you are calling
SocketShutdown()
. Presumably, it closes the socket (you did not show that code), but does it terminate the thread first if it is running? Otherwise, you could be closing the socket behind the thread's back.