Tiny Server: Small Web server

Source: Internet
Author: User
Tags small web server sprintf htons

I. BACKGROUND

Csapp's network programming is a rough introduction to the network programming of some knowledge, in the last section of the main implementation of a small Web server, this server called Tiny, it is a small but full-featured Web server, in just about 300 lines of code, combined with many ideas, for example, Process Control, Unix I/O, sockets, HTTP, and so on, it is exciting that it can provide the Web browser with static and dynamic content, that is, in the browser to open the HTML and other files can be directly displayed through the tiny directly in the window. I always want to learn network programming, this is probably the first thing to make it, think it is exciting, but the original book code is not can be directly used, and how to use it, how to let the browser point to their own server, the book is not specifically said, I do not know, I found on the internet for a long time finally understand how to use, the code in the book is not complete, is not able to directly run the success, here, I will write down my process, I think there will be someone need.

Ii. Compiling and building

1. Compile the full code

GCC Tinyserver.c-o tinyserver

2. Compile the test program adder.c into an executable program, ADDER.C need to be placed under the Cgi-bin folder in the same directory as Tinyserver (and then why)

GCC Adder.c-o adder

3. Run the Tinyserver program and specify the port used (1024--49151 available, other for system use, generally can not occupy)

./tinyserver 2000

4. Enter the access address in the address bar in the browser

http:localhost:2000/cgi-bin/adder?30&72

5. Running Results

Displayed in the browser:



Background Server information display:


If you want to show other files, examples, articles, and so on, the same way as above

http:localhost:2000/testpic.jpg



However, if you encounter the following situation during the test, the background display is always refreshed


I think it may be because this server is a single-threaded reason, when the receipt of a request, in main because it is a continuous refresh, this situation will occur, but I am not very sure, do not know which God can explain the next ...

The above is the simple use of the situation, I believe that the test success at the moment, is not a great sense of accomplishment there is no AH???

Third, source code analysis

Tiny is an iterative server that listens for connection requests on ports determined on the command line. After opening a listening socket through the OPEN_LISTENEDFD function, Tiny performs a typical infinite service loop, repeatedly accepting a connection (accept) request, executing a transaction (doit), and closing the connection descriptor (close).
1. header file:

/* tiny-a simple, iterative http/1.0 Web server*/#ifndef __csapp_h__ #define __CSAPP_H__ #include <stdio.h> # Include <stdlib.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <set jmp.h> #include <signal.h> #include <sys/time.h> #include <sys/types.h> #include &LT;SYS/WAIT.H&G  T #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <errno.h> #include &lt ;math.h> #include <semaphore.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/i n.h> #include <arpa/inet.h>//above the header file is supposed to be in the "csapp.h", but I tried not to, so I wrote a # define Def_mode s_irusr| s_iwusr| s_irgrp| s_iwgrp| s_iroth| S_iwoth #define Def_umask s_iwgrp|  s_iwoth typedef struct SOCKADDR SA;                #define RIO_BUFSIZE 8192 typedef struct {int rio_fd;               /* Descriptor for internal buffer */int rio_cnt;     /* The number of bytes left in the internal buffer is still unread */char *rio_bufptr;     /* point to the next unread byte in the internal buffer */char rio_buf[rio_bufsize];  /* Internal buffer */} rio_t;   extern char **environ; #define MAXLINE 8192/* Maximum characters per line */#define MAXBUF 8192 */I/O buffer maximum capacity */#define LISTENQ 1024/* Second parameter for monitoring */*  Helper functions */ssize_t rio_writen (int fd,void *usrbuf,size_t n);  void Rio_readinitb (rio_t *rp,int FD); Associates the internal buffer of the program with the description typeface.  ssize_t Rio_readlineb (rio_t *rp,void *usrbuf,size_t maxlen); /* Reads a line of text from the internal buffer into BUF and ends the line with a null character. Of course, the maximum number of characters per line cannot exceed maxline.  */int open_clientfd (char *hostname, int portno);  int open_listenfd (int portno);  #endif void doit (int fd), void Read_requesthdrs (rio_t *rp);   Read and ignore request header int Parse_uri (char *uri, Char *filename, char *cgiargs); Parse the URI, the file name is stored in filename, the parameters are stored in Cgiargs.   void serve_static (int fd, char *filename, int filesize); Provides static services.    void Get_filetype (char *filename, char *filetype), void serve_dynamic (int fd, char *cause, char *cgiargs); Provide dynamic services. void Clienterror (int fd, char *cause, Char *errnum, Char *shortmsg, Char *longmsg);/* Tiny is an iterative server that listens for connection requests on ports determined on the command line. After opening a listening socket through the OPEN_LISTENEDFD function, tiny executes a typical infinite service loop, repeatedly accepting a connection (accept) request, executing a transaction (doit), and closing the connection descriptor (Close)////SSCANF (BUF , "%s%s%s", method,uri,version): As an example, buf is usually stored in the "get/http/1.1", so the method is "GET", the URI is "/", Version is "http/1.1".    One of the functions of sscanf: The strings in the buf are separated into the method, Uri, and version by a space delimiter.    STRCASECMP (method, "get"): Ignores case compare method with "Get" size, equal words return 0.    Stat (FILENAME,&AMP;SBUF): fills in sbuf the individual metadata in the file filename, and returns 0 if the file cannot be found.    S_isreg (Sbuf,st_mode): This file is a normal file. S_IRUSR & Sbuf.st_mode: Have Read permission. */


