php socket 用户名密码,PHP socket | 从一个简单的demo开始认识socket

在数据变动比较频繁时对实时显示有一定要求、存在多个客户端的一对多的业务模式时可以考虑使用socket解决问题。

socket的概念和应用场景相信你一定可以通过搜索引擎检索到更多、更详细的信息,本文就不重复了。

socket通信是由两部分组成,一是server,二是client。一个server可以对多个client同时提供服务,以解决高并发和实时通信的问题。下面是demo code。

client.php

php模拟socket客户端,与socket服务端进行信息交互。

/*

+-------------------------------

* @socket连接整个过程

+-------------------------------

* @socket_create

* @socket_connect

* @socket_write

* @socket_read

* @socket_close

+--------------------------------

*/

$port = 9999;

$ip = "127.0.0.1";

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if ($socket < 0) {

echo "socket_create() failed: reason: " . socket_strerror($socket) . "\n";

}else {

echo "socket已创建.\n";

}

echo "试图连接 '$ip' 端口 '$port'...\n";

$result = socket_connect($socket, $ip, $port);

if ($result < 0) {

echo "socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";

}else {

echo "已连接\n";

}

$in = "hello\r\n";

$in .= "world\r\n";

$out = '';

if(!socket_write($socket, $in, strlen($in))) {

echo "socket_write() failed: reason: " . socket_strerror($socket) . "\n";

}else {

echo "发送的内容为:$in";

}

$out = socket_read($socket, 9999);

echo "接受的内容为:",$out;

socket_close($socket);

echo "关闭SOCKET...\n";

server01.php

最简单socket服务器

$host = '127.0.0.1';

$port = 9999;

// 创建一个tcp socket

$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );

// 将socket bind到IP:port上

socket_bind( $listen_socket, $host, $port );

// 开始监听socket

socket_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 );

现在使用 server01.php 作为服务端提供socket服务,client.php 作为客户端与 server01.php 进行信息交互。下面使用ssh的方式演示。

一个简易的demo已经完成了,但上面的 server01.php 存在缺陷。

一次只可以为一个客户端提供服务,如果正在为第一个客户端发送helloworld期间有第二个客户端来连接,那么第二个客户端就必须要等待片刻才行。

很容易受到攻击,造成拒绝服务。

好的,我们继续改造server。

server02.php

我们可以在accpet到一个请求后就fork一个子进程来处理这个客户端的请求,这样当accept了第二个客户端后再fork一个子进程来处理第二个客户端的请求,这样问题就解决了。

需要注意的是:在 UNIX / Linux  系统中,一个进程结束了,但是他的父进程没有等待(调用 wait / waitpid )他, 那么他将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。

$host = '127.0.0.1';

$port = 9999;

// 创建一个tcp socket

$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );

// 将socket bind到IP:port上

socket_bind( $listen_socket, $host, $port );

// 开始监听socket

socket_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 ) );

echo time().' : a new client'.PHP_EOL;

socket_close( $connection_socket );

exit;

}

// 回收子进程,释放资源,避免出现僵尸进程

pcntl_waitpid($pid, $status, WNOHANG);

}

没错, server02.php 仍然存在2个问题。

客户端数量较多时,会fork出请求量对应的进程数。fork本身就是一个很浪费系统资源的系统调用,大量的fork足以让系统崩溃。

依然没解决拒绝服务攻击。

继续改造…

server03.php

可以预估一下业务量,然后在服务启动的时候就fork出固定数量的子进程,每个子进程处于无限循环中并阻塞在accept上,当有客户端连接挤进来就处理客户请求,当处理完成后仅仅关闭连接但本身并不销毁,而是继续等待下一个客户端的请求。这样,不仅避免了进程反复fork销毁巨大资源浪费,而且通过固定数量的子进程来保护系统不会因无限fork而崩溃。

$host = '127.0.0.1';

$port = 9999;

// 创建一个tcp socket

$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );

// 将socket bind到IP:port上

socket_bind( $listen_socket, $host, $port );

// 开始监听socket

socket_listen( $listen_socket );

// 给主进程换个名字

cli_set_process_title( 'phpserver master process' );

// 按照数量fork出固定个数子进程

for( $i = 1; $i <= 1; $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 );

pcntl_waitpid($pid, $status, WNOHANG);

附赠两条命令

//找出僵死进程

ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'

//查找僵死进程,然后将父进程杀死

ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值