问题描述
我目前正在用C编写一个小型项目,以更好地理解TCP,TLS,HTTP方法和C本身。
这是我程序的GET部分的简化代码段(无错误检查,已删除OpenSSL函数):
void htmlGET(char * path,char * address,int sockfd) {
struct pollfd fds[1];
fds[0].fd = sockfd;
fds[0].events = POLLIN | POLLHUP | POLLERR;
char * header;
header = malloc(strlen(address)+50);
sprintf(header,"GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",path,address);
write(sockfd,header,strlen(header));
char buf[BUFSIZE];
int rcount;
while(1) {
poll(fds,1,0);
if (fds[0].revents & (POLLHUP | POLLERR)) { break; }
else if (fds[0].revents & POLLIN){
rcount = read(sockfd,buf,sizeof(buf));
write(1,rcount);
}
}
}
我的程序执行GET请求并接收数据而无需轮询。但是,我发现某些网站会发送标头,然后在其他消息中发送其余的HTML,因此我决定实现轮询以接收所有内容。但是,每当我运行此代码时,该程序都会无限期地循环,并且我一直无法找到根本原因。关于可能出什么问题的任何建议吗?
更新:该程序似乎在一定程度上可以正常工作。我发现它确实在某个时间点完成,因此我决定在其上运行time(1)
。这是一个示例结果:
3.33s user 23.74s system 20% cpu 2:14.94 total
关于为什么这么慢的任何想法?有时HTML会立即加载,程序会轮询很长时间,有时会轮询很长时间,然后HTML就会加载。
解决方法
关于:
poll(fds,1,0);
在MAN页面:
请注意,超时间隔将四舍五入到系统时钟 粒度和内核调度延迟意味着阻塞间隔可能会少量溢出。在中指定负值 超时意味着无限超时。 将超时指定为零会导致poll()立即返回,即使没有准备好文件描述符也是如此。(强调我的想法)
建议超时为负值,而不是0例如:-1
,在this帖子中,很明显,您不能指望Web服务器关闭与客户端的连接,因此使用轮询来确定何时停止从套接字读取并不是完整的解决方案。
我决定为Transfer-Encoding: Chunked
或Content-Length: #
合并消息头的解析。这为我提供了必要的信息,以确定是否已收到所有承诺的数据。
例如,使用Content-Length
,我使用消息头以空白链接(\r\n\r\n
)结尾的事实来确定消息正文的起始位置,然后计算接收到的字节数在缓冲区中,看看是否需要循环回去并再次读取身体的其余部分。
对于Transfer-Encoding: Chunked
,我在相同的帖子和this指南上使用了建议,发现仅用0\r\n\r\n
检查结尾块就可以了。