使用PHP(无需Apache)创建Web服务器

13

我刚刚尝试了这段代码:

<?php
set_time_limit(0);

$address = '176.9.117.136';
$port = 9000;

$sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($sock, $address, $port) or die('Could not bind to address');

while(1)
{
    socket_listen($sock);
    $client = socket_accept($sock);

    $input = socket_read($client, 1024);
    echo $input;

    $output = 'URL: http://ip-of-my-server:9000/
HTTP/1.1 200 OK
Date: Tue, 10 Jul 2012 16:58:23 GMT
Server: TestServer/1.0.0 (PHPServ)
Last-Modified: Fri, 06 Jul 2012 14:29:58 GMT
ETag: "13c008e-1b9-4c42a193de580"
Accept-Ranges: bytes
Content-Length: 441
Vary: Accept-Encoding
Content-Type: text/html

';
    socket_write($client, $output);

    socket_close($client);
}

socket_close($sock);
?>

但是有一个问题。Apache返回它自己的头部而不是使用$output内容作为头部...

我不知道为什么,因为我通过这个命令执行脚本:php webserv.php

然而它实际上可以工作,因为当我从浏览器加载页面http://ip-of-my-server:9000/时,它向我展示了客户端发送到服务器的头部,并将$output的内容返回给客户端(我的浏览器)。

如果可能的话,我想要创建自己的纯PHP Web服务器。我只想知道如何在没有Apache的情况下运行它,这样我就可以管理自己的HTTP头部。


2
如果你不知道的话,PHP5.4附带了一个内置的Web服务器,因此你不必再做这种事情了... - user895378
1
这似乎是在重复造轮子。我向您保证,市面上有很多很多的轮子。 - Wug
4
我知道,没错。只是为了学习而已……我永远不会把它用作生产 Web 服务器。我只是想创建自己的服务器,从头到尾了解它的工作原理。 - Yoone
@Yoone,使用PHP构建Web服务器并不能教你从头到尾的知识。为此,请放弃PHP,改用C语言实现。 - Pacerier
@Pacerier:我大约一年前问过这个问题,自那以后,我也用C#和C++实现了它(当然只是基本实现)。最近我在PHP中尝试了一些套接字的操作,它在我的一些最新项目中表现得非常好! - Yoone
显示剩余4条评论
3个回答

18
有没有理由在PHP中实现一个HTTP服务器(为什么是PHP呢)?它没有线程等功能。这会很麻烦...(除非这是某种学术项目...)
PHP 5.4附带了一个内置的Web服务器。也许这就是你要找的东西...
更新:

我理解你学习这种东西的动机,但我认为你在尝试使用PHP进行这种操作时走错了方向。PHP并不是为长时间运行的进程(比如服务器)设计的,它也不支持并行处理(线程)。即使是多进程也需要使用PCNTL,特别是pcntl_fork(),并且会限制你在Unix系统上的学习(尽管这可能不是个问题)。

如果你的目标是理解服务器如何处理并发,我建议你尝试使用专为此设计的语言(如Erlang、Go、Scala等)。或者玩一下那些至少在某种程度上模拟并行处理的语言(如Python、Ruby等[因为它们有GIL])。

如果你的目标是理解HTTP(让我告诉你,HTTP是个庞然大物,如果你要超越HTTP/1.0并且想做得正确),那么玩弄PHP可能还可以,如果这是你唯一熟悉的语言的话。如果是这样,可以看看这篇(可惜是德文的)文章中的示例代码(聊天服务器)关于PHP中的Socket服务器,以便让基本的套接字工作正常,并专注于实际的HTTP部分。

更新2:

回答你关于头部的问题……我完全不知道apache如何适用于你描述的情况。但是我看到你在使用换行符来分隔头部,并使用双换行符来分隔头部和正文。除非你使用\r\n作为默认的换行符(Windows风格)保存你的php文件,否则你的头部部分是格式错误的,会被识别为正文。根据http客户端(用户代理,无论是浏览器还是curl等)的不同,可能会被处理为“插入一些默认的头部”。将你的换行符替换为\r\n,然后再试一次。

