套接字服务器 - 向客户端发送数据

时间:2015-12-09 18:26:55

标签: c sockets server

我通过socket在c中创建服务器。客户端向服务器发送请求。服务器解析它并发回数据(html,png,jpg或bash脚本输出)。我对此有一些疑问。

当我读取html文件并将其发送给客户端时。如果文件是大数据不发送和浏览器重置连接The connection was reset我怎么能等到所有数据都被发送?在这个循环中。

while ((ret = read(html, buff, 1023)) > 0)
{
    write(client_socketfd, buff, ret);
}

是否可以像html一样发送图像(png或jpg),只更改html标题中的内容类型?

如果html文件中的标签是src =" another.html"点击它后客户端发送GET请求?

如果在html文件中是img标签,它是如何工作的?

最后一个问题关闭无限循环服务器的最佳方法是什么。在Linux中如果我用CTRL C套接字关闭它就不会关闭。

如果出现其他问题,我将非常感谢您的建议。

int main(int argc, char *argv[])
{
    int result;
    int socket_desc, client_socketfd, c, read_size, buffer = 0;
    struct sockaddr_in server, client;
    char sprava[256];

    int arg;

    int port = 0;
    char *homeDir = NULL;
    //get command line arguments -p port -d home dir
    while ((arg = getopt(argc, argv, "p:d:")) != -1) {
        switch (arg) {
        case 'p':
            port = atoi(optarg);
            break;
        case 'd':
            homeDir = optarg;
            break;
        default:
            fprintf(stderr, "Please speicify -p port and -d home directiory\n");
            exit(1);
        }
    }

    if (port < 1500 || homeDir == NULL) {
        fprintf(stderr, "BAD arguments use: -p port(greather then 1500) -d home dir\n");
        exit(1);
    }

    //Create socket
    socket_desc = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1)
    {
        printf("Could not create socket");
        return 1;
    }

    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);

    //Bind
    if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        fprintf(stderr, "bind failed\n");
        exit(1);
    }

    printf("bind done\n");

    //Listen max 3
    listen(socket_desc, 3);

    //Accept and incoming connection
    int loop = 1;

    while (loop) {
        printf("Waiting for incoming connections...\n");
        c = sizeof(struct sockaddr_in);
        loop = 0; //only for testing, if everything run ok loop will be infinity
        client_socketfd = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
        if (client_socketfd < 0) {
            fprintf(stderr, "Accept failed\n");
            exit(1);
        }
        //In child proc we are sending data
        if (fork() == 0) {
            close(socket_desc);//we dont need desc in child
            bzero(sprava, 256);//all  on '\0'

        result = read(client_socketfd, sprava, 255);
        printf("Server read: %s\n", sprava);

        char* path;
        int kod = parser(sprava, &path);//in path is path to file
        if (kod == ERROR_FILE_TYPE)
        {
                printf("BAD request!!!!\n");
                shutdown(client_socketfd, SHUT_RDWR);
                close(client_socketfd);
            }
            if (kod == HTML || kod == BASH || kod == JPG || kod == PNG)
            {
                if (kod == BASH)
                {
                    FILE *pipe;
                    char *cmd = path;
                    strcat(cmd, " 2>&1");//error output send to pipe
                    printf("New command is=%s\n", cmd + 1);//we dont need first /
                    //open pipe without first /
                    pipe = popen(cmd + 1, "r");
                    if (pipe != NULL) {
                        char text[1035];
                        while (fgets(text, sizeof(text) - 1, pipe) != NULL) {
                            printf("output=%s", path);
                            write(client_socketfd, text, strlen(text));
                        }
                    }
                    pclose(pipe);
                }
                else if (kod == HTML)
                {
                    int html;
                    long len;
                    char buff[1024] = { 0 };
                    int ret;
                    printf("Try to open file=%s\n", path + 1);
                    html = open(path + 1, O_RDONLY);
                    if (html == -1) {
                        fprintf(stderr, "Error opening file\n");
                    }
                    len = (long)lseek(html, (off_t)0, SEEK_END);//len of file
                    lseek(html, (off_t)0, SEEK_SET);
                    sprintf(buff, "HTTP/1.1 200 OK\nServer: nweb/%d.0\nContent-Length: %ld\nConnection: close\nContent-Type: %s\n\n", 20, len, "text/html");
                    //send html header to client
                    printf("Length of file=%d\n", len);
                    write(client_socketfd, buff, strlen(buff));
                    printf("Header was send\n");
                    while ((ret = read(html, buff, 1023)) > 0)
                    {
                        printf("number of bytes read=%d\n", ret);
                        //write data to client,it will make connection reset
                        write(client_socketfd, buff, ret);
                    }
                }
                free(path);
            }

            shutdown(client_socketfd, SHUT_RDWR);
            close(client_socketfd);
            exit(0);

        }
        //in parent close client
        else {
            close(client_socketfd);
            wait(&wt);//this wait is only for testing
        }
    }
    close(socket_desc);
    return 0;
}

1 个答案:

答案 0 :(得分:0)

  

当我读取html文件并将其发送给客户端时。如果文件是大数据的话   不发送和浏览器重置连接The connection was reset如何   我可以等到所有数据都发送出去吗?在这个循环中。

虽然您提到的循环存在潜在问题,但您的问题很可能没有答案 我可以使用您的代码重现该问题,并在搜索了一篇非常有趣的帖子后The ultimate SO_LINGER page, or: why is my tcp not reliable。关键部分是来自section 4.2.2.13 of RFC 1122的这句话:

  

如果此类主机在接收到的数据仍然存在时发出CLOSE呼叫   在TCP中挂起,或者如果在调用CLOSE后收到新数据,则为   TCP应该发送一个RST来显示数据丢失。

您的程序可能(并且在我的测试中确实)已收到待处理数据,因为在对话开始时它调用read(client_socketfd, sprava, 255),因此,如果HTTP GET请求超过255个字节,则留下部分等待阅读的请求。现在最后,所有发送数据都已由write提交给操作系统,当调用close时,待处理数据仍然存在,因此操作系统通过发送强制RST中止连接立即,可能会丢弃其缓冲区中尚未传输的任何发送数据。因此,我们有一个令人惊讶的情况,即在开始时遗漏会导致最终丢失传输数据 一个快速而又脏的修复方法是将sprava的大小和字节数增加到read,以便读取整个请求 - 但是请求的最大长度是多少?正确的方法是循环中read,直到请求被仅由\r\n组成的空行终止。