簡體   English   中英

Linux套接字send()錯誤EAGAIN

[英]Linux socket send() error EAGAIN

我正在設計使用代理和HTTP CONNECT請求的HTTP隧道。 大約90%的時間,當我嘗試發送連接請求CONNECT 192.168.1.68:3001 HTTP/1.1\\r\\n\\r\\n發送失敗時,返回errno = 11, "Try Again"

我正在使用非阻塞套接字,並且套接字在連接嘗試之間先關閉然后重新打開。 我不認為發送緩沖區已滿,因為我沒有發送太多數據。 我很困惑為什么它有時會連接而有時卻不連接(並且似乎沒有某種模式,有時它會連續兩次連接,有時連接之間會出現10次失敗)。

我可能會收到此錯誤的原因有哪些?

編輯:

(碼)

if (connect(sock, pSockAddr, iSockAddrSize)) {
    ...
    char message[80];
    sprintf(message, "CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n");
    printf("*J* Message loaded.\n"); //*J*
    printf("*J* %s\n", message); //*J*
    if (send(sock, message, strlen(message) + 1, 0) < 0) printf("*J* Send Failed: %d\n", errno); 
    else printf("*J* Data Sent.\n");
    }
...
printf("\n*J* Close Socket (RESET HOST COMMAND)");
closesocket(sock);

(輸出)

[SUCCESS]
*J* Starting connect...
*J* Message loaded.
*J* CONNECT 192.168.1.68:3001 HTTP/1.1


*J* Data Sent.
*J* rv = 1
*J* Ending connect...

[FAIL]
*J* Starting connect...
*J* Message loaded.
*J* CONNECT 192.168.1.68:3001 HTTP/1.1


*J* Send Failed: 11
*J* Ending connect...

您的代碼中有幾個錯誤:

  1. connect()成功返回0,錯誤返回-1。 if任何非零值, if語句的評估結果為true。 這意味着當connect()因錯誤而失敗時,您的代碼正在發送CONNECT消息。 但是,並非所有錯誤都是致命的。 特別是,由於您使用的是非阻塞套接字,因此EINPROGRESS / WSAEWOULDBLOCK錯誤指示連接仍在掛起,您需要使用select()(e)poll()來等待套接字實際完成連接(或失敗)。 因此,只有在connect()異步連接並且恰好在您有機會調用send()之前完全連接到代理時,您的代碼才能工作。 但這是您不應該依賴的行為。 您需要正確處理錯誤。

  2. send()的返回值指定實際接受的字節數,如果出錯則返回-1。 可以接受的字節數少於請求的字節數(尤其是在非阻塞套接字上)。 而且您需要處理非致命錯誤,尤其是(WSA)EWOULDBLOCK (和非Windows平台上的EAGAIN ,甚至還有EINTR ),這意味着套接字在調用send()時無法接受新數據。 ,因此請再次調用send() 您應該在循環中調用send() ,直到發送完所有字節,否則將發生致命錯誤。

  3. 即使您正確地send()讀取數據,在strlen()之后使用+ 1也是錯誤的。 您將空終止符包含在要發送的數據中,但這不是CONNECT協議的一部分。 代理會將空終止符視為應用程序數據,並將按原樣通過隧道轉發到下一個服務器(如果隧道已成功打開),從而中斷與該服務器的通信。

話雖如此,請嘗試以下類似的方法。 您使用closeocket()表示您正在為Windows編程,因此這是Windows特定的:

int ret, err;
...
ret = connect(sock, pSockAddr, iSockAddrSize);
if (ret == -1)
{
    err = WSAGetLastError();

    if (err == WSAEWOULDBLOCK)
    {
        fd_set wfd;
        FD_ZERO(&wfd);
        FD_SET(sock, &wfd);

        fd_set efd;
        FD_ZERO(&efd);
        FD_SET(sock, &efd);

        timeval timeout;
        timeout.tv_sec = ...;
        timeout.tv_usec = ...;

        ret = select(0, NULL, &wfd, &wfd, &timeout);

        if (ret == 0)
        {
            printf("*J* Connect Timeout\n"); 
            // handle timeout as needed ...
            return;
        }

        if ((ret > 0) && FD_ISSET(sock, &efd))
        {
            err = 0;
            getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof(err));
            ret = -1;
        }
    }

    if (ret == -1)
    {
        printf("*J* Connect Failed: %d\n", err); 
        // handle fatal error as needed ...
        return;
    }
}

...

char message[80];
sprintf(message, "CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n");
printf("*J* Message loaded.\n"); //*J*
printf("*J* %s\n", message); //*J*

char *pmsg = message;
int len = strlen(message);

