Note that there are some explanatory texts on larger screens.

plurals
  1. POSimple http server in C, multiple process not work properly
    text
    copied!<h2>Solved by adding fseek(file, 0, 0) once finished a repsonse.</h2> <p>I am programming a very simple http server, which will reply any GET request with a HTTP response. The response content will be passed to the program when it started, e.g. </p> <pre><code>$./server test1.html 999 </code></pre> <p>This command will open listen to 999 port, if got any GET request, the server will send "http headers" + "content of test1.html" to the requesting client.</p> <p>The fork() part doesn't work well, I can get the correct response for the first time, but after that, new clients won't receive anything from the server (cookies and cache are cleared).</p> <p>I debugged, the program would fprintf() the correct content, the problem is "client won't receive them". </p> <p>Below is my code, there is a function called get_mime_type(), you can ignore it and set mime to "text/html" or whatever you want.</p> <p>Any hints?</p> <p>BTW, I also want to avoid using multiple process to handle multiple connections, any idea on implementing a single-process, single-threaded http server?</p> <pre><code>#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;unistd.h&gt; #include &lt;errno.h&gt; #include &lt;string.h&gt; #include &lt;sys/types.h&gt; #include &lt;sys/socket.h&gt; #include &lt;sys/wait.h&gt; #include &lt;sys/stat.h&gt; #include &lt;netinet/in.h&gt; #include &lt;netdb.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;signal.h&gt; #include &lt;dirent.h&gt; #include &lt;time.h&gt; #include &lt;unistd.h&gt; #define BACKLOG 1024 #define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" #define DEBUG 1 /** * Get the mime type of a file * * Pre-Condition: 1. mime-types.tsv is in current directory, and it is correct * 2. file exists and all right */ char *get_mime_type(char *filename) { // Get extension char *ext; // file extension if ((ext = strrchr(filename, '.')) == NULL) { fprintf(stderr, "Error: Cannot get the extension of \"%s\".\n", filename); exit(1); } ext += sizeof(char); // get rid of the `.' // Read in mime-types.tsv and try to get the mime-type regarding to current file's extension // No detailed error-checking according to pre-condition FILE *mime_file; mime_file = fopen("mime-types.tsv", "r"); int BUF_MAX = 1024; char *buffer = (char *)malloc(sizeof(char) * BUF_MAX); char *mime; fgets(buffer, BUF_MAX, mime_file); int linum = atoi(buffer); while (linum-- &gt; 0) { fgets(buffer, BUF_MAX, mime_file); if ((strncasecmp(buffer, ext, strlen(ext))) == 0) { buffer[strlen(buffer) - 1] = '\0'; mime = buffer; while (*mime != ' ') { mime += sizeof(char); } mime += sizeof(char); free(buffer); // TODO: is this all right? fclose(mime_file); return mime; } } free(buffer); fclose(mime_file); return NULL; } void sigchld_handler(int s) { while(waitpid(-1, NULL, WNOHANG) &gt; 0); } /** * Get sockaddr, IPv4 or IPv6: */ void *get_in_addr(struct sockaddr *sig_action) { if (sig_action-&gt;sa_family == AF_INET) { return &amp;(((struct sockaddr_in*)sig_action)-&gt;sin_addr); } return &amp;(((struct sockaddr_in6*)sig_action)-&gt;sin6_addr); } /** * Open a socket and listen to it, send information once got requests */ int run_server(char *filename, char *port) { // open file and get its information FILE *file; struct stat statbuf; if (stat(filename, &amp;statbuf) &lt; 0) { fprintf(stderr, "Error: Unable to open \"%s\".\n", filename); exit(1); } if (S_ISDIR(statbuf.st_mode)) { fprintf(stderr, "Error: %s is a directory, expected a file.\n", filename); exit(1); } if ((file = fopen(filename, "r")) == NULL) { fprintf(stderr, "Error: Unable to open \"%s\".\n", filename); exit(1); } char *mime = get_mime_type(filename); if (mime == NULL) { fprintf(stderr, "Error: the type of \"%s\" cannot be recognised.\n", filename); exit(1); } int file_len = S_ISREG(statbuf.st_mode) ? statbuf.st_size : -1; time_t mtime = statbuf.st_mtime; // last modified time, used in header // init sock int server_fd, client_fd; struct addrinfo hints, *server_info, *p_server_info; struct sockaddr_storage client_addr; socklen_t sock_in_size; struct sigaction sig_action; int yes = 1; char client_ip[INET6_ADDRSTRLEN]; int return_value; // store return value of getaddrinfo memset(&amp;hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ((return_value = getaddrinfo(NULL, port, &amp;hints, &amp;server_info)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(return_value)); return 1; } // TODO ? free hints? free p_server_info // bind for (p_server_info = server_info; p_server_info != NULL; p_server_info = p_server_info-&gt;ai_next) { if ((server_fd = socket(p_server_info-&gt;ai_family, p_server_info-&gt;ai_socktype, p_server_info-&gt;ai_protocol)) == -1) { perror("server: socket"); continue; } if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &amp;yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } if (bind(server_fd, p_server_info-&gt;ai_addr, p_server_info-&gt;ai_addrlen) == -1) { close(server_fd); perror("server: bind"); continue; } break; } if (p_server_info == NULL) { // not bind fprintf(stderr, "server: failed to bind\n"); return 2; } freeaddrinfo(server_info); // listen if (listen(server_fd, BACKLOG) == -1) { perror("listen"); exit(1); } // reap dead processes sig_action.sa_handler = sigchld_handler; sigemptyset(&amp;sig_action.sa_mask); sig_action.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &amp;sig_action, NULL) == -1) { perror("sigaction"); exit(1); } printf("server: waiting for connections...\n"); while (1) { sock_in_size = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *) &amp;client_addr, &amp;sock_in_size); if (client_fd == -1) { perror("accept"); continue; } inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&amp;client_addr), client_ip, sizeof(client_ip)); printf("server: got connection from %s\n", client_ip); if (!fork()) { // child process close(server_fd); FILE* response = fdopen(client_fd, "a+"); // response file // response header time_t now; char timebuf[128]; now = time(NULL); strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&amp;now)); fprintf(response, "HTTP/1.1 200 OK\r\n"); fprintf(response, "Server: server\r\n"); fprintf(response, "Date: %s\r\n", timebuf); strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&amp;mtime)); fprintf(response, "Last-Modified: %s\r\n", timebuf); fprintf(response, "Content-Type: %s\r\n", mime); fprintf(response, "Content-Length: %d\r\n", file_len); fprintf(response, "Connection: close\r\n"); fprintf(response, "\r\n"); // response content char buf[1024]; int n; while ((n = fread(buf, 1, sizeof(buf), file)) &gt; 0) { fwrite(buf, 1, n, response); } fclose(response); fclose(file); close(client_fd); exit(0); } } fclose(file); close(server_fd); return 0; } int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: $server filename port\n"); exit(1); } char *filename = argv[1]; char *port = argv[2]; return run_server(filename, port); } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload