问题描述
我有一个用C编写的简单XMLHttpRequest处理程序。它读取并处理来自浏览器中运行的JavaScript XMLHttpRequest send()的请求。
父进程接受传入连接,并为每个传入连接派生一个子进程以读取和处理数据。
它对大多数请求都非常有效,但是在某些情况下(显然与客户端和服务器之间的网络基础结构有关)会失败,如果请求的长度超过2K字节。我假设该请求被分解为浏览器和套接字服务器之间某个位置的多个数据包。
我无法更改请求格式,但可以看到正在发送的请求并验证了内容。数据是带有包含“类型”字段的编码URI的“ GET”。如果类型为“文件”,则请求的长度可能为3K,否则最多为数百个字节。 “文件”请求很少见-用户正在提供要写入服务器上文件的配置数据。所有其他请求都可以正常工作,所有小于2K的“文件”请求都可以正常工作。
在这种情况下,确保我拥有所有数据的首选技术是什么?
以下是父级接受连接并派生子级的部分(非阻塞版本):
for (hit = 1;; hit++) {
length = sizeof(cli_addr);
if ((socketfd = accept4(listensd,(struct sockaddr *) &cli_addr,&length,SOCK_NONBLOCK)) < 0){
//if ((socketfd = accept(listensd,&length)) < 0){
exit(3);
}
if ((pid = fork()) < 0) {
exit(3);
} else {
if (pid == 0) { /* child */
//(void) close(listensd);
childProcess(socketfd,hit); /* never returns. Close listensd when done*/
} else { /* parent */
(void) close(socketfd);
}
}
}
这是子进程中执行初始recv()的部分。对于较长的“文件”请求,孩子的第一个套接字recv()获得约1700字节的有效负载,后跟浏览器提供的连接数据。
ret = recv(socketfd,recv_data,BUFSIZE,0); // read request
if (ret == 0 || ret == -1) { // read failure stop Now
sprintf(sbuff,"Failed to read request: %d",ret);
logger(&shm,FATAL,sbuff,socketfd);
}
recv_data[ret] = 0;
len = ret;
如果类型为“文件”,则可能会有更多数据。子进程永远不会获取其余数据。如果套接字阻塞,则第二次读取尝试将挂起。如果套接字是非阻塞的(如下面的代码片段所示),则所有后续读取都将返回-1,并显示错误“资源暂时不可用”,直到超时:
// It's a file. Could be broken into multiple blocks. Try second read
sleep(1);
ret = recv(socketfd,&recv_data[len],0); // read request
while (ret != 0){
if(ret > 0){
recv_data[len+ret] = 0;
len += ret;
} else {
sleep(1);
}
ret = recv(socketfd,0); // read request
}
我希望客户端关闭连接时read()会返回0,但这不会发生。
解决方法
一个GET请求只有一个头部,没有任何主体(嗯,几乎总是这样),因此一旦有了请求头部,您就拥有了客户端已发送的所有内容,并且您知道何时阅读了整个请求头部读取一个空行,即两次返回(并且不迟早)。
如果客户端仅发送一部分,而没有空白行,则您应该等待其余部分。如果要花费太长时间,我会暂停并拒绝整个请求。
顺便说一句,仍然有一些浏览器,也许还有一些代理,它们的URL长度限制约为2000个字符。