do
{
    ret = send(sock, pmsg, len, 0);
    if (ret == -1)
    {
        err = getLastSocketError();    

        if (err == WSAEWOULDBLOCK)
        {
            fd_set wfd;
            FD_ZERO(&wfd);
            FD_SET(sock, &wfd);

            timeval timeout;
            timeout.tv_sec = ...;
            timeout.tv_usec = ...;

            ret = select(0, NULL, &wfd, NULL, &timeout);

            if (ret > 0)
                continue;

            if (ret == 0)
            {
                printf("*J* Send Timeout\n"); 
                // handle timeout as needed ...
                return;
            }

            err = WSAGetLastError();
        }

        printf("*J* Send Failed: %d\n", err); 
        // handle fatal error as needed ...
        return;
    }
    else
    {
        pmsg += ret;
        len += ret;
    }
}
while (len > 0);

printf("*J* Data Sent\n");

...

printf("*J* Close Socket\n");
closesocket(sock);

如果您想要更多的跨平台內容,請嘗試以下方法(或使用跨平台套接字庫):

int getLastSocketError()
{
    #ifdef WINDOWS
    return WSAGetLastError();
    #else
    return errno;
    #endif
};

int ret, err;
...
ret = connect(sock, pSockAddr, iSockAddrSize);
if (ret == -1)
{
    err = getLastSocketError();    

    if (
        #ifdef WINDOWS
        err == WSAEWOULDBLOCK
        #else
        err == EINPROGRESS
        #endif
        )
    {
        #ifndef WINDOWS
        do {
        #endif

        fd_set wfd;
        FD_ZERO(&wfd);
        FD_SET(sock, &wfd);

        #ifdef WINDOWS
        fd_set efd;
        FD_ZERO(&efd);
        FD_SET(sock, &efd);
        #endif

        timeval timeout;
        timeout.tv_sec = ...;
        timeout.tv_usec = ...;

        #ifdef WINDOWS
        ret = select(0, NULL, &wfd, &wfd, &timeout);
        #else
        ret = select(sock+1, NULL, &wfd, NULL, &timeout);
        #endif

        if (ret == 0)
        {
            printf("*J* Connect Timeout\n"); 
            // handle timeout as needed ...
            return;
        }

        #ifdef WINDOWS
        if ((ret > 0) && FD_ISSET(sock, &efd))
        {
            err = 0;
            getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof(err));
            ret = -1;
        }
        #endif

        #ifndef WINDOWS
        } while ((ret == -1) && (errno == EINTR));
        #endif
    }

    if (ret == -1)
    {
        printf("*J* Connect Failed: %d\n", err); 
        // handle fatal error as needed ...
        return;
    }
}

...

char message[80];
sprintf(message, "CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n");
printf("*J* Message loaded.\n"); //*J*
printf("*J* %s\n", message); //*J*

char *pmsg = message;
int len = strlen(message);

do
{
    ret = send(sock, pmsg, len, 0);
    if (ret == -1)
    {
        err = getLastSocketError();    

        if (
            #ifdef WINDOWS
            err == WSAEWOULDBLOCK
            #else
            (err == EWOULDBLOCK) || (err == EAGAIN)
            #endif
            )
        {
            fd_set wfd;
            FD_ZERO(&wfd);
            FD_SET(sock, &wfd);

            timeval timeout;
            timeout.tv_sec = ...;
            timeout.tv_usec = ...;

            ret = select(
                #ifdef WINDOWS
                0
                #else
                sock+1
                #endif
                , NULL, &wfd, NULL, &timeout);

            if (ret > 0)
                continue;

            if (ret == 0)
            {
                printf("*J* Send Timeout\n"); 
                // handle timeout as needed ...
                return;
            }

            err = getLastSocketError();
        }

        #ifndef WINDOWS
        if (err != EINTR)
        #endif
        {
            printf("*J* Send Failed: %d\n", err); 
            // handle fatal error as needed ...
            return;
        }
    }
    else
    {
        pmsg += ret;
        len += ret;
    }
}
while (len > 0);

printf("*J* Data Sent\n");

...

printf("*J* Close Socket\n");

#ifdef WINDOWS
closesocket(sock);
#else
close(sock);
#endif
if (connect(sock, pSockAddr, iSockAddrSize)) {
    ...
    if (send(sock, message, strlen(message) + 1, 0) < 0) printf("*J* Send Failed: %d\n", errno); 

if (connect(..connect返回非零值時輸入if (connect(.. )中的塊。由於connect在成功時返回0,而在錯誤時(永久或暫時)返回-1,這意味着在connect未返回時(尚未)輸入該塊。在大多數情況下,由於連接尚未完成,因此在這種情況下調用send會失敗,但在某些幸運的情況下,如果同時成功,則可能會成功。

如果連接成功,則可能打算進入該塊。 在這種情況下,正確的處理方法是

if (0 == connect(sock, pSockAddr, iSockAddrSize)) {
     ...

當然,由於使用了非阻塞式套接字,因此連接不可能立即成功進行,因此需要對非阻塞式連接進行適當的處​​理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM