Sockets https://tech.feedyourhead.at/tags/sockets en How to program a very simple webserver in C https://tech.feedyourhead.at/content/how-program-very-simple-webserver-c <span class="field field--name-title field--type-string field--label-hidden">How to program a very simple webserver in C</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Around 10 years ago I played with network-sockets in C. Now I just want to refresh my knowledge a bit and write a very simple webserver. This webserver just displays a simple site. the side is hardcoded, but close enough to&nbsp; see how it works...</p> <h2>Download the Source</h2> <pre> <code> git clone https://github.com/whotwagner/SimpleWebserv.git </code></pre> <h2><strong>HTTP-Basics:</strong></h2> <p>Every Webserver is talking the HTTP-protocol. The HTTP-protocol is a pretty simple request/response-protocol. You can try, and connect via telnet to any webserver and send a valid http-request. Lets try to connect to google and ask for the file "robots.txt":</p> <pre> <code> telnet www.google.com 80 Trying 173.194.113.82... Connected to www.google.com. Escape character is '^]'. GET /robots.txt HTTP/1.0 200 OK Vary: Accept-Encoding Content-Type: text/plain Last-Modified: Thu, 27 Nov 2014 07:03:43 GMT Date: Wed, 03 Dec 2014 15:17:59 GMT Expires: Wed, 03 Dec 2014 15:17:59 GMT Cache-Control: private, max-age=0 X-Content-Type-Options: nosniff Server: sffe X-XSS-Protection: 1; mode=block Alternate-Protocol: 80:quic,p=0.02 User-agent: * Disallow: /search Disallow: /sdch Disallow: /groups Disallow: /images Disallow: /catalogs Allow: /catalogs/about Allow: /catalogs/p? Disallow: /catalogues Allow: /newsalerts Disallow: /news Allow: /news/directory Disallow: /nwshp Disallow: /setnewsprefs? Disallow: /index.html? Sitemap: http://www.gstatic.com/culturalinstitute/sitemaps/www_google_com_culturalinstitute/sitemap-index.xml Sitemap: https://www.google.com/work/sitemap.xml Sitemap: https://www.google.com/intx/sitemap.xml Sitemap: http://www.google.com/hostednews/sitemap_index.xml Sitemap: http://www.google.com/maps/views/sitemap.xml Sitemap: http://www.google.com/sitemaps_webmasters.xml Sitemap: http://www.google.com/ventures/sitemap_ventures.xml Sitemap: http://www.gstatic.com/dictionary/static/sitemaps/sitemap_index.xml Sitemap: http://www.gstatic.com/earth/gallery/sitemaps/sitemap.xml Sitemap: http://www.gstatic.com/s2/sitemaps/profiles-sitemap.xml Sitemap: http://www.gstatic.com/trends/websites/sitemaps/sitemapindex.xml Sitemap: http://www.google.com/adwords/sitemap.xml Sitemap: http://www.google.com/drive/sitemap.xml Connection closed by foreign host. </code></pre> <p>First we sent "GET /robots.txt" to Google, and recieved then the HTTP-response. In this response we find the "HTTP-Code 200 OK" followed by the HTTP-Headers:</p> <pre> <code> HTTP/1.0 200 OK Vary: Accept-Encoding Content-Type: text/plain Last-Modified: Thu, 27 Nov 2014 07:03:43 GMT Date: Wed, 03 Dec 2014 15:17:59 GMT Expires: Wed, 03 Dec 2014 15:17:59 GMT Cache-Control: private, max-age=0 X-Content-Type-Options: nosniff Server: sffe X-XSS-Protection: 1; mode=block Alternate-Protocol: 80:quic,p=0.02 </code></pre> <p>The rest of the output is the content of the robots.txt-file. This was a very simple HTTP-Request without special headers. It is possible to send a multiline-http-request with headers:</p> <pre> <code> telnet www.google.com 80 Trying 173.194.113.84... Connected to www.google.com. Escape character is '^]'. GET /robots.txt HTTP/1.1 User-Agent: Telnet HTTP/1.1 200 OK Vary: Accept-Encoding Content-Type: text/plain Last-Modified: Thu, 27 Nov 2014 07:03:43 GMT Date: Wed, 03 Dec 2014 15:27:29 GMT Expires: Wed, 03 Dec 2014 15:27:29 GMT Cache-Control: private, max-age=0 X-Content-Type-Options: nosniff Server: sffe X-XSS-Protection: 1; mode=block Alternate-Protocol: 80:quic,p=0.02 Transfer-Encoding: chunked 1e5f User-agent: * Disallow: /search Disallow: /sdch Disallow: /groups Disallow: /images Disallow: /catalogs Allow: /catalogs/about Allow: /catalogs/p? Disallow: /catalogues Allow: /newsalerts Disallow: /news Allow: /news/directory Disallow: /nwshp Disallow: /setnewsprefs? Disallow: /index.html? Sitemap: http://www.gstatic.com/culturalinstitute/sitemaps/www_google_com_culturalinstitute/sitemap-index.xml Sitemap: https://www.google.com/work/sitemap.xml Sitemap: https://www.google.com/intx/sitemap.xml Sitemap: http://www.google.com/hostednews/sitemap_index.xml Sitemap: http://www.google.com/maps/views/sitemap.xml Sitemap: http://www.google.com/sitemaps_webmasters.xml Sitemap: http://www.google.com/ventures/sitemap_ventures.xml Sitemap: http://www.gstatic.com/dictionary/static/sitemaps/sitemap_index.xml Sitemap: http://www.gstatic.com/earth/gallery/sitemaps/sitemap.xml Sitemap: http://www.gstatic.com/s2/sitemaps/profiles-sitemap.xml Sitemap: http://www.gstatic.com/trends/websites/sitemaps/sitemapindex.xml Sitemap: http://www.google.com/adwords/sitemap.xml Sitemap: http://www.google.com/drive/sitemap.xml Connection closed by foreign host. </code></pre> <p>In this example we sent a multiline request containing the User-Agent-Header:</p> <pre> <code> GET /robots.txt HTTP/1.1 User-Agent: Telnet </code></pre> <p>This means, our webserver has to handle simple requests as well as more complex multiline requests.</p> <h2><strong>Sockets</strong></h2> <p>In order to have a functional Webserver we have to create a network-socket and make it listening for connections. If a client connects it has to accept the connection and start the http-routine. It would be nice if our webserver would be able to handle more than just one client at once, so we have to think about this too..</p> <p>So first let's create a TCP-Socket:</p> <pre> <code> sockfd = socket(AF_INET,SOCK_STREAM,0); </code></pre> <p>This socket we want to bind on a specific port:</p> <pre> <code> memset(&amp;servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); /* bind sockt to address + port */ if(bind(sockfd, (struct sockaddr*)&amp;servaddr,sizeof(servaddr)) != 0) { perror("bind() failed.."); close(sockfd); exit(EXIT_FAILURE); } </code></pre> <p>..and start listening:</p> <pre> <code> listen(sockfd,LISTENQ); </code></pre> <p>Now it starts to get a bit tricky. Our server has to accept the connections of the clients, then it has to fork into a subprocess and this subprocess will do the http-stuff. This sounds not that complicated, but we have to take care of our child-processes too, otherwise we might have some zombie-processes in some cases.</p> <p>Let's start with the child-handler:</p> <pre> <code> void sigchld_handler(int signo) { int status; pid_t pid; /* -1 means that we wait until the first process is terminated WNOHANG tells the kernel not to block if there are no terminated child-processes. */ while( (pid = waitpid(-1,&amp;status,WNOHANG)) &gt; 0) { clientcount--; printf("%i exited with %i\n", pid, WEXITSTATUS(status)); } return; } </code></pre> <p>Now we connect this child-handler with our signal, so that the parent is waiting until the child-processes are terminated:</p> <pre> <code> memset(&amp;sa, 0, sizeof(sa)); sa.sa_handler = sigchld_handler; sigaction(SIGCHLD, &amp;sa, NULL); </code></pre> <p>Now let's complete our server-socket-construct:</p> <pre> <div class="geshifilter"><pre class="c geshifilter-c" style="font-family:monospace;"><span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> connfd <span style="color: #339933;">=</span> accept<span style="color: #009900;">&#40;</span>sockfd<span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> sockaddr <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>cliaddr<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;</span>clilen<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> childpid <span style="color: #339933;">=</span> fork<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>childpid <span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/perror.html"><span style="color: #000066;">perror</span></a><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;fork() failed&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/exit.html"><span style="color: #000066;">exit</span></a><span style="color: #009900;">&#40;</span>EXIT_FAILURE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span> &nbsp; <span style="color: #808080; font-style: italic;">/* let's start our child-subprocess */</span> <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span> childpid <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> close<span style="color: #009900;">&#40;</span>sockfd<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/exit.html"><span style="color: #000066;">exit</span></a><span style="color: #009900;">&#40;</span>handle_client<span style="color: #009900;">&#40;</span>connfd<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span> &nbsp; <span style="color: #808080; font-style: italic;">/* continue our server-routine */</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #000066;">printf</span></a><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Client has PID %i<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span>childpid<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp; close<span style="color: #009900;">&#40;</span>connfd<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span></pre></div></pre> <p>&nbsp;</p> <h3>Where to go from here..</h3> <p>In this article i just showed the most important routines to implement a simple Web-Server in C. Of course there is much more work for a working Webserver, but I think I gave a glimpse of how it might works. At the following link there is the full source to this example(with a lot of comments): <a href="https://github.com/whotwagner/SimpleWebserv">https://github.com/whotwagner/SimpleWebserv</a></p> <p>&nbsp;</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/hoti" typeof="schema:Person" property="schema:name" datatype="">Hoti</span></span> <span class="field field--name-created field--type-created field--label-hidden">Feb 05 2015</span> <div class="field field--name-field-tagies field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class='field__items'> <div class="field__item"><i class="fa fa-tags"></i> <a href="/Programming" hreflang="en">Programming</a></div> <div class="field__item"><i class="fa fa-tags"></i> <a href="/tags/c" hreflang="en">C</a></div> <div class="field__item"><i class="fa fa-tags"></i> <a href="/tags/sockets" hreflang="en">Sockets</a></div> <div class="field__item"><i class="fa fa-tags"></i> <a href="/tags/downloads" hreflang="en">Downloads</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> </section> <section class="field field--name-comment field--type-comment field--label-above comment-wrapper"> <h2 class='title comment-form__title'> <i class="fa fa-comments-o"></i> Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=22&amp;2=comment&amp;3=comment" token="8G-0ziIYgIKVNZP_lsiweU7U_jtelpkeubzWPKGyBMY"></drupal-render-placeholder> </section> Thu, 05 Feb 2015 15:00:08 +0000 Hoti 22 at https://tech.feedyourhead.at