C Sockets: recv() blocks when all data is downloaded [duplicate]

别说谁变了你拦得住时间么 提交于 2020-06-01 07:36:26

问题


I'm writing a wrapper for Berkley sockets on Windows and Linux. The test program got problem here:

char buf[BUFSIZE];
int res = 0;
while((res = NetRecv(sock, buf, BUFSIZE, 0)) > 0) // 'NetRecv' is pointing to 'recv'
{
    buf[res-1] = '\0';
    printf("%s", buf);
}

The response is to a HTTP-Get request of a web-page content. The socket is streaming.

The 'NetRecv' is initialized correctly - that is, no type mismatch of the functions' pointers there is, I've checked it.

So, Windows version works flawlessly, the Linux one is stuck after reading all page. Namely, the previous to the last 'NetRecv' call accepts last chunk of the response, outputs it, and the next (last) call just blocks. Closing the terminal causes 'SIGHUP' signal. Looks like the Linux version just doesn't realize, that it received the last chunk of data and waits for more.

Is it as it should be? Don't understand then, for what reason there is blocking call possibility. Now, I surely could make non-blocking call and use 'select', but do I really have to?

Thanks in advance)

EDIT: Minimal working example (all checks are omitted and net functions are the standard ones, which also were tested):

int sock = socket(AF_INET, SOCK_STREAM, 0);

// Here getting good IP address of google.com - no problem here
char serv_ip[IPADDR_BUFSIZE];
GetHostAddrByName(AF_INET, "www.google.com", serv_ip, IPADDR_BUFSIZE);
//                 ip ver        site        out buf   out buf size
// The routine above is made with 'getaddrinfo', to be precise

printf("Current IP of '%s' is '%s'.\n", SERV_URL, serv_ip);

// Copying IP string to address struct
struct sockaddr_in addr;
NetIpFromStr(AF_INET, serv_ip, &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = NetHtons(80);

connect(sock, (const struct sockaddr*)&addr, sizeof(addr));

const char* msg = "GET / HTTP/1.1\r\n\r\n";
send(sock, msg, strlen(msg), 0);

char buf[BUFSIZE];
int res = 0;
while((res = recv(sock, buf, BUFSIZE-1, 0)) > 0)
{
    buf[res] = '\0';
    printf("%s", buf);
}

EDIT 2: Important notice: the Windows version also blocks the call, when all the data is read. Closing the terminal just doesn't crash the program, like it happens in Linux. Therefore, the whole question is such: How to realize that all data is read?


回答1:


The problem is that you are blindly reading from the socket in a loop until an error occurs. Once you have received the entire response, you go back to the socket and keep reading, which then blocks because there is nothing left to read. The only error that can occur at this point is when the connection is closed (or lost), which the server is likely not doing since you are sending an HTTP 1.1 request, where keep-alive is the default behavior for 1.1 (see RFC 2616 Section 8.1 Persistent Connections)

The correct solution is to parse the HTTP response and stop reading from the socket when you reach the end of the response, NOT simply relying on the server to close the socket. Read RFC 2616 Section 4.4 Message Length for how to detect when you have reached the end of the response. DO NOT read more than the response indicates! Once you stop reading, then you can decide whether to close your end of the socket, or reuse it for a new request.

Have a look at this pseudo code for the type of parsing and reading logic you need to use.

Also, your HTTP request is malformed, as you are not sending a required Host header, so no matter what, you will always receive a 400 Bad Request response from any HTTP 1.1 compliant server:

const char* msg = "GET / HTTP/1.1\r\n"
                  "Host: www.google.com\r\n" // <-- add this!
                  "\r\n";



回答2:


The solution was to shutdown the socket for reading, both in Windows and Linux:

// after sending a request:
shutdown(sock, SD_SEND);     // or 'SHUT_WR' in Linux

// now read loop

Curiously, 'shutdown' was called in Winsock tutorials too, but I thought that was unnecessary.



来源:https://stackoverflow.com/questions/51917021/c-sockets-recv-blocks-when-all-data-is-downloaded

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!