用C编写Web服务器

3

我正在尝试用C语言编写一个简单的Web服务器。目前,我能够接受连接并完整地接收消息。但是,根据HTTP/1.0协议,当遇到"\r\n\r\n"序列时,我希望能够向客户端发送信息。然而,当我使用Telnet测试我的服务器时,当我输入"\r\n\r\n"时,服务器不会做任何事情,直到我在客户端上按下"^]"。我已经对比了Apache,发现Apache没有这个问题。因此,我希望得到一些关于如何模仿Apache行为的信息。我的代码如下,但请记住我还没完成,也没有实现很多错误检查。

main(){
        int sock_fd = 0;
        int client_fd = 0;
        struct sockaddr_in socket_struct;
        /*Creates the socket*/
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }/*Ends the socket creation*/

        /*Populates the socket address structure*/
        socket_struct.sin_family = AF_INET;
        socket_struct.sin_addr.s_addr=INADDR_ANY;
        socket_struct.sin_port =htons(port);

        if (bind(sock_fd, (struct sockaddr*) &socket_struct, sizeof(socket_struct)) < 0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the binding.

        if (listen(sock_fd, 5) <0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the listening function

        if ( (client_fd = accept(sock_fd, NULL, NULL)) <0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the accepting.
        while ( (size = read(client_fd, msg, 1024)) > 0)
        {
                //size = recv(client_fd, msg, 1024, MSG_PEEK|MSG_WAITALL);
                if ( (msg[size-4] == 13) && (msg[size-3] == 10)&&(msg[size-2] == 13) && (msg[size-1] == 10) )
                {
                        char* buffer = (char *)malloc(sizeof("The msg was: ")+ sizeof(msg));
                        sprintf(buffer, "The msg was: %s", msg);
                        send(client_fd, buffer, sizeof("The msg was: ")+ sizeof(msg), MSG_OOB);
                }

        }//ends the while loop for receiving data
        close(client_fd);
}

尝试使用printf()并查看它是否进入while循环内的if()条件。似乎在send()处存在问题。 - Aniket Inge
这并不是发送(send)的问题,因为在发布之前我已经做了printf()操作...不过还是谢谢。 - tpar44
1个回答

2

首先,这行代码是无法运行的:

while ( (size = read(client_fd, msg, 1024)) > 0)

这将接收消息块,并且每个接收到的块都会覆盖上一个块。可以像这样处理:
char msg[1024];
size_t pos = 0;
while (1) {
    assert(pos < sizeof(msg));
    ssize_t amt = read(client_fd, msg + pos, sizeof(msg) - pos);
    if (amt < 0)
        err(1, "read failed");
    if (amt == 0)
        break; // EOF
    pos += amt;
    char *e = strstr(msg, "\r\n\r\n");
    if (e) {
        size_t msglen = e - msg;
        /* Handle a complete message here */
    }
}

这样,当您接收到消息块时,它们会被写入缓冲区。 一旦您拥有"\r\n\r\n"序列,即使您可能分块获取它,也可以处理整个消息。
关键教训:在TCP中,数据包边界和消息边界可能完全不同,并且从read获得的大小可能仍然不同。 您必须通过查看数据本身而不是查看从read()返回的数据量(除了EOF,当read()返回0时发出信号)来找到消息的结尾。
注:我认为带外数据并不像您想象的那样。
send(client_fd, buffer, sizeof("The msg was: ")+ sizeof(msg), MSG_OOB);

带外数据是TCP的一个古怪功能,现代协议中几乎肯定应该避免使用。它的实现因平台而异,只能安全地发送一个字节的带外数据。

Telnet协议(以及建立在Telnet上的FTP)有时会使用它,但HTTP没有任何用处。


谢谢你的帮助!我已经改成使用recv而不是read来工作了,但你确实让我走上了正确的轨道,非常感谢你。 - tpar44
@tpar44:对于套接字,recvread是相同的(除了标志参数),所以可以随意使用任何一个。 - Dietrich Epp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接