The following content introduces the method of php to implement dynamic web server through pictures and texts. The specific content is as follows:
The server implemented in this article is only used to demonstrate and understand the principles, and strives to be simple and easy to understand. Friends who are interested can continue the transformation in depth
If you want to implement a web server, you need to have a general understanding of the operating principles of the web server. Let’s start with a static text server, taking accessing 1.html of the web server as an example
1. The client sends an http request to the server. If the port number the server listens to is 9002, then the address to test access on the machine itself is http://localhost:9002 /1.html .
2. The server listens to port 9002. After receiving the request, it can obtain the location in the web directory of the uri resource that needs to be accessed in the request from the http header.
3. The server reads the resource file that needs to be accessed, then fills it into the http entity and returns it to the client.
The schematic diagram is as follows:
php <?php class web_config { // 监听的端口号 const PORT = 9003; // 项目根目录 const WEB_ROOT = "/Users/zhoumengkang/Documents/html"; } class server { private $ip; private $port; public function __construct($ip, $port) { $this->ip = $ip; $this->port = $port; $this->await(); } private function await() { $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($sock < 0) { echo "Error:" . socket_strerror(socket_last_error()) . "\n"; } $ret = socket_bind($sock, $this->ip, $this->port); if (!$ret) { echo "BIND FAILED:" . socket_strerror(socket_last_error()) . "\n"; exit; } echo "OK\n"; $ret = socket_listen($sock); if ($ret < 0) { echo "LISTEN FAILED:" . socket_strerror(socket_last_error()) . "\n"; } do { $new_sock = null; try { $new_sock = socket_accept($sock); } catch (Exception $e) { echo $e->getMessage(); echo "ACCEPT FAILED:" . socket_strerror(socket_last_error()) . "\n"; } try { $request_string = socket_read($new_sock, 1024); $response = $this->output($request_string); socket_write($new_sock, $response); socket_close($new_sock); } catch (Exception $e) { echo $e->getMessage(); echo "READ FAILED:" . socket_strerror(socket_last_error()) . "\n"; } } while (TRUE); } /** * @param $request_string * @return string */ private function output($request_string){ // 静态 GET /1.html HTTP/1.1 ... $request_array = explode(" ",$request_string); if(count($request_array) < 2){ return $this->not_found(); } $uri = $request_array[1]; $filename = web_config::WEB_ROOT . $uri; echo "request:".$filename."\n"; // 静态文件的处理 if (file_exists($filename)) { return $this->add_header(file_get_contents($filename)); } else { return $this->not_found(); } } /** * 404 返回 * @return string */ private function not_found(){ $content = " <h1>File Not Found </h1> "; return "HTTP/1.1 404 File Not Found\r\nContent-Type: text/html\r\nContent-Length: ".strlen($content)."\r\n\r\n".$content; } /** * 加上头信息 * @param $string * @return string */ private function add_header($string){ return "HTTP/1.1 200 OK\r\nContent-Length: ".strlen($string)."\r\nServer: mengkang\r\n\r\n".$string; } } $server = new server("127.0.0.1", web_config::PORT);
The code has been uploaded to github https://github.com/zhoumengkang/php/tree/master/php-webserver/static
As mentioned in the above code, as long as the file is executed in the terminal, a static web server will be started.
The picture below is a screenshot of me accessing the 1.jpg file in my web directory
The simple static web server has been completed. The next question is how to make it support the output of dynamic content. Do we only need to execute a certain program inside the web server and return the result to the client? But in this way, the web server code is coupled with the business code. How to solve a web server that can be used in various business scenarios?
The advent of CGI solved this problem. So what is CGI? The following paragraph is copied:
CGI is an interface standard between external applications (CGI programs) and Web servers. It is a procedure for transferring information between CGI programs and Web servers. The CGI specification allows Web servers to execute external programs and send their output to Web browsers. CGI turns the Web's set of simple static hypermedia documents into a complete new interactive media.
It’s so confusing. To give a specific example, for example, the global variable <font face="NSimsun">$_SERVER['QUERY_STRING']</font>
of PHP we are using is passed by the web server through the CGI protocol. For example, in Nginx, maybe you remember this fastcgi configuration
Copy code The code is as follows:
fastcgi_param
QUERY_STRING
$query_string;
Yes, nginx passes its global variable <font face="NSimsun">$query_string</font>
to the environment variable of fastcgi_param.
Below we also use CGI's <font face="NSimsun">QUERY_STRING</font>
as a bridge to pass the information in the uri requested by the client to the cgi program. Store <font face="NSimsun">putenv</font>
in the environment variable of the request through <font face="NSimsun">QUERY_STRING</font>
.
We agree that the resources accessed in the web server are <font face="NSimsun">.cgi</font>
. The suffix indicates dynamic access. This is a bit similar to configuring location in nginx to find php scripts. It's all a rule to check whether a cgi program should be requested. In order to distinguish it from the web server, I wrote a cgi program in C to query user information and query user information based on user id.
The general access logic is as follows
Demo code address: https://github.com/zhoumengkang/php/tree/master/php-webserver/dynamic
If you want to run the demo, you need to do the following
1. Modify the project root directory in <font face="NSimsun">config.php</font>
<font face="NSimsun">WEB_ROOT</font>
2. Compile <font face="NSimsun">cgi-demouser.c</font>
, compile command <font face="NSimsun">gcc -o user.cgi user.c</font>
, and then put the <font face="NSimsun">user.cgi</font>
file under the root directory of your configured project
3. Execute <font face="NSimsun">php start.php</font>
in the terminal, so that the web server will start
4. Through http://localhost:9003/user.cgi?id=1 you can access and see the following effect
其实只是在静态服务器的基础上做了一些 cgi 的判断是请求的转发处理,把github 上的三个文件的代码合并到一个文件里方便大家观看
php <?php class web_config { // 监听的端口号 const PORT = 9003; // 项目根目录 const WEB_ROOT = "/Users/zhoumengkang/Documents/html"; // 系统支持的 cgi 程序的文件扩展名 const CGI_EXTENSION = "cgi"; } class server { private $ip; private $port; public function __construct($ip, $port) { $this->ip = $ip; $this->port = $port; $this->await(); } private function await() { $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($sock < 0) { echo "Error:" . socket_strerror(socket_last_error()) . "\n"; } $ret = socket_bind($sock, $this->ip, $this->port); if (!$ret) { echo "BIND FAILED:" . socket_strerror(socket_last_error()) . "\n"; exit; } echo "OK\n"; $ret = socket_listen($sock); if ($ret < 0) { echo "LISTEN FAILED:" . socket_strerror(socket_last_error()) . "\n"; } do { $new_sock = null; try { $new_sock = socket_accept($sock); } catch (Exception $e) { echo $e->getMessage(); echo "ACCEPT FAILED:" . socket_strerror(socket_last_error()) . "\n"; } try { $request_string = socket_read($new_sock, 1024); $response = $this->output($request_string); socket_write($new_sock, $response); socket_close($new_sock); } catch (Exception $e) { echo $e->getMessage(); echo "READ FAILED:" . socket_strerror(socket_last_error()) . "\n"; } } while (TRUE); } /** * @param $request_string * @return string */ private function output($request_string){ // 静态 GET /1.html HTTP/1.1 ... // 动态 GET /user.cgi?id=1 HTTP/1.1 ... $request_array = explode(" ",$request_string); if(count($request_array) < 2){ return ""; } $uri = $request_array[1]; echo "request:".web_config::WEB_ROOT . $uri."\n"; $query_string = null; if ($uri == "/favicon.ico") { return ""; } if (strpos($uri,"?")) { $uriArr = explode("?", $uri); $uri = $uriArr[0]; $query_string = isset($uriArr[1]) ? $uriArr[1] : null; } $filename = web_config::WEB_ROOT . $uri; if ($this->cgi_check($uri)) { $this->set_env($query_string); $handle = popen(web_config::WEB_ROOT.$uri, "r"); $read = stream_get_contents($handle); pclose($handle); return $this->add_header($read); } // 静态文件的处理 if (file_exists($filename)) { return $this->add_header(file_get_contents($filename)); } else { return $this->not_found(); } } /** * 设置环境变量 给 cgi 程序使用 * @param $query_string * @return bool */ private function set_env($query_string){ if($query_string == null){ return false; } if (strpos($query_string, "=")) { putenv("QUERY_STRING=".$query_string); } } /** * 判断请求的 uri 是否是合法的 cgi 资源 * @param $uri * @return bool */ private function cgi_check($uri){ $info = pathinfo($uri); $extension = isset($info["extension"]) ? $info["extension"] : null; if( $extension && in_array($extension,explode(",",web_config::CGI_EXTENSION))){ return true; } return false; } /** * 404 返回 * @return string */ private function not_found(){ $content = "<h1>File Not Found </h1>"; return "HTTP/1.1 404 File Not Found\r\nContent-Type: text/html\r\nContent-Length: ".strlen($content)."\r\n\r\n".$content; } /** * 加上头信息 * @param $string * @return string */ private function add_header($string){ return "HTTP/1.1 200 OK\r\nContent-Length: ".strlen($string)."\r\nServer: mengkang\r\n\r\n".$string; } } $server = new server("127.0.0.1", web_config::PORT);
以上就是本文的全部内容,希望大家喜欢。