php实现socket网络编程
【摘要】 之前本人其实写过一个tcp多进程服务器了http://www.php20.cn/article/139,本文将总结以及完善php实现网络服务器相关代码php实现tcp服务器tcp服务器的实现,其实和c语言实现的步骤差不多,大概为:创建一个socket绑定socket为tcp,到网卡中监听socket(将socket改为可接受其他进程的请求)阻塞/非阻塞,循环获取连接事件<?php$liste...
之前本人其实写过一个tcp多进程服务器了http://www.php20.cn/article/139,本文将总结以及完善php实现网络服务器相关代码
php实现tcp服务器
tcp服务器的实现,其实和c语言实现的步骤差不多,大概为:
创建一个socket
绑定socket为tcp,到网卡中
监听socket(将socket改为可接受其他进程的请求)
阻塞/非阻塞,循环获取连接事件
<?php
$listen_host = '0.0.0.0';
$port = '8080';
$tcp_socket = socket_create(AF_INET/*ipv4*/,SOCK_STREAM/*tcp类型*/,SOL_TCP/*tcp*/);
if ($tcp_socket===false){
die("socket_create() fail:" . socket_strerror(socket_last_error()) . "/n");
}
socket_bind($tcp_socket,$listen_host ,$port)||die('bind error');//绑定
socket_listen($tcp_socket)||die('listen error');//监听
socket_set_nonblock($tcp_socket);//设置非阻塞
socket_set_option($tcp_socket,SOL_SOCKET,SO_REUSEADDR,1);//SO_REUSEADDR 可避免程序意外退出,导致端口绑定失败
$client_arr = [];
while(1){
$connection = socket_accept($tcp_socket);
if($connection){//如果有新链接,则处理新链接
echo $connection."连接成功\n";
$client_arr[$connection] = $connection;
socket_set_nonblock($connection);//设置客户端非阻塞
}
//处理客户端
foreach ($client_arr as $client){
$data='';
$result = @socket_recv($client,$data, 1024,MSG_DONTWAIT);
if($result===false){//出错
$error_id = socket_last_error();
if($error_id!==11){
socket_clear_error();
throw new Exception(socket_strerror($error_id));
}
}elseif ($result===0){//客户端关闭
echo $client."客户端关闭\n";
unset($client_arr[$client]);
}else {
echo $client."客户端发送消息:".$data."\n";
socket_write($client,$data,strlen($data));
}
}
usleep(1);//避免while true死循环卡死
}
复制
除了使用socket函数可以创建一个tcp服务器外,我们还可以通过steam流函数创建:
<?php
$listen_host = '0.0.0.0';
$port = '8080';
$tcp_socket = stream_socket_server("tcp://{$listen_host }:$port", $errno, $errstr);
//直接创建一个监听tcp的套接字
$tcp_socket || die("$errstr ($errno)\n");
stream_set_blocking($tcp_socket, 0);//设置非阻塞
$client_arr = [];
while (1) {
set_error_handler(function(){});//socket错误拦截
$connection = stream_socket_accept($tcp_socket,0,$remote_address);
restore_error_handler();
if ($connection) {//如果有新链接,则处理新链接
echo $connection . "连接成功\n";
$client_arr[$connection] = $connection;
stream_set_blocking($connection, 0);//设置客户端非阻塞
}
//处理客户端
foreach ($client_arr as $client){
$data = fread($client, 65535);
if ($data === '' || $data === false) {
if ( (feof($client) || $buffer === false)) {//客户端关闭
echo $client."客户端关闭\n";
unset($client_arr[$client]);
}
}
if(!empty($data)){
fwrite($client,$data);//发送给客户端
echo $client."客户端发送消息:".$data."\n";
}else{
continue;
}
}
usleep(1);//避免while true死循环卡死
}
复制
php实现tcp客户端
tcp客户端的实现步骤是:
先创建一个tcpsocket
通过socket_connect连接
接收/发送消息
<?php
$host='192.168.159.1';
$port='8080';
$client_socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);//创建一个tcp的socket
$connection = socket_connect($client_socket,$host,$port);//连接
socket_write($client_socket, "hello socket") or die("Write failed\n"); // 数据传送 向服务器发送消息
while(1){
$buffer = socket_read($client_socket, 1024, PHP_BINARY_READ);//默认阻塞类型,没有消息会一直阻塞
if (empty($buffer)){
die("已断开");
}
echo "服务端发送:".$buffer.PHP_EOL;
}
复制
同样,我们可以通过流函数进行创建一个tcp客户端:
<?php
$host='192.168.159.1';
//$host = '127.0.0.1';
$port = '8080';
$client_socket = stream_socket_client("tcp://$host:$port", $errno, $errstr, 30);
fwrite($client_socket, "hello socket");
while (!feof($client_socket)) {
$buffer = fread($client_socket,255);//默认阻塞类型,没有消息会一直阻塞
if(empty($buffer)){
echo "客户端关闭\n";
fclose($client_socket);
break;
}
echo "服务端发送:" . $buffer . PHP_EOL;
sleep(1);
}
复制
php实现udp服务端
udp是无连接的协议,我们不需要去额外的创建客户端的socket进行一对一的传输,直接可通过创建udp服务端的socket,接收/发送所有的客户端:
<?php
$listen_host = '0.0.0.0';
$port = '8080';
if (($udp_socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) == FALSE) {
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("创建udp socekt失败: [$errorcode] $errormsg");
}
socket_bind($udp_socket,$listen_host,$port)||die('bind error');
while(1){
$data = socket_recvfrom($udp_socket, $buf, 512, 0, $remote_ip, $remote_port);
var_dump(base64_decode($buf));//udp直接发送中文将会出现乱码问题,可通过传输pack二进制包或者base64等方法解决
$msg="客户端发送的是:".$buf;
$msg.="客户端ip:".$remote_ip;
$msg.="客户端端口:".$remote_port;
//Send back
socket_sendto($udp_socket, $msg, strlen($msg), 0, $remote_ip, $remote_port);
}
复制
同样,我们可以通过流函数创建udp服务端:
<?php
$listen_host = '0.0.0.0';
$port = '8080';
$socket = stream_socket_server("udp://{$listen_host }:{$port}", $errno, $errstr, STREAM_SERVER_BIND);
if (!$socket) {
die("$errstr ($errno)");
}
do {
//接收客户端发来的信息
$inMsg = stream_socket_recvfrom($socket, 1024, 0, $peer);//udp无需握手,不需要获取客户端一对一获取数据,直接接收
//服务端打印出相关信息
echo "Client : $peer\n";
echo "Receive : {$inMsg}\n";
//给客户端发送信息
$outMsg = "$inMsg";
stream_socket_sendto($socket, $outMsg, 0, $peer);
} while ($inMsg !== false)
复制
在上面的实现过程中,recvfrom都是阻塞的,这种情况会造成我们无法主动给客户端发送消息,我们可以参考tcp服务器的非阻塞实现,进行修改代码
php实现udp客户端
通过udp实现服务端的代码,我们可以发现:
只要有服务端的ip和端口(废话,没端口怎么通信),我们就可以用udp服务端的代码当成udp客户端使用(需要注意recvfrom阻塞问题).
<?php
$listen_host = '0.0.0.0';
$port = '8080';
if (($udp_socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) == FALSE) {
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("创建udp socekt失败: [$errorcode] $errormsg");
}
socket_bind($udp_socket,$listen_host,$port)||die('bind error');
socket_set_nonblock($udp_socket);//设置非阻塞
while(1){
$remote_ip='192.168.159.1';
$remote_port="8080";
$msg="I'm client";
//Send back
socket_sendto($udp_socket, $msg, strlen($msg), 0, $remote_ip, $remote_port);//每隔一秒给192.168.159.1发送数据
sleep(1);
}
复制
除了使用服务端代码可以实现,我们也可以用客户端代码实现:
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$msg = 'hello';
$len = strlen($msg);
socket_sendto($socket, $msg, $len, 0, '192.168.159.1', 8080);
while(1){
$data = socket_recvfrom($socket, $buf, 512, 0, $remote_ip, $remote_port);
var_dump(base64_decode($buf));//udp直接发送中文将会出现乱码问题,可通过传输pack二进制包或者base64等方法解决
$msg="客户端发送的是:".$buf;
$msg.="客户端ip:".$remote_ip;
$msg.="客户端端口:".$remote_port;
//Send back
socket_sendto($socket, $msg, strlen($msg), 0, $remote_ip, $remote_port);
sleep(1);
}
复制
流函数创建:
<?php
$host='192.168.159.1';
$port = '8080';
$client_socket = stream_socket_client("udp://$host:$port", $errno, $errstr, 30);
fwrite($client_socket, "hello socket");
while (!feof($client_socket)) {
$buffer = fread($client_socket,255);//默认阻塞类型,没有消息会一直阻塞
echo "服务端发送:" . $buffer . PHP_EOL;
sleep(1);
}
复制
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)