salian
salian

Reputation: 37

socket server - send data to client

I make server in c by socket. Client send request to server. Server parse it and send back data (html,png,jpg or bash script output). I have some questions about it.

When I read html file and send it to client. If file is large data are not send and browser reset connection The connection was reset How can i wait until all data are send? in this loop.

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

Is it possible to send image(png or jpg) same way like html, only change Content type in html header?

How it works if in html file are a tags with src="another.html" after click on it client send GET request?

How it works if in html file are img tags?

Last question what is the best way to close infinity loop server. In linux if I close it with CTRL C socket are not close.

If something else is wrong I will be grateful for your advice.

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

Upvotes: 1

Views: 1847

Answers (1)

Armali
Armali

Reputation: 19375

When I read html file and send it to client. If file is large data are not send and browser reset connection The connection was reset How can i wait until all data are send? in this loop.

While the loop you refer to has potential problems, there is likely not the answer to your question.
I could reproduce the issue with your code, and after some searching hit on the highly interesting post The ultimate SO_LINGER page, or: why is my tcp not reliable. The key part is this sentence from section 4.2.2.13 of RFC 1122:

If such a host issues a CLOSE call while received data is still pending in TCP, or if new data is received after CLOSE is called, its TCP SHOULD send a RST to show that data was lost.

Your program may (and in my tests, does) indeed have received data pending, because at the start of the conversation it calls read(client_socketfd, sprava, 255), thus, if the HTTP GET request is longer than 255 bytes, leaving part of the request pending to be read. Now at the end after all send data has been submitted to the OS by write, when close is called, the pending data is still there, and therefore the OS, by sending the mandated RST, aborts the connection immediately, possibly discarding any not yet transmitted send data in its buffers. So, we have the surprising situation that an omission to receive in the beginning leads to loss of transmit data in the end.
A quick and dirty fix would be to increase the size of sprava and the number of bytes to read so that the whole request is read - but what might be the maximum length of a request? The correct way is to read in a loop until the request is terminated by an empty line consisting of only \r\n.

Upvotes: 1

Related Questions