如果你的服务器可以从互联网访问,请尝试一些头部测试工具来验证你的HTTP是否正常。如果只能在本地访问,请查看curl -I http://我的服务器的IP地址:9000输出的内容。


1
是的,这是一种学术性的事情,但只是为了我自己。我只想学习并深入了解PHP的使用。 - Yoone
1
+1 - 我不知道是谁给我点了踩,但他一定是个白痴才会这样做。创建一个HTTP服务器可不仅仅是“下一步,下一步,下一步”的任务,特别是在使用PHP时。 - tftd
1
@tftd 我知道,但我还是要重申一遍,这只是为了学习如何做而已。我永远不会在生产网站上使用它... - Yoone
这就是“Connectify”通过网页分享用户文件所做的事情。(我的想法) - Oki Erie Rinaldi
嗯...PHP现在支持多线程了...这要归功于由@JoeWatkins开发的惊人扩展pthreads https://github.com/krakjoe/pthreads/,并且这里还有一个使用`pthreads`构建服务器的示例 https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.php - Ikari
显示剩余11条评论

2
我认为你在这种情况下所指的 web-server,实际上只是服务器与客户端之间的某种套接字通信。我的 PHP 和套接字的经验是针对 Flash 代理客户端。

这需要在页面中嵌入一个 1x1 像素的 Flash 像素,并将其用作 Flash 像素、JavaScript 和 PHP Socket 服务器之间的桥梁(一旦你知道它如何工作,套接字通信确实非常简单)。这种方法也是您获得最大浏览器兼容性的唯一方式(即使是像socket.io这样更先进的 WebSocket 框架,也使用此 Flash 像素方法作为后备方法)。

另一个选择当然是 WebSockets,类似于本站用于实时刷新功能的 WebSockets。在 Stack Overflow 上,甚至有一个专门的标签来介绍它。

如果你想用PHP创建一个socket服务器,那么你的客户端就不能仅仅是浏览器。希望这能指引你朝着正确的方向前进...

我不确定我理解了所有的内容,但是我该如何创建一个“完整”的Web服务器,其中客户端只是一个Web浏览器? - Yoone
如果这正是您要寻找的内容,那么我必须同意其他人关于重新发明轮子的说法... 如果您只是想研究PHP的套接字通信,那么也许您应该从比HTTP服务器更小的东西开始... - Lix
1
不,我真的想在PHP中创建一个HTTP服务器。我不想“仅仅”学习套接字。 - Yoone

2

你最初尝试的代码非常接近,只需要进行一些小的修改:

<?php
set_time_limit(0);

$address = '127.0.0.1';
$port = 80;

$sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($sock, $address, $port) or die('Could not bind to address');

echo "\n Listening On port $port For Connection... \n\n";

while(1)
{
    socket_listen($sock);
    $client = socket_accept($sock);

    $input = socket_read($client, 1024);

    $incoming = array();
    $incoming = explode("\r\n", $input);

    $fetchArray = array();
    $fetchArray = explode(" ", $incoming[0]);

    $file = $fetchArray[1];
    if($file == "/"){ 

        $file = "index.html"; 

    } else {

        $filearray = array();
        $filearray = explode("/", $file);
        $file = $filearray[1];
    }

echo $fetchArray[0] . " Request " . $file . "\n"; 

$output = "";
$Header = "HTTP/1.1 200 OK \r\n" .
"Date: Fri, 31 Dec 1999 23:59:59 GMT \r\n" .
"Content-Type: text/html \r\n\r\n";

$Content = file_get_contents($file);
$output = $Header . $Content;

    socket_write($client,$output,strlen($output));
    socket_close($client);
}

这里的代码将恢复请求数据包的头部以查找所请求的文件,然后它将转到该文件的本地目录。目前仅适用于HTML,不支持图像,但可以作为非常轻量级的Web服务器使用。此外,它当前默认为index.html,因此只需保存它,在同一目录中放置一些HTML文件并指向浏览器即可。
希望这有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接