0

我正在尝试使用 C 中的套接字编写一个基本的 HTTP 文件服务器,但有两个主要问题。

首先,当我尝试通过浏览器访问“ip:port/”时,我得到了 index.html 文件,但它完全是乱码。但是,当我访问“ip:port/not_in_directory”时,我的小自定义 404 消息会正常返回。如果我将默认文件设置为现有的 pdf 文件,乱码也会回来(pdf 乱码的长度)。

其次,当我尝试访问“ip:port/file_that_exists”时,我得到的是 404 而不是正确的文件。在调试时,我确实看到 scratch_pad 具有正确的文件名,没有多余或丢失的字符,但是 !access(scratch_pad, R_OK) 仍然为 0。即使 access() 需要一个 char* 是否也有一些必要的转换为文件名?

编辑:第二个问题已解决(我使用指向已释放缓冲区的指针作为文件名)。但是,浏览器获取的所有内容都是胡言乱语。我正在删除更多非必要的代码。

#define FILE_NOT_FOUND "HTTP/1.0 404 FILE NOT FOUND\r\n"
#define FILE_FOUND "HTTP/1.0 200 OK\r\n"
#define SERVER_NAME "Server: Test Server\r\n"
#define CONTENT_TYPE "Content-Type: text/html\r\n"
#define DATE "Date: "
#define MESSAGE_BREAK "\r\n"
#define NOT_FOUND_HTML "<!DOCTYPE html>\n<html>\n<title>404 Not Found</title>\n<body>\n<p>Nope</p>\n</body>\n</html>\r\n"

  if(!access(pulled_name, R_OK | W_OK)) {
      printf("file found on server\n");
      server_file = fopen(pulled_name, "rb+");
      fseek(server_file, 0L, SEEK_END);
      file_size = ftell(server_file);
      fseek(server_file, 0l, SEEK_SET);
      printf("opened successfully and set file_size to %i\n", file_size);
      getting_time = time(NULL);
      c_time = localtime(&getting_time);
      current_time = asctime(c_time);
      printf("set time for message\n");

  message = malloc((int) strlen(FILE_FOUND)
                + (int) strlen(SERVER_NAME)
                + (int) strlen(CONTENT_TYPE)
                + (int) strlen(DATE)
                + (int) strlen(current_time)
                + (int) strlen(MESSAGE_BREAK));

  printf("malloc'ed message\n");

  message_size = (int)(strlen(message)*sizeof(char));     
  memset(message, 0, message_size);
  strcpy(message, FILE_FOUND);
  strcat(message, SERVER_NAME);
  strcat(message, CONTENT_TYPE);
  strcat(message, DATE);
  strcat(message, current_time);
  strcat(message, MESSAGE_BREAK);
  printf("built message\n");

  printf("set message_size to %i\n", message_size);
  while(total_sent < message_size) {
    n = send(new_socket_fd, message, message_size, 0);
    if (n==-1) {
      printf("error sending message\n");
      break;
    }
    total_sent += n;
    printf("sent %i\n", total_sent);
  }
  free(message);

  total_sent = 0;
  while(total_sent < file_size) {
    n = send(new_socket_fd, server_file, file_size, 0);
    if (n==-1) {
      printf("error sending file\n");
      break;
    }
    total_sent += n;
    printf("sent %i\n", total_sent);
  }
  fclose(server_file);
  close(new_socket_fd);
  printf("closed client connection!\n");
4

4 回答 4

4

这个错误有几个例子:

memset(buffer, '\0', sizeof(buffer));

当第一个参数的类型是 achar*时,这意味着缓冲区不会填充空字符,而只会填充第一个sizeof(char*)字节(通常为 4 或 8)。在这种情况下,这意味着buffer不能以空值结尾(因为recv()不附加空字符)。两者都printf()要求strtok()字符串参数以空值结尾。通常会看到在printf("%s", p);使用非空终止字符串调用的字符串之后打印出垃圾字符。

memset()通过显式指定第一个参数指向的缓冲区的大小来修复所有调用:

const size_t BUFFER_SIZE      = 512;
const size_t SCRATCH_PAD_SIZE = 256;
buffer                        = malloc(BUFFER_SIZE);     /*Check return value*/
scratch_pad                   = malloc(SCRATCH_PAD_SIZE);/*to ensure success.*/
memset(buffer,      0, BUFFER_SIZE);
memset(scratch_pad, 0, SCRATCH_PAD_SIZE);

sizeof(char)保证是,所以1它可以从malloc()参数中省略。


其他滥用memset()

memset(message, '0', sizeof(message)); 
  • '0'是字符零,不为空
  • sizeof(message)是个sizeof(char*)

改成:

memset(message, 0, /* the actual size of message*/ );
于 2012-09-28T13:21:46.787 回答
1

您的代码中有几处看起来不太好。

这里:

   buffer = malloc(sizeof(char)*512);
   scratch_pad = malloc(sizeof(char)*256);

scratch_pad 是指向分配区域的指针。

但是随后您丢失了指向该区域的指针并将 scratch_pad 设置为指向缓冲区:

   printf("request:\n%s\n", buffer);
   scratch_pad = strtok(buffer, "/");

那么这里:

   if (!strncmp(scratch_pad, "HTTP", 4)) {
       scratch_pad = "index.html";
       printf("request was for root directory\n");
   }

您将 scratch_pad 指向一个常量字符串。

当你做了

free(scratch_pad);

我本以为所有的地狱都会放松。

你的标记化方法有点冒险。我看到的第一个结果是您会接受一个GETLOST方法作为 的同义词GET,并且您在处理路径时会遇到问题(例如GET /js/main.js HTTP/1.0):

scratch_pad = strtok(buffer, "/");
printf("pulling request type\n");

if (!strncmp(scratch_pad, "GET", 3)) {
  printf("this is a Get\n");
  scratch_pad = strtok(NULL, " ");
  printf("pulling file name: >%s<\n", scratch_pad);
  if (!strncmp(scratch_pad, "HTTP", 4)) {
    scratch_pad = "index.html";
    printf("request was for root directory\n");
  }
  printf("requested file: %s\n", scratch_pad);
}

至于access(),如果scratch_pad有正确的文件并且服务器在web根目录下运行,那么access()应该返回0。如果没有,验证的值errno

更新

正如我在上面所写的,scratch_pad是(或可能是)内部 buffer的指针。所以当你这样做时:

free(buffer);
if(!access(scratch_pad, R_OK)) {

可能会发生(不应该,至少不会持续几毫秒,但是嘿......)指向的区域buffer被破坏了。然后也scratch_pad将是任何点。

到那时,access(现在我想起来,这很可能是做垃圾处理的人)将处理垃圾,并拒绝继续。

要检查,只需执行以下操作:

if (access(scratch_pad, R_OK)) { printf("无法访问文件 '%s'\n", scratch_pad); } 别的 { ...

为避免这种情况,请以不同的方式标记化buffer

例如:

字符 *空格;字符 *http;

// 缓冲区是 GET /this/path/to/file/name HTTP/1.0... if (!strncmp(buffer, "GET ", 4)) // 不是 GET if (NULL == (space = strchr( buffer+4, ' '))) // 错误:没有第二个空格。if (!strncmp(space + 1, 'HTTP/1.', 7)) // 错误:GET 不以 HTTP/1.something 结尾 // 现在 buffer+4 和空格之间的所有缓冲区都是文件名len = 空间缓冲区 4;strncpy(scratch_pad, 缓冲区 + 4, len); // 终止字符串 scratch_pad[len] = 0x0;

// 现在 scratch_pad 包含以 / 结尾的文件名,但它是相对的 // 到错误的路径;我们希望它从我们的文档根目录开始。// 所以与其

#define DOCUMENT_ROOT "./" len = space-buffer-4; strcpy(scratch_pad, DOCUMENT_ROOT); strncpy(scratch_pad + strlen(DOCUMENT_ROOT), 缓冲区 + 4, len); len += strlen(DOCUMENT_ROOT); // 终止字符串。现在是:.//path/to/file scratch_pad[len] = 0x0; // 我们实际上应该在这里运行 realpath()...

于 2012-09-28T13:31:00.313 回答
0

看起来您的Date: ...标题没有换行终止,因此 MESSAGE_BREAK 被吞下。另外,我不确定为什么要为所有字符串长度添加 +1。

于 2012-09-28T13:22:28.330 回答
0

有两件事对我来说很奇怪:

  1. 为什么你总是在到达文件的真实内容之前发送 404 消息?
  2. 第二个 send() 错误,为什么要发送文件描述符?你的缓冲区在哪里?您应该提供缓冲区指针并在每个循环中更新它。

提示:gcc -Wall 是你的朋友。

于 2012-09-28T16:03:08.560 回答