socket授予了我们操控传输层和网络层的能力,从而得到更强的性能和更高的效率,socket编程是办理高并发网络做事器的最常用办理和成熟的办理方案。任何一名做事器程序员都应该节制socket编程干系技能。
在php中,可以操控socket的函数一共有两套,一套是socket_系列的函数,另一套是stream_系列的函数。socket_是php直接将C措辞中的socket抄了过来得到的实现,而stream_系则是php利用流的观点将其进行了一层封装。下面用socket_系函数大略为这一系列文章开个篇。
先来做个最大略socket做事器:
<?php$host = '0.0.0.0';$port = 9999;// 创建一个tcp socket$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );// 将socket bind到IP:port上socket_bind( $listen_socket, $host, $port );// 开始监听socketsocket_listen( $listen_socket );// 进入while循环,不用担心去世循环去世机,由于程序将会壅塞不才面的socket_accept()函数上while( true ){ // 此处将会壅塞住,一贯到有客户端来连接做事器。壅塞状态的进程是不会霸占CPU的 // 以是你不用担心while循环会将机器拖垮,不会的 $connection_socket = socket_accept( $listen_socket ); // 向客户端发送一个helloworld $msg = \公众helloworld\r\n\公众; socket_write( $connection_socket, $msg, strlen( $msg ) ); socket_close( $connection_socket );}socket_close( $listen_socket );
将文件保存为server.php,然后实行php server.php运行起来。客户端我们利用telnet就可以了,打开其余一个终端实行telnet 127.0.0.1 9999按下回车即可。运行结果如下:
大略解析一下上述代码来解释一下tcp socket做事器的流程:
1.首先,根据协议族(或地址族)、套接字类型以及详细的的某个协议来创建一个socket。2.第二,将上一步创建好的socket绑定(bind)到一个ip:port上。3.第三,开启监听linten。4.第四,使做事器代码进入无限循环不退出,当没有客户端连接时,程序壅塞在accept上,有连接进来时才会往下实行,然后再次循环下去,为客户端供应持久做事。上面这个案例中,有两个很大的毛病:
1.一次只可以为一个客户端供应做事,如果正在为第一个客户端发送helloworld期间有第二个客户端来连接,那么第二个客户端就必须要等待少焉才行。2.很随意马虎受到攻击,造成谢绝做事。剖析了上述问题后,又遐想到了前面说的多进程,那我们可以在accpet到一个要求后就fork一个子进程来处理这个客户真个要求,这样当accept了第二个客户端后再fork一个子进程来处理第二个客户真个要求,这样问题不就办理了吗?OK!
撸一把代码演示一下:
<?php$host = '0.0.0.0';$port = 9999;// 创建一个tcp socket$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );// 将socket bind到IP:port上socket_bind( $listen_socket, $host, $port );// 开始监听socketsocket_listen( $listen_socket );// 进入while循环,不用担心去世循环去世机,由于程序将会壅塞不才面的socket_accept()函数上while( true ){ // 此处将会壅塞住,一贯到有客户端来连接做事器。壅塞状态的进程是不会霸占CPU的 // 以是你不用担心while循环会将机器拖垮,不会的 $connection_socket = socket_accept( $listen_socket ); // 当accept了新的客户端连接后,就fork出一个子进程专门处理 $pid = pcntl_fork(); // 在子进程中处理当前连接的要求业务 if( 0 == $pid ){ // 向客户端发送一个helloworld $msg = \"大众helloworld\r\n\公众; socket_write( $connection_socket, $msg, strlen( $msg ) ); // 休眠5秒钟,可以用来不雅观察时候可以同时为多个客户端供应做事 echo time().' : a new client'.PHP_EOL; sleep( 5 ); socket_close( $connection_socket ); exit; }}socket_close( $listen_socket );
将代码保存为server.php,然后实行php server.php,客户端依然利用telnet 127.0.0.1 9999,只不过这次我们开启两个终端来实行telnet。重点不雅观察当第一个客户端连接上去后,第二个客户端时候也可以连接上去。运行结果如下:
通过接管到客户端要求的韶光戳可以看到现在做事器可以同时为N个客户端做事的。但是,接着想,如果先后有1万个客户端来要求呢?这个时候做事器会fork出1万个子进程来处理每个客户端连接,这是会去世人的。fork本身便是一个很摧残浪费蹂躏系统资源的系统调用,1W次fork足以让系统崩溃,即便当下系统承受住了1W次fork,那么fork出来的这1W个子进程也够系统内存喝一壶了,末了是好不容易费劲fork出来的子进程在处理完毕当前客户端后又被关闭了,下次要求还要重新fork,这本身便是一种摧残浪费蹂躏,不符合社会主义主流代价不雅观。如果是有人恶意攻击,那么系统fork的数量还会呈直线上涨一贯到系统崩溃。
以是,我们就再次提出匆匆进型办理方案。我们可以预估一下业务量,然后在做事启动的时候就fork出固天命量的子进程,每个子进程处于无限循环中并壅塞在accept上,当有客户端连接挤进来就处理客户要求,当处理完成后仅仅关闭连接但本身并不销毁,而是连续等待下一个客户真个要求。这样,不仅避免了进程反复fork销毁巨大资源摧残浪费蹂躏,而且通过固天命量的子进程来保护系统不会因无限fork而崩溃。
<?php$host = '0.0.0.0';$port = 9999;// 创建一个tcp socket$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );// 将socket bind到IP:port上socket_bind( $listen_socket, $host, $port );// 开始监听socketsocket_listen( $listen_socket );// 给主进程换个名字cli_set_process_title( 'phpserver master process' );// 按照数量fork出固定个数子进程for( $i = 1; $i <= 10; $i++ ){ $pid = pcntl_fork(); if( 0 == $pid ){ cli_set_process_title( 'phpserver worker process' ); while( true ){ $conn_socket = socket_accept( $listen_socket ); $msg = \"大众helloworld\r\n\"大众; socket_write( $conn_socket, $msg, strlen( $msg ) ); socket_close( $conn_socket ); } }}// 主进程不可以退出,代码演示比较粗暴,为了不担保退出直接走while循环,休眠一秒钟// 实际上,主进程真正该做的该当是网络子进程pid,监控各个子进程的状态等等while( true ){ sleep( 1 );}socket_close( $connection_socket );
将文件保存为server.php后php server.php实行,然后再用ps -ef | grep phpserver | grep -v grep来看下做事器进程状态:
可以看到master进程存在,除此之外还有10个子进程处于等待做事状态,再同一个时候可以同时为10个客户端供应做事。我们通过telnet 127.0.0.1 9999来考试测验一下,运行结果如下图:
好啦,php新的征程系列就先通过一个大略的入门开始啦!
下篇将会讲述一些比较深刻的理论根本知识。