【问题标题】:how to send image files through http?如何通过http发送图像文件?
【发布时间】:2019-09-22 02:22:51
【问题描述】:

我正在学习套接字编程并尝试使用c 编写一个简单的http 服务器。我的程序可以正确加载html/css/javascript文件,但无法加载图像文件。例如html文件的网站图标favicon.ico<img>总是加载失败。我正在使用以下代码来构建我的简单服务器:

server.c:

#define CYAN(format, ...) \
  printf("\033[1;36m" format "\33[0m\n", ## __VA_ARGS__)

struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
socklen_t c_addr_size;
int s_sock;// server socket
int c_sock;// clinet socket

char buf[4096];// user agent
char msg[4096];// file content
char head[1024];// http header
char file[128];// which file requested

void init_server();
void read_request();
void send_file();

int main() 
{
  init_server();

  while (1) {
    c_sock = accept(s_sock, NULL, NULL);
    if (c_sock != -1) {
      int nread = recv(c_sock, buf, sizeof(buf), 0);
      read_request();// TODO

      CYAN("%d", nread);
      CYAN("%s", buf);

      send_file();
      close(c_sock);
    }
  }

  close(s_sock);
  return 0;
}

void init_server() 
{
  s_sock = socket(AF_INET, SOCK_STREAM, 0);
  assert(s_sock != -1);
  s_addr.sin_family = AF_INET;
  s_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  s_addr.sin_port = htons(8000);

  int res = bind(s_sock, (struct sockaddr*)&s_addr, sizeof(s_addr));
  if (res == -1) { perror("cannot bind"); exit(-1); }

  listen(s_sock, 10);// TODO

  c_addr_size = sizeof(c_addr);
}

void read_request()
{
  int buf_len = strlen(buf);
  int i = 0, j = 0;
  for (i = 0; i < buf_len - 10; i ++) {
    if (buf[i] == 'G' && buf[i + 1] == 'E' && buf[i + 2] == 'T') {// `GET` keyword
      i = i + 4;// skip space
      while (buf[i] != ' ') {
        file[j] = buf[i];
        j ++, i ++;
      }
      file[j] = '\0';
      CYAN("%s", file);
      return;
    }
  }
}

void send_file()
{
  if (strcmp(file, "/") == 0) {
    sprintf(file, "index.html");
    is_html = 1;
  } else {
    sprintf(file, "%s", file + 1);// skip `/`
  }

  FILE *fp = fopen(file, "r");

  // count file length
  int file_len = 0;
  while (fgets(msg, 1000, fp)) {// read by lines
    file_len += strlen(msg);
  }

  // send http header
  sprintf(head, 
      "HTTP/1.1 200 OK\n"
      // "Content-Type: text/html\n"
      "Content-Length: %d\n"
      "\n", file_len
      );

  // send file content
  CYAN("%d", fseek(fp, 0, SEEK_SET));
  memset(msg, 0, sizeof(msg));
  send(c_sock, head, strlen(head), 0);
  while (fgets(msg, 1000, fp)) {// read by lines
    send(c_sock, msg, strlen(msg), 0);
  }

  fclose(fp);
}

我对@9​​87654329@不熟悉,不知道在发送图片文件时是否应该更改http标头的内容。如何更正我的代码,谁能帮助我?

【问题讨论】:

  • 关于:while (fgets(msg, 1000, fp)) { 函数:fgets() 仅适用于文本文件。强烈建议使用fread(),因为它可以正确处理二进制文件
  • 关于:c_sock = accept(s_sock, NULL, NULL); if (c_sock != -1) { 始终在指示发生错误的语句之后立即处理错误。
  • OT:对于像这样的行:if (res == -1) { perror("cannot bind"); exit(-1); } 请遵循公理:每行只有一个语句,并且(最多)每个语句有一个变量声明。
  • HTTP 标头中的每一行都以 "\r\n" 而不是 '\n' 结尾标头行的结尾标有:"\r\n\r\n"(另外, HTTP 消息正文的结尾,如果存在任何正文,则以“\r\n\r\n”结尾
  • OT:关于:FILE *fp = fopen(file, "r");调用C库函数时,一定要检查错误,调用fopen()时返回值为NULL表示调用失败。当它失败时,调用perror( "fopen failed" ); 后跟:exit( EXIT_FAILURE );

标签: c sockets server


【解决方案1】:
FILE *fp = fopen(file, "r");

// count file length
int file_len = 0;
while (fgets(msg, 1000, fp)) {// read by lines
  file_len += strlen(msg);
}

没有。不。不。不。永远不要对二进制数据使用文本处理。

文件长度很简单。

FILE *fp = fopen(file, "rb");
fseek(fp, 0, SEEK_END);
long file_len = ftell(fp);
fseek(fp, 0, SEEK_SET);

现在发送:

send_helper(c_sock, head, strlen(head), 0); /* send header */
while (file_len > 4096) {
    int delta = fread(msg, 4096, 1, fp);
    if (delta == 0) {
        /* handle error */
        fclose(fp);
        /* need to refactor here; c_sock is useless and needs to be closed */
        return;
    }
    send_helper(c_sock, msg, delta);
    file_len -= delta;
}
if (file_len > 0) {
    /* last chunk; fread only returns a short count on an actual error so no loop here */
    int delta = fread(msg, 4096, 1, fp);
    if (delta == 0) {
        /* handle error */
        fclose(fp);
        /* need to refactor here; c_sock is useless and needs to be closed */
        return;
    }
    send_helper(c_sock, msg, delta);
}
fclose(fp);

fread 不同,send 需要一个循环来确保发送所有字节。

void send_helper(int c_sock, char *msg, size_t size)
{
    while (size > 0)
    {
        ssize_t delta = sendto(c_sock, msg, size, 0);
        /*
         * not bothering to handle error well--
         * we'll just error a few more times and drop out of loop anyway.
         * You probably should come back and fix this later though
         */
        if (delta <= 0) return; 
        size -= (size_t)delta;
        msg += (size_t)delta;
    }
}

【讨论】:

    猜你喜欢
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    相关资源
    最近更新 更多