通过C套接字发送图像

时间:2014-08-20 17:33:19

标签: c image sockets

void callback (struct Request req) {

    char buffer[8196];
    int file;
    bzero(buffer, sizeof(buffer));

    if(!strncmp(req.method, "GET", 3)){

        if (!strcmp(req.path, "/")){

            memcpy(buffer, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n", 44);
            response(buffer);

            int file = open("./static/index.html", O_RDONLY);
            long ret;

            while ((ret = read(file, buffer, 8196)) > 0) {
                response(buffer);   
            }

        }else if (!strcmp(req.path, "/login")){

            memcpy(buffer, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n", 44);
            response(buffer);

            int file = open("./static/login.html", O_RDONLY);
            long ret;

            while ((ret = read(file, buffer, 8196)) > 0) {
                response(buffer);   
            }

        }else{

            char path[80];
            char header[240];

            sprintf(path, "./static%s", req.path);
            int ext = readFile(path, buffer);
            char type[15];
            struct stat st;

            if(!ext){
                strcpy(type, "text/html\0");
            }else if(ext == 1){
                strcpy(type, "text/javascript\0");
            }else if(ext == 2){
                strcpy(type, "text/css\0");
            }else if(ext == 3){
                strcpy(type, "image/jpeg\0");
            }

            stat(path, &st);
            sprintf(header, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", type, st.st_size);
            response(header);

            int file = open(path, O_RDONLY);
            long ret;

            while ((ret = read(file, buffer, 8196)) > 0) {
                buffer[8196-1] = 0;
                response(buffer);   
            }

        }

    }else if(!strncmp(req.method, "POST", 4)){

    }

    bzero(buffer, sizeof(buffer));

};

void response (char message[]) {
    write(clientsocket, message, strlen(message));
};

这是我用C编码的HTTP服务器的代码。每次套接字收到请求时都会执行回调函数。 struct Request有2 char [],方法可以是POSTGET,path包含请求的路径。服务器正确发送静态文件,如html,js和css文件。它正确地提供了bootstrap静态,但是当我发送文件时{}不是buffer[8196-1] = 0;时,该文件包含错误的字符。

我尝试发送.jpg图片,并且标题已正确发送,但文件不是。当我访问127.0.0.1:8080/image.jpg时,Firefox会显示错误,表示图像包含错误且无法显示,或只显示一堆字符,服务器将停止提供其余的js和css文件。

我阅读了所有与此相关的问题,但我没有提交解决方案。图像大小是1'1Mb,也许是问题?我想不是因为我以'件'发送数据,但我根本不确定。该程序没有显示有关该文件的错误,似乎是打开并正确读取。

这可能是一个标题问题?其余的js和css文件都是在图像(else语句)的相同代码中发出的,广告没有任何问题,FF控制台显示每个文件的标题,而且他们是正确的所以我认为这不是问题。

感谢先进!

3 个答案:

答案 0 :(得分:3)

当客户端请求//login时,您不会发送任何Content-LengthTransfer-Encoding: chunked响应标头。没有它,客户端无法知道消息体是否存在或何时停止读取它,除非您在发送响应后立即关闭套接字连接(请参阅RFC 1945 Section 7.2.2RFC 2616 Section 4.4)。无论如何,您应该在做什么,因为您在没有Connection: keep-alive响应标头的情况下发送HTTP 1.0响应。

你根本不应该对buffer[8196-1]做任何事情。您应该告诉response() buffer中实际有多少字节,然后让buffer按原样发送到该字节数。不要依赖strlen()二进制数据,因为二进制数据可以包含嵌入的空值。

如果open()无法打开所请求的文件,您应该发送HTTP错误。

话虽如此,请尝试更像这样的事情:

void callback (struct Request req)
{
    char buffer[8196];
    int file;
    bzero(buffer, sizeof(buffer));

    if (strncmp(req.method, "GET") == 0)
    {
        char path[258];
        char header[240];
        char *type;

        if (strcmp(req.path, "/") == 0)
        {
            strcpy(path, "./static/index.html");
            type = "text/html";
        }
        else if (strcmp(req.path, "/login") == 0)
        {
            strcpy(path, "./static/login.html");
            type = "text/html";
        }
        else
        {
            sprintf(path, "./static%.258s", req.path);
            // or: snprintf(path, 258, "./static%s", req.path); 

            int ext = determineFileType(path);
            switch (ext)
            {
                case 0: // HTML
                    type = "text/html";
                    break;
                case 1: // Javascript
                    type = "text/javascript";
                    break;
                case 2: // CSS
                    type = "text/css";
                    break;
                case 3: // JPG
                    type = "image/jpeg";
                    break;
                default: // something else
                    type = "application/octet-stream";
                    break;
            }
        }

        int file = open(path, O_RDONLY);
        if (file == -1)
        {
            // send different errors depending on the failure, like 404 if the file is not found, etc...
            if (errno == ENOENT) {
                strcpy(header, "HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n");
            } else {
                strcpy(header, "HTTP/1.0 500 Internal Error\r\nContent-Length: 0\r\n\r\n");
            }
            response(header, strlen(header));
        }
        else
        {
            struct stat st;
            stat(path, &st);

            sprintf(header, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", type, st.st_size);
            response(header, strlen(header));   

            long ret;
            while ((ret = read(file, buffer, sizeof(buffer))) > 0)
                response(buffer, ret);   

            close(file);
        }
    }
    else if (strncmp(req.method, "POST") == 0)
    {
        //...
    }
}

void response (void *message, int msglen)
{
    char *msg = (char*) message;

    while (msglen > 0)
    {
         int len = write(clientsocket, msg, msglen);
         if (len <= 0) return;
         msg += len;
         msglen -= len;
    }
}

答案 1 :(得分:2)

write()中对response()的调用不一定会将完整的缓冲区写入套接字。

如果操作系统已经有很多数据缓冲,那么它可能只会在缓冲区中写入一部分数据,而您需要另外(或多次)调用write()来传输所有内容。要查看传输了多少数据,请查看write()的返回值。它返回写入的数字。

此外,您使用strlen()来确定要传输的数据的长度,但图像不是字符串。它们可以包含零字节,strlen()不会产生有用的结果。更好地将数据长度明确地传递给response()函数。

此外,在读取图像文件时,buffer[8196-1] = 0将用零覆盖图像的字节。

答案 2 :(得分:2)

HTTP是一个复杂的协议(规范有数百页)。因此,最好使用一些现有的库。 在服务器端(在您的应用程序中嵌入一些Web服务器),使用例如libonion。 在客户端(在您的应用程序中发出HTTP请求),请使用例如libcurl

顺便说一句,你可能strace(1)你的程序可以理解真正执行的syscalls(2)