2013-01-02 58 views
1

代碼首先Winsock2的錯誤10014

DWORD WINAPI tcp_t(LPVOID lpParam) 
{ 
    SOCKET tcp_client_s = (SOCKET)lpParam; 
    struct sockaddr_in tcp_client; 
    int tcp_client_len = sizeof(tcp_client), length; 
    char req[4096], resp[4096]; 

    getpeername(tcp_client_s, (struct sockaddr *)&tcp_client, &tcp_client_len); 

    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) TCP thread spawned\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port)); 

    length = get_req_tcp(tcp_client_s, req, tcp_client); 

    if(strci(req, "GET /syachi2ds/web/", 0)) 
    { 
     while(!strstr(req, "Connection: close\r\n\r\n")) 
      length += get_req_tcp(tcp_client_s, req + length, tcp_client); 

     length = check_req(req, resp); 

     if(length > 0) 
      send_resp_tcp(tcp_client_s, resp, length, tcp_client); 
    } 

    closesocket(tcp_client_s); 
    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) socket closed, closing thread\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port)); 
    ExitThread(0); 
} 

int get_req_tcp(SOCKET in_s, char *buf, struct sockaddr_in in) 
{ 
    int retval; 

    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) waiting for incoming request...\n", inet_ntoa(in.sin_addr), ntohs(in.sin_port)); 
    if ((retval = recv(in_s, buf, 4096, 0)) == SOCKET_ERROR) 
     cli_log(PROTO_TCP, LOG_ERROR, "(%d) recv() failed\n", WSAGetLastError()); 
    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) data received\n", inet_ntoa(in.sin_addr), ntohs(in.sin_port)); 
    return retval; 
} 

這是一個較大的程序我正在寫的一部分做了一些類型的服務器仿真。它適用於TCP流不會分成多個數據包的情況,否則會在while循環中調用的第一個後續recv()上產生winsock錯誤10014。

PS:strci()是一個自定義的不區分大小寫strstr()

PS2:我知道有沒有檢查緩衝區REQ陣列上溢出。

+0

在失敗的情況下:其值做的第一'get_req_tcp()'調用返回的'length'(循環之前)? – sb9

+0

此時,如果LOG_ERROR作爲第二個參數傳遞(我已經計劃更改此行爲),cli_log()調用exit(0) – Ithilion

+0

從套接字流中讀取的數據不是nul結尾的,所以您需要手動添加如果你想使用'strstr',每個'recv'後面的''\ 0''。 –

回答

3

10014是WSAEFAULT,這意味着recv()正在檢測「buf參數未完全包含在用戶地址空間的有效部分中」。這是有道理的,因爲你的代碼中有一個緩衝區溢出錯誤。您已爲req緩衝區的調用堆棧分配了4096個字節。每次調用get_req_tcp()時,即告知它讀取4096個字節,即使req實際上沒有可讀取的4096個字節。

您的每一個循環運行的時候,你告訴recv()以字節讀入緩衝區內的一個新起點的位置,但你沒有告訴recv()多少個字節的位置之後的剩餘,所以循環溢出緩衝區,並最終將訪問不在調用堆棧上的內存地址,導致WSAEFAULT錯誤。

您需要添加一個額外的參數get_req_tcp(),告訴它如何讀取的字節數。

試試這個:

DWORD WINAPI tcp_t(LPVOID lpParam) 
{ 
    SOCKET tcp_client_s = (SOCKET)lpParam; 
    struct sockaddr_in tcp_client; 
    int tcp_client_len = sizeof(tcp_client), length; 
    char req[4096], resp[4096]; 

    getpeername(tcp_client_s, (struct sockaddr *)&tcp_client, &tcp_client_len); 

    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) TCP thread spawned\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port)); 

    length = get_req_tcp(tcp_client_s, req, sizeof(req), tcp_client); 
    if (length > 0) 
    { 
     while (!strstr(req, "\r\n\r\n")) 
     { 
      retval = get_req_tcp(tcp_client_s, req + length, sizeof(req) - length, tcp_client); 
      if (retval < 1) 
      { 
       length = 0; 
       break; 
      } 
      length += retval; 
     } 

     if ((length > 0) && (strci(req, "GET /syachi2ds/web/", 0))) 
     { 
      length = check_req(req, resp); 
      if (length > 0) 
       send_resp_tcp(tcp_client_s, resp, length, tcp_client); 
     } 
    } 

    closesocket(tcp_client_s); 
    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) socket closed, closing thread\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port)); 

    return 0; 
} 

int get_req_tcp(SOCKET in_s, char *buf, int buflen, struct sockaddr_in in) 
{ 
    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) waiting for incoming request...\n", inet_ntoa(in.sin_addr), ntohs(in.sin_port)); 

    if ((!buf) || (buflen < 1)) 
    { 
     cli_log(PROTO_TCP, LOG_ERROR, "invalid buffer passed for recv()\n"); 
     return -1; 
    } 

    int retval = recv(in_s, buf, buflen, 0); 

    if (retval == SOCKET_ERROR) 
    { 
     cli_log(PROTO_TCP, LOG_ERROR, "(%d) recv() failed\n", WSAGetLastError()); 
     return -1; 
    } 

    if (retval == 0) 
    { 
     cli_log(PROTO_TCP, LOG_ERROR, "client disconnected\n"); 
     return 0; 
    } 

    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) %d bytes received\n", retval, inet_ntoa(in.sin_addr), ntohs(in.sin_port)); 

    return retval; 
} 
+0

看起來確實是原因,它現在完美無缺! – Ithilion

+0

僅供參考,HTTP響應可能很容易大於4096字節,因此您可能需要重新考慮您的方法。改爲使用動態大小的緩衝區。將原始數據讀入緩衝區,根據需要生長,然後從緩衝區逐行讀取,而不是使用'strstr()'。 –

+0

+1;這既是資料性的,也解決了我的(相當類似的)問題。 – Wolfer