如何通过http发送图像文件?

时间:2019-09-22 02:22:51

标签: c sockets server

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

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);
}

我对http不熟悉,也不知道在发送图像文件时是否应该更改http标头的内容。如何更正我的代码,有人可以帮助我吗?

1 个答案:

答案 0 :(得分:3)

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;
    }
}