C 套接字服务器:读取所有未知长度 XMLHttpRequest 的正确方法是啥?

Posted

技术标签:

【中文标题】C 套接字服务器:读取所有未知长度 XMLHttpRequest 的正确方法是啥?【英文标题】:C socket server: What's the right way to read all of an unknown length XMLHttpRequest?C 套接字服务器:读取所有未知长度 XMLHttpRequest 的正确方法是什么? 【发布时间】:2021-02-09 20:35:25 【问题描述】:

我有一个用 C 编写的简单 XMLHttpRequest 处理程序。它读取和处理来自在浏览器中运行的 javascript XMLHttpRequest send() 的请求。

父进程接受传入的连接,并为每个传入的连接派生一个子进程来读取和处理数据。

它适用于大多数请求,但如果请求长度超过 2K 字节,则在某些情况下会失败(显然与客户端和服务器之间的网络基础设施有关)。我假设请求在浏览器和我的套接字服务器之间的某处被分解为多个数据包。

我无法更改请求格式,但我可以看到正在发送的请求并验证内容。数据是一个“GET”,带有一个包含“type”字段的编码 URI。如果类型是“文件”,则请求可能长达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, (struct sockaddr *) &cli_addr, &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;

如果类型是“文件”,则可能有更多数据。子进程永远不会得到其余的数据。如果套接字阻塞,第二次读取尝试就会挂起。如果套接字是非阻塞的(如下面的 sn-p 所示),所有后续读取都返回 -1 并返回错误“资源暂时不可用”,直到超时:

// It's a file. Could be broken into multiple blocks. Try second read
sleep(1);
ret = recv(socketfd, &recv_data[len], BUFSIZE, 0); // read request
while (ret != 0)
  if(ret > 0)
    recv_data[len+ret] = 0;
    len += ret;
   else 
    sleep(1);
  
  ret = recv(socketfd, &recv_data[len], BUFSIZE, 0); // read request


我预计当客户端关闭连接时 read() 会返回 0,但事实并非如此。

【问题讨论】:

当数据不立即可用时,非阻塞套接字会引发错误。它的基本意思是“稍后再试”。您可以使用select 来确定套接字是否已准备好进行读/写,或者不使用非阻塞套接字。如果您从未获得更多数据,则服务器可能未正确发送数据。提供可重现的示例以进一步调试。 "如果您从未获得更多数据,那么服务器可能没有正确发送数据" - 或者,更有可能的是,您没有读取/处理数据正确,因此在达到请求结束时不知道何时停止读取。显示的代码绝对不是从 XMLHttpRequest 客户端读取请求的正确方法。 “所以当请求结束时不知道何时停止阅读。” - 没错,但我也不知道什么时候需要阅读更多。 read() 的返回值似乎没有帮助。是否有我缺少的套接字选项或内容标题技巧? 【参考方案1】:

GET 请求只有头部而没有主体(嗯,几乎总是如此),因此,只要您拥有请求头部,您就会拥有客户端发送的所有内容,并且当您阅读整个请求头部时,您就会知道读取一个空白行,即两次返回(并且不迟早)。

如果客户端只发送了一部分,没有空行,你应该等待其余的。如果时间过长,我会设置一个超时并拒绝整个请求。

顺便说一句,仍然有浏览器,也许还有一些代理,其 URL 长度限制约为 2000 个字符。

【讨论】:

以上是关于C 套接字服务器:读取所有未知长度 XMLHttpRequest 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在C中动态分配的从文件读取的未知长度字符串(必须防止从文件中读取数字)

未捕获(承诺)TypeError:无法读取未定义的属性“长度”

DataReader - 读取长度未知的数据

C:你如何通过套接字读取和解包消息?

HTTPS 通信失败,jdk 1.6(32 位客户端)与 jdk 1.8(64 位)服务器:读取:未知 3.3 警报,长度 = 2

C#字符串长度错误