nweb:一个微型、安全的web服务器(仅支持静态页面)(上)

本文是翻译,版权归原作者所有


你曾经想知道一个web服务器究竟是如何运行的吗?体验一下nweb—一个只有200行C源代码的简单web服务器。本文,Nigel Griffiths提供了该服务器的一份拷贝,也包括了源代码。你完全可以看到它能够、和不能够做什么。

简介

你曾经想运行一个微型的、安全的web服务器吗?不用为使用一个完全膨胀的、安装和配置都很复杂的web服务器而担忧。你想知道如何写一个带有网络套接字的接收进来的消息的程序吗?你曾经只想要用来实验和学习的、你自己的web服务器吗?2012年的进一步更新支持了最近的web服务器、浏览器标准和代码刷新。

好吧,不必再期待了—nweb就是你需要的。这是一个只有200行C源代码的web服务器。它面向普通用户,不能运行任何服务器端脚本或程序,因此它不能开通任何特殊权限或安全漏洞。

本文包括:

nweb仅仅把下列总类的文件传输给浏览器:

如果你想要的静态文件类型没有在上面的列表里,你可以简单地添加到源代码里,并重新编译就可以了。

被提供的例子文件包含了用C实现的UNIX®源代码和为AIX准备的预编译好的源代码。这份代码将用IBM VisualAge® C编译器或GNU C编译器进行编译,应该在AIX、Linux®或任何其它UNIX版本无变化地运行。

用下面的命令编译。程序用C实现,不需要额外的类库或服务。-O2选项仅当你想优化代码时才需要:

cc –O2 nweb.c –o nweb

nweb内部的函数

源代码里只有一些函数,解释如下:

log()

日志消息存放在一个日志文件。如果用户从web服务器请求一个不被允许或不能被完成的操作,nweb试着直接告知用户。这会返回给用户一个伪造的、包含了错误信息的web页。既然这个函数仅仅被web服务器子进程调用,它能够退出(在完成之后),主进程继续允许接下来的浏览器连接请求。如果这个错误不可恢复,那么进程就被停止了。

web()

处理HTTP浏览器请求、并给浏览器返回数据。这个函数在子进程里调用—每个web请求产生一个子进程。它也允许web服务器主进程持续等待更多连接。检查确认请求是安全的、以及能够被完成。检查之后,它把请求的静态文件传输给浏览器并退出。

main()

这是主要的web服务器进程函数。在检查命令参数之后,它生成一个处理即将到来的浏览器请求的套接字,处于一个循环里来接受请求,启动子进程来处理请求。它应该永不关闭。

伪代码

下面是大约200行源代码的伪代码。它应当有助于你理解程序流。

logger() { outputs error, sorry or log messages to the nweb.log file if a sorry message, transmit it to the browser as a fake HTML response if error or sorry message the program is stopped } web() - this function returns the request back to the browser { read from the socket the HTTP request check it’s a simple GET command check no parent directory requested to escape the web servers home directory if no file name given assume index.html check the file extension is valid and supported check the file is readable by opening it transmit the HTTP header to the browser transmit the file contents to the browser if LINUX sleep for one second to ensure the data arrives at the browser stop } main() { if option is "-?", output the hints and stop check the directory supplied is sensible and not a security risk become a daemon process ignore child programs (to avoid zombies when child processes stop) create a socket, bind it to a port number and start listening to the socket forever { wait and accept incoming socket connection fork a child process if the child process then call the web function else close new connection } }

套接字系统调用

假如你以前没有碰到过这些网络套接字系统调用,下面就是介绍——特别是它们如何组装在了一起。你也能在手册或网上了解它们——虽然难以搞清楚它们做了什么,以及它们从源代码和手册为什么就能组成一个web服务器。

socket(), bind(), listen(), 和 accept()网络系统调用一起工作产生一个服务器进程。当组合在一起的时候,它们为网络上的通信准备了一个套接字。一个套接字是:

下图解释了它们如何组合:

nweb服务器架构图

socket()函数产生套接字,并返回文件描述符,它能够在任何使用文件描述符的函数中使用,比如read、write和close。参数告诉操作系统套接字类型和你需要的通信。

对于socket()和bind()的参数,有大量的排列和组合。程序用到的参数对于使用IP的普通目的的套接字是非常典型的;根据我的经验,其它更多的复杂选项是很少的。

bind()函数把一个特定端口号绑定到套接字。当一个客户端试着联系你的服务器时,它将使用IP地址(经常通过把主机名转换成IP地址的DNS服务来找到)和端口号。端口号告诉操作系统你想要的服务器上的服务。大多数UNIX机器上的通用端口号详情被列在了/etc/services文件里。这些文件包括了标准的服务端口号,比如FTP(端口21)、Telnet(端口23)和web服务器(通常是80端口)。你应该检查/etc/services文件里的端口号,确保你不要尝试已经在用的端口。尽管如此,如果你用了,你应当在日志文件得到一个错误消息,因为对于两个服务,使用同一个端口号,正常情况下是不可能的。

listen()函数调用告诉操作系统你现在准备接收到来的请求了。这是让套接字被本地或远程程序使用的最后开关。

accept()函数不会返回给你的程序,直到有一个套接字连接请求到你的IP地址和端口号。该函数使用套接字的文件描述符,但是进来的连接的新结构体(struct sockaddr_in)允许服务器检查谁正在连接。在nweb,你不需关心谁在连接。

一旦accept()函数返回了,意味着客户端套接字文件描述符被激活了。如果你从套接字读取字节,你就能从客户端得到字符,如果你向套接字写字节,它们能够传输到客户端。但是从主程序读或写套接字不是惯常做法。通常,你想允许多个客户端访问你的服务器上的服务,如果主程序做一个读或写操作,它会阻塞,直到有要读的字符或要传输的被写的字符。这个主程序应该再次运行accept()函数来允许启动一个新连接。主程序应该启动一个子进程来做与客户端的“交谈”以及任何需要完成的工作。当主程序关闭套接字时,重新运行accept函数,以等待下一个客户端连接。当子进程启动后,它继承了父级打开的套接字来保持alive。

其它系统调用

getenv()

返回shell变量值的简单函数。如果你设置了一个Korn shell【注1】变量 $ export ABC=123,那么getenv(“ABC”)函数将返回一个指向以null结束的、包含123的字符串。

chdir()

改变目录

fork()

启动一个子进程。在父级,它返回子进程的id(PID),在子级它返回零。

setpgrp()

设置进程组。效果是该进程脱离被用户启动的其它子进程,因此它不会受用户发生情况的影响(比如注销)。

signal()

当软件中断出现在进程的时候,决定要发生什么。在nweb.c代码里,主服务器想忽略一个子进程死亡的信号。如果不这样,主服务器进程将不得不为每个子进程运行等待系统调用,或者它们将永远处于等待父级调用wait()函数的“僵尸”状态。最终,会有太多的僵尸进程,用户环境也会宕掉,因为它们不能生成更多的进程了。

open(), read(), write() 和 close()

这些是普通C类库函数,但是当文件和套接字被文件描述符访问的时候,它们用来读将被发送到客户端web浏览器的套接字和文件。套接字用accept()函数打开;代码中的open()用于打开请求的文件。

原文地址:http://www.ibm.com/developerworks/systems/library/es-nweb/index.html 注1:Korn shell,http://zh.wikipedia.org/wiki/Korn_shell

译文:nweb:一个微型、安全的web服务器(仅支持静态页面)(上) 》| 腊八粥