2013-02-23 145 views
1

我想写一个web服务器,但与代码我得到'打开失败'。应该在浏览器中打开的html文档(ht.html)。Socket编程C

我的代码是:

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

哪里是我的错误?或者需要添加什么?

+0

您是如何调试服务器的? – StoryTeller 2013-02-23 18:40:51

+1

'read(sa,buf,BUF_SIZE);'你希望read()有一个以null结尾的字符串。你不会得到一个:read()可以返回-1和BUF_SIZE之间的任何东西,包括。即使它会返回你的“文件名”的所有字符,它仍然不一定会被终止。 – wildplasser 2013-02-23 18:50:41

+0

你不是'开放失败',你是在对自己说'开放失败'。这没有用。调用perror(),这样你就会知道错误是什么。绝不要像这样压制错误信息。 – EJP 2013-02-23 22:05:43

回答

1

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

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

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

我们还需要发送HTTP响应代码并告诉浏览器我们要发送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; 
} 

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

+0

什么是打开它的代码? – Samuel 2013-02-23 21:22:26

+0

谢谢。我很感激帮助。 – Samuel 2013-02-24 21:31:08

+0

它还没有完全运作,但你的帮助给了我一些头。 – Samuel 2013-02-25 21:18:00

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

这将是足够理解的错误,因为您的应用程序根本不打开该文件。

+0

你是说要用1,2,3代替fd = open(buf,O_RDONLY);/*打开要发回的文件*/ if(fd <0)致命(“打开失败”); – Samuel 2013-02-23 21:15:56

+0

是的。这只是一些调试信息,但你会看到一个错误原因。 – someuser 2013-02-24 02:40:50

0

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

我认为这个问题依赖于传递给open语句的缓冲区。它看起来像buf没有初始化为零,也不是由“read”终止NULL。

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

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

+0

我正在使用Linux,这就是为什么。 – Samuel 2013-02-23 21:18:41

0

你从浏览器中读取的东西是一个HTTP请求。

您需要对此进行解码 - 所以请阅读HTTP的spec

可以找到HTTP请求的示例here