2017-08-18 51 views
0

我设计使用代理服务器和HTTP CONNECT请求的HTTP隧道。大约90%的时间,当我尝试发送我的连接请求CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n发送失败,返回errno = 11, "Try Again"Linux的套接字的send()错误EAGAIN

我使用非阻塞套接字,并关闭套接字连接,然后尝试之间重开。我不认为发送缓冲区已满,因为我没有发送太多数据。我很困惑它为什么有时连接,有时不连接(并且似乎没有模式,有时连接两次,有时连接之间有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... 
+0

发布您的代码并输出 – EsmaeelE

+1

您是否在每次发送'CONNECT'命令后都读取代理的响应? – dbush

+0

为什么这个'if(connect(sock,pSockAddr,iSockAddrSize))',而不是'if(connect(sock,pSockAddr,iSockAddrSize)!= -1)'? –

回答

2

你在你的代码的几个错误:

  1. connect()成功返回0和1的错误。对于任何非零值,if语句的计算结果为true。这意味着当connect()出现故障时,您的代码正在发送CONNECT消息。但是,并非所有的错误都是致命的。特别是,由于您使用的是非阻塞套接字,一个EINPROGRESS/WSAEWOULDBLOCK错误指示的连接仍然悬而未决,你需要使用select()(e)poll()等待插座实际完成连接(或失败)。因此,只有当connect()异步连接,并且恰好在您有机会致电send()之前完全连接到代理服务器时,您的代码才会起作用。但这是你不应该依赖的行为。你需要正确处理错误。

  2. send()的返回值指定多少字节实际上被接受,或者1的错误。可能会接受比请求更少的字节(尤其是在非阻塞套接字上)。并且您需要处理非致命错误,特别是在非Windows平台上的(WSA)EWOULDBLOCK(和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 
0
if (connect(sock, pSockAddr, iSockAddrSize)) { 
    ... 
    if (send(sock, message, strlen(message) + 1, 0) < 0) printf("*J* Send Failed: %d\n", errno); 

connect返回一个非零值时,被输入内if (connect(..块。由于连接在成功时返回0,在错误时返回-1(永久或暂时),这意味着当connect尚未(尚未)成功时输入该块。在这种情况下调用send在大多数情况下会失败,因为连接尚未完成,但在某些幸运的情况下,如果在此期间连接成功,则可能成功。

的打算很可能进入块如果连接succeded。在这种情况下,正确处理本来

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

当然,因为非阻塞套接字使用它是不太可能的连接将立即接替所以会有的无阻塞连接所需妥善处理。

+1

此外,对于非阻塞套接字,'connect()'可能无法立即成功,因此您必须在成功时检查返回值0,并在错误时检查-1。如果错误是'EINPROGRESS',则必须使用'select()'或'(e)poll()'等待连接实际完成,然后才能交换数据。 –

+1

另外,在'send()'上使用'strlen + 1'也是错误的,因为它发送了不是'CONNECT'协议一部分的空终止符。空终止符将被视为应用程序数据,通过隧道原样转发到下一个服务器(如果隧道成功),从而中断与该服务器的通信。此外,'send()'不保证发送给它的所有东西(特别是使用非阻塞套接字),所以你必须注意返回值,并在循环中不断调用send()来重发任何东西未发送的字节,直到发送完所有字节,或发生真正的错误。 –

+0

@RemyLebeau(...)包含对'EINPROGRESS'的检查,但我没有使用select!它现在正在工作,我添加了一个选择来等待套接字准备就绪。如果你想发布它,你有正确的答案! –