【问题标题】:socket server - send data to client套接字服务器 - 向客户端发送数据
【发布时间】:2015-12-09 18:26:55
【问题描述】:

我通过套接字在 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 关闭它,socket 就不会关闭。

如果有其他问题,我将不胜感激您的建议。

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

【问题讨论】:

  • http RFC(规范)在这里:w3.org/Protocols(7230 到 7235)
  • 另外,您想针对您的每一个问题提出一个 SO 问题。
  • 另外^2 请注意,来自套接字的read()ing 不一定返回请求的字节数,但很少。读取次数不需要与写入次数相匹配。仔细阅读文档。
  • 最后也是为了帮自己一个忙总是测试函数调用的结果,至少在它们返回与以下代码相关的任何值的情况下。
  • 一个细节:不要做int c; ... ... (socklen_t*) &amp;c);,而只是socklen_t c; ... ... &amp;c);。想象一下如果int 的大小与socklen_t 的大小不同会发生什么。

标签: c sockets server


【解决方案1】:

当我读取 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 的空行终止。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-26
    • 2017-12-07
    • 2011-08-12
    • 2015-02-10
    • 2017-07-10
    • 2016-03-03
    • 2013-04-01
    • 1970-01-01
    相关资源
    最近更新 更多