2016-11-15 32 views
0

WSAConnectByName()を使用してアドレスに接続しようとしています。しかし、それはtimeoutパラメータを無視したようです。私の代わりに、タイムアウトで失敗の、(ローカルIPアドレスなど)が存在しないアドレスを使用する場合、WSAConnectByName timeout

SOCKET ConnSocket = INVALID_SOCKET; 
int iResult; 
BOOL bSuccess; 
SOCKADDR_STORAGE LocalAddr = {0}; 
SOCKADDR_STORAGE RemoteAddr = {0}; 
DWORD dwLocalAddr = sizeof(LocalAddr); 
DWORD dwRemoteAddr = sizeof(RemoteAddr); 

ConnSocket = socket(AF_INET, SOCK_STREAM, 0); 
if (ConnSocket == INVALID_SOCKET){ 
    wprintf(L"socket failed with error: %d\n", WSAGetLastError()); 
    return INVALID_SOCKET; 
} 

struct timeval tv; 
tv.tv_sec = 1; 
tv.tv_usec = 0; 

bSuccess = WSAConnectByName(ConnSocket, NodeName, 
     PortName, &dwLocalAddr, 
     (SOCKADDR*)&LocalAddr, 
     &dwRemoteAddr, 
     (SOCKADDR*)&RemoteAddr, 
     (struct timeval *)&tv, 
     NULL); 
if (!bSuccess){ 
    wprintf(L"WsaConnectByName failed with error: %d\n", WSAGetLastError()); 
    closesocket(ConnSocket); 
    return INVALID_SOCKET;  
} 

:ここ

は、MSの例から(ほんの少しだけ変更された)コードであります他のタイムアウトが発生するまでコードは停止します。

ここで何が起こっているのでしょうか?

答えて

1

これは不正な文書によるものです。

パラメータは、部分的に使用されますです。 WSAConnectByNameは、内部的に多くの操作を実行する複雑な関数です。

ULONG time, ms; 
if (timeout) 
{ 
    time = GetTickCount(); 
    ms = timeout->tv_sec * 1000 + timeout->tv_usec/1000; 
} 

し、それがこのようなコードを呼び出して、数回:初め

は、このようなコードが存在する

if (timeout && GetTickCount() - time > ms) return WSAETIMEDOUT; 

が、関数の心はこのようConnectExの呼び出し:

if (!ConnectEx(*)) 
{ 
    if (GetLastError() == ERROR_IO_PENDING) 
    { 
    WSAGetOverlappedResult(*); // you wait here and the timeout is not used! 
    } 
} 

ConnectEx(いずれも非同期関数)とGetOverlappedResultには、タイムアウトを指定するパラメータがありません。 ConnectEx終了後にGetOverlappedResultで待機することになりますが、タイムアウトを設定することはできません。

ただ1つの素晴らしい解決策があります。ConnectExを直接使用し、タイムアウトを使用しないでください。

他に1つの問題点 - GetAddrInfoExnodenameをipアドレスに変換するために使用されました。この関数はtimeout=0, lpOverlapped=0, lpCompletionRoutine = 0と呼ばれています - ここでもDNS要求を待つことができます。作成/ OVERLAPPED.hEvent

OVERLAPPED Overlapped = {}; 
Overlapped.hEvent = CreateEvent(0, 0, 0, 0); 
//... ConnectEx ... 
ULONG NumberOfBytesTransferred = 0; 
ULONG err = GetLastError(); 
if (err == ERROR_IO_PENDING) 
{ 
    switch (WaitForSingleObject(Overlapped.hEvent, ms)) 
    { 
    case STATUS_TIMEOUT: 
     CancelIoEx((HANDLE)s, &Overlapped); 
     err = WSAETIMEDOUT; 
     break; 
    case WAIT_OBJECT_0: 
     // really final NT status of operation is Internal and NumberOfBytesTransferred == InternalHigh 
     // GetOverlappedResult set LastError by translating (NTSTATUS)Internal to win32 error 
     // with the loss of accuracy, especially if status > 0 
     GetOverlappedResult((HANDLE)s, &Overlapped, &NumberOfBytesTransferred, TRUE); 
     err = GetLastError(); 

     // code of GetOverlappedResult in this case for clarity 
     //if ((NTSTATUS)Overlapped.InternalHigh == STATUS_PENDING) 
     //{ 
     // WaitForSingleObject(Overlapped.hEvent, INFINITE); 
     //} 
     //NumberOfBytesTransferred = (ULONG)Overlapped.InternalHigh; 
     //if (0 > (NTSTATUS)Overlapped.Internal) 
     //{ 
     // SetLastError(RtlNtStatusToDosError((NTSTATUS)Overlapped.Internal)); 
     //} 

     break; 
    default: __debugbreak(); 
    } 
} 

または代替使用のために使用 - 私たちは(アイデアのためRemy Lebeauに感謝)タイムアウトを使用することができます直接使用ConnectEx場合にのみサポートされ、非同期クエリは、EDIT

のWindows 8から始まりますGetOverlappedResultExタイムアウト(ただし、ウィンドウズ8+が必要)

または私の最良の選択 - 使用BindIoCompletionCallback((HANDLE)s,)またはソケットのダイレクトバインド自己IOCPであり、呼び出し後もまったく待ちません。

+1

'ConnectEx()'は標準的なオーバーラップI/Oを使用しているので、タイムアウトを使うことができます。 'OVERLAPPED'構造体を' ConnectEx() 'に渡す必要があります。そのため、イベントオブジェクトへのハンドルを渡してください。次に、GetOverlappedResult()を呼び出す前に、そのイベントでWaitForSingleObject()を呼び出すことができます。イベントがタイムアウトした場合は、 'CancelIo/Ex()'を使って 'ConnectEx()'操作をキャンセルしてください。もし 'WSAConnectByName()'が内部的にそれをしていないなら、それを追加するように求めるM $に機能要求を送ることを勧めます。 –

+0

@RemyLebeau - もちろん、自分自身が 'ConnectEx' - ' WSAConnectByName'を呼び出し、 'OVERLAPPED'でhEventを作成/使用するとタイムアウトを使用することができます。 'GetOverlappedResult()'このイベントではまさにこのイベントを待っているので、単に 'WaitForSingleObject'(タイムアウトで)を' GetOverlappedResult() 'を呼び出すことなく簡単に行うことができます。しかし、私は最高の解決策(私の見解では) - 本当に非同期的に 'ConnectEx'を使うことを意味します。 IOCPをソケットに直接バインドするか(または 'BindIoCompletionCallback'を使用する)、まったく待ちません。 'ERROR_IO_PENDING'はOKです。私たちのコールバックは – RbMm

+0

@RemyLebeauと呼ばれています - はい、最新の勝利10(1607) - まだ同じコード 'ConnectEx(*)/ if(GetLastError()== ERROR_IO_PENDING)/ GetOverlappedResult(*);' 'WSAGetOverlappedResult' - より正確に言えば、 – RbMm