the main function of 2.Tiny

int main (int argc, char const *argv[]) {int LISTENFD, CONNFD, Port, clientlen;struct sockaddr_in clientaddr;if (argc! = 2) { fprintf (stderr, "Usage:%s\n", argv[0]); exit (1);} Port = atoi (Argv[1]), LISTENFD = OPEN_LISTENFD (port), while (1) {Clientlen = sizeof (CLIENTADDR); connfd = Accept (LISTENFD, ( SA *) &clientaddr,&clientlen);d oit (CONNFD); close (CONNFD);}}
the doit function of 3.Tiny

void doit (int fd) {int is_static;struct stat Sbuf;char buf[maxline],method[maxline],uri[maxline],version[maxline]; Char filename[maxline],cgiargs[maxline];rio_t RIO;RIO_READINITB (&AMP;RIO,FD); Rio_readlineb (&rio,buf,MAXLINE) ; sscanf (buf, "%s%s%s", method,uri,version), if (strcasecmp (method, "GET")) {Clienterror (Fd,method, "501", "not Implemented "," Tiny does not implement this method "); return;} Read_requesthdrs (&rio); is_static = Parse_uri (Uri,filename,cgiargs); if (stat (FILENAME,&AMP;SBUF) < 0) { Clienterror (fd,filename, "404", "Not Found", "Tiny coundn ' t find this file"); if (is_static) {if (!) ( S_isreg (sbuf.st_mode)) | | ! (S_irusr & Sbuf.st_mode)) {Clienterror (Fd,filename, "403", "Forbidden", "Tiny coundn ' t read the file"), return; Serve_static (fd,filename,sbuf.st_size);} else {if (! ( S_isreg (sbuf.st_mode)) | | ! (S_ixusr & Sbuf.st_mode)) {Clienterror (Fd,filename, "403", "Forbidden", "Tiny coundn ' t run the CGI program"); Serve_dynamic (Fd,filename,cgiargs);}} /* from the doit function, our tThe Iny Web server only supports the "GET" method, and other methods request that an error message is sent, the main program returns, and waits for the next request. Otherwise, we read and ignore the request header.    (In fact, when we request the service, we do not have to write the request header, only to comply with the HTTP protocol standard). We then parse the URI into a file name and a potentially empty CGI parameter, and set a flag bit indicating whether the requested static or dynamic content.    Determine whether a file exists by using the stat function. Finally, if a static content is requested, we need to verify that it is a plain file and readable. The condition passes, then our server sends the static content to the customer service side; Similarly, if the requested dynamic content, I will verify whether the file is an executable file, if yes, execute the file, and provide dynamic functionality. */


the Clienterrorh function of 4.Tiny

void Clienterror (int fd, char *cause, Char *errnum, Char *shortmsg, char *longmsg) {char buf[maxline],body[maxbuf];sprintf (Body, "

5.Tiny of

void Read_requesthdrs (rio_t *rp) {char buf[maxline];rio_readlineb (rp,buf,maxline), while (strcmp (buf, "\ r \ n")) {Rio_ Readlineb (rp,buf,maxline);p rintf ("%s", buf);} return;} /*    tiny do not need to request any information in the header, this function is to skip these request headers, read the request headers until the empty line, and then return. */


6.Tiny of
int Parse_uri (char *uri, char *filename,char *cgiargs) {char *ptr;if (!strstr (URI, "Cgi-bin")) {strcpy (Cgiargs, ""); strcpy (FileName, "."); strcat (Filename,uri);        if (Uri[strlen (URI)-1] = = '/') {        strcat (filename, "home.html");        }        return 1;} else {ptr = index (URI, '? '); if (PTR) {strcpy (cgiargs,ptr+1); *ptr = ' + ';} else {strcpy (Cgiargs, "");} strcpy (FileName, "."); strcat (Filename,uri); return 0;}} /*   determine if the requested static or dynamic content is based on whether the URI contains Cgi-bin. If there is no cgi-bin, the request is static content. So   , we need to put Cgiargs null, and then get the file name, if the URI we requested is finally "/", the home.html is added automatically. For example,   when I ask for "/", the returned file is named "./home.html" and we Request "/logo.gif", the file name is "./logo.gif". If the   URI contains Cgi-bin, the requested dynamic content is indicated. So, we need to copy the parameters to Cgiargs and write the file path to be executed to   ilename. For example, if the URI is/cgi-bin/adder?12&45, the Cigargs stored in 12&45,filename is   "./cgi-bin/adder"   index (URI, '? ') : Find the first parameter in the URI string '? ' Address, and return this address. */


the serve_static function of 7.Tiny

void serve_static (int fd, char *filename, int filesize) {int Srcfd;char *srcp,filetype[maxline],buf[maxbuf];get_ FileType (filename,filetype); sprintf (buf, http/1.0 ok\r\n); sprintf (buf, "%sserver:tiny Web server\r\n", buf); sprintf (buf, "%scontent-length:%d\r\n", buf,filesize); sprintf (buf, "%scontent-type:%s\r\n\r\n", buf,filetype); Rio_ Writen (Fd,buf,strlen (BUF)); srcfd = open (filename,o_rdonly,0); SRCP = Mmap (0,filesize, Prot_read, map_private,srcfd,0) ; Close (SRCFD); Rio_writen (fd,srcp,filesize); Munmap (srcp,filesize);} /* Open the file named filename, map it to a virtual memory space, map the first filesize bytes of the file to the virtual storage area starting from address SRCP. Closes the file descriptor Srcfd, writes data from the virtual store to the FD descriptor, and finally releases the virtual memory area. */void Get_filetype (Char *filename, char *filetype) {if (strstr (filename, ". html")) strcpy (filetype, "text/html"); else if (strstr (filename, ". gif")) strcpy (filetype, "image/gif"), Else if (strstr (filename, ". jpg")) strcpy (filetype, "image/ JPG "); else strcpy (filetype," text/plain ");}

The server_dynamic function of 8.Tiny

void serve_dynamic (int fd, char *filename, char *cgiargs) {char buf[maxline],*emptylist[] = {null};sprintf (buf, "http/1.0 Ok\r\n "), Rio_writen (Fd,buf,strlen (BUF)), sprintf (buf," Server:tiny Web server\r\n "), Rio_writen (Fd,buf,strlen ( BUF)); if (fork () = = 0) {setenv ("query_string", cgiargs,1);d up2 (Fd,stdout_fileno); Execve (Filename,emptylist,environ) ;} Wait (NULL);}    /* Tiny provides various types of dynamic content by deriving a child process and running a CGI program (executable file) in the context of the child process.    setenv ("Query_string", cgiargs,1): Sets the QUERY_STRING environment variable.    dup2 (Fd,stdout_fileno): redirects its standard output to the connected descriptor. At this point, anything written to the standard output is written directly to the client.    Execve (Filename,emptylist,environ): Load Run CGI program. */

9. A number of other functions

ssize_t rio_writen (int fd, void *usrbuf, size_t n) {size_t nleft = n;    ssize_t Nwritten;    char *BUFP = usrbuf;       while (Nleft > 0) {if (Nwritten = Write (fd, BUFP, nleft)) <= 0) {if (errno = = eintr) Nwritten = 0;      elsereturn-1;    }nleft-= NWRITTEN;BUFP + = Nwritten; } return n;}    Static ssize_t Rio_read (rio_t *rp, Char *usrbuf, size_t n) {int cnt; while (rp->rio_cnt <= 0) {/* If the buffer is empty, repopulate */rp->rio_cnt = Read (RP-&GT;RIO_FD, rp->rio_buf, sizeof (Rp->rio _BUF)); if (rp->rio_cnt < 0) {if (errno! = eintr) return-1;} else if (rp->rio_cnt = = 0)/* EOF */return 0;else rp->rio_bufptr = rp->rio_buf;              /* Reset the buffer pointer */}/* from the internal buffer copy min (n, rp->rio_cnt) bytes to usrbuf*/cnt = n;    if (rp->rio_cnt < n) cnt = rp->rio_cnt;    memcpy (Usrbuf, rp->rio_bufptr, CNT);    Rp->rio_bufptr + = cnt;    RP-&GT;RIO_CNT-= CNT; return CNT;}   void Rio_readinitb (rio_t *rp, int fd) {rp->rio_fd = FD;   rp->rio_cnt = 0; Rp->rio_bufptr = Rp->rio_buf;}    ssize_t Rio_readlineb (rio_t *rp, void *usrbuf, size_t maxlen) {int n, RC;    Char C, *BUFP = Usrbuf;    for (n = 1; n < maxlen; n++) {if (rc = Rio_read (RP, &c, 1) = = 1) {*bufp++ = C; if (c = = ' \ n ') break;    else if (rc = = 0) {if (n = = 1) return 0;/* EOF, no data read */elsebreak;  /* EOF, some data was read */} else return-1;    /* ERROR */} *BUFP = 0; return n;}    int Open_clientfd (char *hostname, int port) {int clientfd;    struct Hostent *hp;    struct sockaddr_in serveraddr;     if (CLIENTFD = socket (af_inet, sock_stream, 0)) < 0) return-1;     if (HP = gethostbyname (hostname)) = = NULL) return-2;    Bzero (char *) &serveraddr, sizeof (SERVERADDR));    serveraddr.sin_family = af_inet;    Bcopy (char *) hp->h_addr_list[0], (char *) &serveraddr.sin_addr.s_addr, hp->h_length);    Serveraddr.sin_port = htons (port); if (Connect (CLIENTFD, (SA *) &serveraddr, sizeOf (SERVERADDR)) < 0) return-1; return CLIENTFD;}    int open_listenfd (int port) {int LISTENFD, optval=1;      struct sockaddr_in serveraddr;     /* Create a Socket Descriptor */if ((LISTENFD = socket (af_inet, sock_stream, 0)) < 0) return-1; /* eliminates "Address already in use" error from bind.    */if (setsockopt (LISTENFD, Sol_socket, so_reuseaddr, (const void *) &optval, sizeof (int)) < 0) return-1; /* LISTENFD'll be a endpoint for all requests to port on any IP address for this host */bzero ((char *) &    SERVERADDR, sizeof (SERVERADDR));     serveraddr.sin_family = af_inet;     SERVERADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any);     Serveraddr.sin_port = htons ((unsigned short) port);    if (Bind (LISTENFD, (SA *) &serveraddr, sizeof (SERVERADDR)) < 0) return-1;    /* Make it a listening sockets ready to accept connection requests */if (Listen (LISTENFD, Listenq) < 0) return-1; return LISTENFD;}






Tiny Server: Small Web server

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.