1

我正在尝试编写一个 Web 服务器,但是使用我的代码我得到了“打开失败”。应该在浏览器中打开的 html 文档 (ht.html)。

我的代码是:

#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10


int main(int argc, char *argv[])
{
  int s, b, l, fd, sa, bytes, on = 1;
  char buf[BUF_SIZE]; //buffer for outgoing file
  struct hostent *h; //info about server
  struct sockaddr_in channel; //holds IP address

  //Build address structure to bind to socket
  memset(&channel, 0, sizeof(channel)); //zero channel
  channel.sin_family = AF_INET;
  channel.sin_addr.s_addr = htonl(INADDR_ANY);
  channel.sin_port = htons(SERVER_PORT);


  //Passive open. Wait for connection
  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
  if (s < 0) fatal("socket failed");
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));


  b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
  if (b < 0) fatal("bind failed");

  l = listen(s, QUEUE_SIZE);        /* specify queue size */
  if (l < 0) fatal("listen failed");


  /* Socket is now set up and bound. Wait for connection and process it. */
  while(1) {
    sa = accept(s, 0, 0);       /* block for connection request */
    if (sa < 0) fatal("accept failed");

    read(sa, buf, BUF_SIZE);    /* read file name from socket */

    /* Get and return the file. */

    fd = open(buf, O_RDONLY);   /* open the file to be sent back */
    if (fd < 0) fatal("open failed");

    while(1){
      bytes = read(fd, buf, BUF_SIZE); /* read from file */
      if (bytes <= 0) break;         /* check for end of file */
      write(sa, buf, bytes);              /*write bytes to socket*/
    }

    close(fd); //close file
    close(sa); //close connection

  }
}

fatal(char*string)
{
  printf("%s", string);
  exit(1);
}

我的错误在哪里?或者必须添加什么?

4

4 回答 4

1

也许您可以从输出从套接字接收到的数据开始,或者至少在调试器中运行,否则一切都将在黑暗中运行,您不知道发生了什么。在下面的代码中,我添加了一个printf打印我们从 Web 浏览器获得的内容。

也像其他人指出的那样,很高兴知道errno试图告诉我们什么。perror使用+有点尴尬/烦人exit,所以在 Linux 和 BSD 中你可以使用err(3)warn(3)。err 将打印 errno 消息然后退出,而 warn 只会打印 errno 消息而不退出,我fatal用这些替换了你的函数。

Web 浏览器很可能会发送GET /ht.html HTTP/1.1\r\n,这就是您尝试打开的内容。为了打开文件,我们需要提取ht.html部分。我在下面更新了您的代码,现在strchr(3)strstr(3)用于提取ht.html.

我们还需要发送一个 HTTP 响应代码并告诉 Web 浏览器我们要发送 HTML,这就是发送 HTML 的原因HTTP/1.1 200 OK。请记住,所有 HTTP 标头都需要用\r\n(回车 - 换行)分隔。您将在RFC 2616中找到有关 HTTP 协议的更多信息。

#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>

#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10

int main(int argc, char *argv[])
{
  int s, b, l, fd, sa, bytes, on = 1;
  char buf[BUF_SIZE]; /* buffer for outgoing file */
  char *p, *endp, *cp;

  struct sockaddr_in channel; /* holds IP address */

  /* Build address structure to bind to socket */
  memset(&channel, 0, sizeof(channel)); /* zero channel */
  channel.sin_family = AF_INET; /* ipv4 */
  channel.sin_addr.s_addr = htonl(INADDR_ANY); /* 0.0.0.0 */
  channel.sin_port = htons(SERVER_PORT);


  /* Passive open. Wait for connection */
  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
  if (s < 0) err(1, "socket failed");
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

  b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
  if (b < 0) err(1, "bind failed");

  l = listen(s, QUEUE_SIZE);        /* specify queue size */
  if (l < 0) err(1, "listen failed");


  /* Socket is now set up and bound. Wait for connection and process it. */
  while(1) {
    sa = accept(s, NULL, NULL);       /* block for connection request */
    if(sa < 0) {
      warn("accept failed");
      continue;
    }

    bytes = 0;
    endp = buf + sizeof(buf); /* pointer to end of buf */
    cp = NULL;
    buf[0] = '\0';
    /* read first line from socket */
    /* should be "GET /[file] HTTP/1.1" */
    for(p = buf; (bytes = read(sa, p, endp - p)) > 0; p += bytes) {
      p[bytes] = '\0'; /* read(2) doesn't NUL terminate buf */
      if((cp = strchr(p, '\r')) != NULL) /* break at first carriage return */
        break;
    }
    printf("incoming request %lu bytes:\n%s\n", strlen(buf), buf);
    /* no carrige return or no "GET /" was found */
    if(cp == NULL || strstr(buf, "GET /") != buf) { 
      warnx("incomplete request");
      close(sa);
      continue;
    }
    *cp = '\0'; /* replace '\r' with '\0' */
    p = buf + sizeof("GET /") - 1; /* point to after "GET /" */
    cp = strchr(p, ' '); /* find " HTTP/1.1" */
    if(cp == NULL) {
      warnx("HTTP version was not found");
      close(sa);
      continue;
    }
    *cp = '\0'; /* replace ' ' with '\0' */


    /* Get and return the file. */
    fd = open(p, O_RDONLY);   /* open the file to be sent back */
    if(fd < 0) {
      warn("open failed: %s", p);
      close(fd);
      close(sa);
      continue;
    }

    /* Send HTTP header */
    /* Should probably also send Content-Length: <sizeof file>, */
    /* this can be checked using fstat(2) */
    write(sa, "HTTP/1.1 200 OK\r\n" \
      "Content-Type: text/html;charset=UTF-8\r\n\r\n", 58);
    while(1) {
      bytes = read(fd, buf, sizeof(buf)); /* read from file */
      if (bytes <= 0) break;         /* check for end of file */
      write(sa, buf, bytes);              /*write bytes to socket*/
    }

    close(fd); /* close file */
    close(sa); /* close connection */
  }
  return 0;
}

要从您的 Web 浏览器连接到您的 HTTP 服务器,请访问:http://127.0.0.1:12345/ht.html

于 2013-02-23T18:40:19.727 回答
0
  1. 试着看看ERRNO。

    if (fd < 0) perror("open failed");
    
  2. 试试看buf。

    if (fd < 0){ 
      printf("%s\n", buf);
      perror("open failed");
    }
    
  3. 试着这样看 buf:

    if (fd < 0){
      for(i=0;i<strlen(buf);i++) 
         printf("%d", buf[i]);
      perror("open failed");
    }
    

这足以理解错误,因为您的应用程序根本没有打开文件。

于 2013-02-23T18:48:03.543 回答
0

尝试添加一些调试消息或使用调试器运行。

我认为问题在于传递给 open 语句的缓冲区。看起来 buf 没有用零初始化,也没有由“读取”终止的 NULL。

n = read(sa, buf, BUF_SIZE);
buf[n] = '\0';

通常,在使用 read 时,应该在循环中调用它,直到返回 0 或 -1。它可能只填充缓冲区的一小部分。

于 2013-02-23T18:50:11.343 回答
0

您从浏览器中读取的内容是 HTTP 请求。

您将需要对此进行解码 - 因此请阅读HTTP规范

HTTP 请求的示例可以在这里找到

于 2013-02-24T13:27:12.163 回答