An answer:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
    int listenfd, clientfd, len;
    struct sockaddr_in r, q;
    static char *files[3] = { "1", "2", "3" };
    int whichfile = 0;
    FILE *fp;
    int c;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return(1);
    }

    memset(&r, '\0', sizeof r);
    r.sin_family = AF_INET;
    r.sin_addr.s_addr = INADDR_ANY;
    r.sin_port = htons(1234);
    if (bind(listenfd, (struct sockaddr *)&r, sizeof r) < 0) {
        perror("bind");
        return(1);
    }
    if (listen(listenfd, 5)) {
        perror("listen");
        return(1);
    }

    while (1) {
        len = sizeof q;
        if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
            perror("accept");
            return(1);
        }
        if ((fp = fopen(files[whichfile], "r")) == NULL) {
            perror(files[whichfile]);
        } else {
            while ((c = getc(fp)) != EOF) {
                if (c == '\n') {
                    write(clientfd, "\r\n", 2);
                } else {
                    char cc = c;
                    write(clientfd, &cc, 1);
                }
            }
            fclose(fp);
            whichfile = (whichfile + 1) % 3;
        }
        close(clientfd);
    }
}

Note: write()ing a single character at a time will make for a really slow program. In real life, buffering would be essential. I would use fdopen() to make a "FILE *" out of the socket (see the fdopen man page), then write to it with putchar, then fclose() it to close the socket.
Actually, this program is not any longer than the above — in fact, it's shorter. See below.

Note 2: When we go to do the write("\r\n"), passing sizeof "\r\n" as the length parameter would be quite wrong, because that size would be 3. If you're not sure why, you should think about it and/or ask.

Note 3: When we go to write the character 'c', we cannot call write(clientfd, &c, 1), because c is an int, not a char. On "big-endian" machines, in fact, this would write a zero byte every time in the above loop (again, figure out why!). We also cannot make the 'c' variable be a char. This is because getc() returns -1 for EOF, and returns 255 if there is a 255 byte read in; as an eight bit value, these are identical, which is a big problem. Alternately put, getc() can return any of the 256 possible eight-bit values OR it can return -1 for EOF; this is 257 possible values, which cannot be stored in an 8-bit variable, because eight bits can be in only 28 possible states.


Here's the fdopen version, which will be faster for significantly-sized files or for a significant number of requests.

(Note, however, that this strategy is not suitable for your assignment four. In the case below, the buffering isn't a problem because we're fclosing the file anyway, which flushes all buffers.)

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
    int listenfd, clientfd, len;
    struct sockaddr_in r, q;
    static char *files[3] = { "1", "2", "3" };
    int whichfile = 0;
    FILE *fp, *ofp;
    int c;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return(1);
    }

    memset(&r, '\0', sizeof r);
    r.sin_family = AF_INET;
    r.sin_addr.s_addr = INADDR_ANY;
    r.sin_port = htons(1234);
    if (bind(listenfd, (struct sockaddr *)&r, sizeof r) < 0) {
        perror("bind");
        return(1);
    }
    if (listen(listenfd, 5)) {
        perror("listen");
        return(1);
    }

    while (1) {
        len = sizeof q;
        if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
            perror("accept");
            return(1);
        }
        if ((fp = fopen(files[whichfile], "r")) == NULL) {
            perror(files[whichfile]);
            close(clientfd);
        } else if ((ofp = fdopen(clientfd, "w")) == NULL) {
            fprintf(stderr, "can't allocate stdio data\n");
            close(clientfd);
        } else {
            while ((c = getc(fp)) != EOF) {
                if (c == '\n')
                    putc('\r', ofp);
                putc(c, ofp);
            }
            fclose(fp);
            fclose(ofp);  /* which also does close(clientfd) */
            whichfile = (whichfile + 1) % 3;
        }
    }
}


[press 'back' in your web browser to return to where you were in the exam]