怎么推广我做的网站,知名网站制作公司,jplayer wordpress,一个好的网站的重要性PHP常用socket创建TCP连接#xff0c;使用CURL创建HTTP连接#xff0c;为了简化操作#xff0c;Swoole提供了Client类用于实现客户端功能#xff0c;并增加了异步非阻塞模式#xff0c;让用户在客户端也能使用事件循环。
作为客户端使用#xff0c;Swoole Client可以在F…PHP常用socket创建TCP连接使用CURL创建HTTP连接为了简化操作Swoole提供了Client类用于实现客户端功能并增加了异步非阻塞模式让用户在客户端也能使用事件循环。
作为客户端使用Swoole Client可以在FPM环境下或 Apache中使用但不允许使用Async异步模式只能使用同步非阻塞模式异步非阻塞模式仅限CLI模式下使用。
Client提供了TCP/UDP socket的客户端的封装代码使用时仅需new Swoole\Client。
Swoole的socket客户端对比PHP提供的stream族函数有什么优势呢
stream函数存在超时设置的陷阱和Bug一旦没有处理好将会导致服务器长时间阻塞。 stream和fread有8192长度限制无法支持UDP大包。 swoole_client客户端支持waitall在知道包长度的情况下可以一次取完不必循环获取。 swoole_client支持UDP的connect解决了UDP串包的问题 swoole_client是纯C的代码 swoole_client支持异步非阻塞回调 构造方法 constructor Swoole的Client功能封装为一个swoole_client类可通过new swoole_client来创建一个客户端实例。
原型
swoole_client-__construct(int $sock_type, int $is_async SWOOLEL_SOCK_SYNC, string $key
);
参数参数1int $sock_type $sock_type 表示socket类型可使用swoole提供的宏来指定类型。
SWOOLE_TCP 创建TCP SocketSWOOLE_TCP6 创建IPv6 TCP SocketSWOOLE_UDP 创建UDP SocketSWOOLE_UDP6 创建IPv6 UDP SocketSWOOLE_SSL 开启SSL加密SWOOLE_KEEP 开启连接复用
其中SWOOLE_SSL与SWOOLE_KEEP不能单独使用需要与前四个选项共同作用。
// 创建并开启SSL加密的TCP客户端
$client new swoole_client(SWOOLE_TCP | SWOOLE_SSL);
SWOOLE_KEEP
swoole_client支持在PHP-FPM或Apache中建立一个TCP长连接到服务器当客户端启用SWOOLE_KEEP选项后一个请求结束不会关闭socket下一次再进行connect连接时会自动复用上次创建的连接。如果执行connect连接时发现连接已经 被服务器断开那么connect将会创建新的连接。
// 创建一个可以在FPM中使用的长连接客户端
$client new swoole_client(SWOOLE_TCP | SWOOLE_KEEP); SWOOLE_KEEP建立TCP长连接有什么优势呢
TCP长连接可以减少connect建立连接时的三次握手和close断开连接的四次挥手所带来的额外IO开销 降低服务器connect建立连接和close断开连接的次数 例如在PHP-FPM模式下可以使用同步模式的swoole_client向后端发送数据为了能够让不同的连接复用同一个客户端client可以开启SWOOLE_KEEP选项并指定对应的key。这样对于同一个逻辑就不需要为每个请求都创建一个新的连接了。这里需要注意的是虽然共用了同一个连接但是每个请求仍然会创建一个新的swoole_client实例只是这些实例底层会复用同一个TCP连接。因此如果开启了SWOOLE_KEEP选项也就不能随便调用close关闭客户端并且在使用时可使用isConnect方法来判断连接是否可用。
?php
//获取参数
$data isset($_GET[data])!empty($_GET[data]) ? $_GET[data] : ;
//创建同步阻塞TCP客户端并开启连接复用。
$host 127.0.0.1;
$port 9501;
$key $host.:.$port;//默认使用IP:PORT作为长连接的key具有相同key的连接会被复用。
$client new swoole_client(SWOOLE_TCP | SWOOLE_KEEP, SWOOLE_SYNC, $key);
//判断客户端时已连接
if(!$client-isConnect()){$client-connect($host, $port);
}
//发送数据
$client-send($data);
//接收打印
echo $client-recv();
参数2int $is_sync $is_sync表示同步阻塞或异步非阻塞默认为同步阻塞。模式不同决定了可以使用的API的不同。
SWOOLE_SOCK_SYNC
SWOOLE_SOCK_SYNC 表示创建一个同步阻塞客户端默认设置。
swoole_client(SWOOLE_TCP, SWOOLE_SYNC, $key); 当设定swoole_client为同步模式后可以像使用PHP的sockets扩展也一样使用swoole_client来创建socket连接。由于是同步阻塞模式所以connect、recv、send这样的方法都会阻塞进程。相比较PHP的sockets扩展提供的方法swoole_client的API更为简洁使用起来更加方便。
?php
/*** 创建同步阻塞模式下的TCP客户端* 同步阻塞模式下connect/send/recv会等待IO完成后再返回服务端返回后才会向下执行。* 同步阻塞模式下并不会消耗CPU资源IO操作未完成当前进程会自动转入sleep模式。* 当IO完成后操作系统会唤醒当前进程继续向下执行代码。* */
$client new swoole_client(SWOOLE_TCP);//连接到服务器
$host 127.0.0.1;
$port 9501;
$timeout 1;//超过与服务器交互的超时秒数会自动断开
if(!$client-connect($host, $port, $timeout)){die([connect] failed.PHP_EOL);
}//发送数据
$message hello world;
if(!$client-send($message)){die([send] failed.PHP_EOL);
}//接收数据
if(!$data $client-recv()){die([recv] failed.PHP_EOL);
}
echo $data.PHP_EOL;//关闭连接
$client-close(); SWOOLE_SOCK_ASYNC
SWOOLE_SOCK_ASYNC 表示创建一个异步非阻塞客户端
需要通过on方法注册异步回调函数 多个swoole_client客户端可以嵌套回调 异步模式仅可用于CLI命令行模式
swoole_client(SWOOLE_TCP, SWOOLE_SOCK_ASYNC) 当设置swoole_client为异步模式后swoole_client就不能再使用recv方法了而是需要通过on方法提供指定的回调函数然后在回调函数当中处理。
异步模式的swoole_client必须设置四种回调函数connect、receive、error、close不能够缺省否则在调用connect连接时会提示回调未设置。同样因为已经有了onConnect回调因此异步模式的swoole_client调用connect方法时不再阻塞connect方法也只会返回true此时需要在onConnect回调中确定连接成功或者在onError回调中确定连接失败。
?php
/*** 创建异步非阻塞模式下的TCP客户端* 必须设置onConnect、onError、onReceive、onClose* */
$client new swoole_client(SWOOLE_TCP, SWOOLE_SOCK_ASYNC);//连接成功时回调
$client-on(connect, function(swoole_client $client){//向服务器发送数据$message hello world.PHP_EOL;$client-send($message);
});//数据接收时回调
$client-on(receive, function(swoole_client $client, $data){if(empty($data)){$client-close();}else{echo [receive] $data.PHP_EOL;sleep(1);$client-send(time());}
});//连接失败时回调
$client-on(error, function(swoole_client $client){echo [error] connection failed.PHP_EOL;
});//注册关闭连接时回调
$client-on(close, function(swoole_client $client){echo [close] connection close.PHP_EOL;
});//连接到服务器
$host 127.0.0.1;
$port 9501;
$timeout 0.5;//超过与服务器交互的超时秒数会自动断开
if(!$client-connect($host, $port, $timeout)){die([connect] failed.PHP_EOL);
} 异步模式的swoole_client跟swoole_server一样以事件作为驱动而无法发像同步模式一样来驱动。只要提供需要的事件就能过够处理逻辑如swoole的定时器、事件循环等。
例如实现客户端每秒向服务器发送一个心跳包在接收到终端输入的内容后该客户端会将终端输入的内容发送给服务器。
服务器
$ vim server.php ?php
//创建TCP服务器并设置IP和端口
$host 0.0.0.0;
$port 9501;
$serv new swoole_server($host, $port);//设置服务器运行时配置
$configs [];
$configs[worker_num] 2;//设置Worker工作进程数量
$configs[task_worker_num] 2;//设置Task异步任务的进程数量
$serv-set($configs);//Master主进程 当服务器启动时触发
$serv-on(start, function(swoole_server $serv){echo PHP_EOL.[start] master {$serv-master_pid} manager {$serv-manager_pid}.PHP_EOL;
});//Worker工作进程 当客户端有新TCP连接时触发
$serv-on(connect, function(swoole_server $serv, $fd, $reactor_id){echo PHP_EOL.[connect] worker {$serv-worker_pid} reactor {$reactor_id} client {$fd}.PHP_EOL;
});//Worker工作进程 当客户端连接向服务器发送数据时触发
$serv-on(receive, function(swoole_server $serv, $fd, $reactor_id, $data){echo [receive] worker {$serv-worker_pid} reactor {$reactor_id} client {$fd} data:{$data}.PHP_EOL;if($data ! PING){//投递异步任务给TaskWorker异步任务进程程序会立即返回向下执行后续代码。$task_id $serv-task($data);echo [receive] task {$task_id} data:{$data}.PHP_EOL;//向客户端文件描述符发送字符串信息$message success;$serv-send($fd, $message);echo [receive] client {$fd} send:{$message}.PHP_EOL;}
});//TaskWorker任务进程 处理异步任务
$serv-on(task, function(swoole_server $serv, $task_id, $reactor_id, $data){echo [task] task {$task_id} reactor {$reactor_id} data:{$data}.PHP_EOL;sleep(10);//模拟异步操作执行时长10秒//返回任务执行的结果$serv-finish(finish);
});//Worker进程 处理异步任务完成的结果
$serv-on(finish, function(swoole_server $serv, $task_id, $data){echo [finish] worker {$serv-worker_pid} task {$task_id} data:{$data}.PHP_EOL;
});//Worker进程 监听断开连接时触发
$serv-on(close, function(swoole_server $serv, $fd, $reactor_id){//连接断开类型$disconnect_type client;//客户端主动断开if($reactor_id 0){$disconnect_type server;//服务器主动断开}echo [close] worker {$serv-manager_pid} client {$fd} disconnect {$disconnect_type}.PHP_EOL;
});//启动服务器
$serv-start(); 客户端
$ vim client.php
?php
class Client
{private $client;private $timer_id;/**构造函数 */public function __construct($host, $port, $timeout1){//构建异步非阻塞客户端对象$this-client new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);//绑定事件回调函数$this-client-on(Connect, [$this, onConnect]);$this-client-on(Receive, [$this, onReceive]);$this-client-on(Close, [$this, onClose]);$this-client-on(Error, [$this, onError]);//连接服务器$this-client-connect($host, $port, $timeout1);}/**连接服务器 */public function connect($host, $port, $timeout1){//连接服务器$fp $this-client-connect($host, $port, $timeout);if(!$fp){echo [connect] error{$fp-errCode} {$fp-errMsg}.PHP_EOL;return;}}/**连接成功回调 */public function onConnect($client){//将标准输入添加到swoole的事件监听中swoole_event_add(STDIN, function($fp){//读取标准输入$message trim(fgets(STDIN));if(!empty($message)){//发送数据$this-client-send($message);}});//添加定时器每秒向服务器发送一个心跳包$this-timer_id swoole_timer_tick(1000, function(){$this-client-send(PING);});}public function send($message){$this-client-send($message);}public function onReceive(swoole_client $client, $data){echo [receive] {$data}.PHP_EOL;}public function onClose(swoole_client $client){$data json_encode($client);echo [close] {$data}.PHP_EOL;//断开连接时清除定时器swoole_timer_clear($this-timer_id);}public function onError(swoole_client $client){$data json_encode($client);echo [error] {$data}.PHP_EOL;}
}
$client new Client(127.0.0.1, 9501, 1);
参数3string $key $key 表示用于长连接的key默认使用IP:PORT作为key相同key的连接会被复用。
对象属性
?php
$client new swoole_client(SWOOLE_TCP, SWOOLE_ASYNC);
echo json_encode($client).PHP_EOL; {errCode:0,sock:-1,reuse:false,reuseCount:0,type:1025,id:null,setting:null
}
错误码 int swoole_client-errCode 当connect、send、recv、close失败时会自动设置$swoole_client-errCode的值errCode的值等于Linux的errno可以使用socket_strerror将错误码转换为错误信息。
$client-on(close, function(swoole_client $client){if(!$client-errCode){$error socket_strerror($client-errCode);echo error: {$error}.PHP_EOL;}echo close.PHP_EOL;
});
Socket文件描述符 int swoole_client-sock sock属性值为整型的socket文件描述符可转换int作为数组的key。
// 在PHP代码中可以使用
$sock fopen(php://fd/.$swoole_client-sock); $client-sock属性值仅在$client-connect后才能取出在未连接服务器之前此属性值为null。将swoole_client的socket转换为一个stream socket可调用fread、fwrite、fclose等函数进行操作。swoole_server中的$fd文件描述符不能使用sock()方法转换因为$fd文件描述符只是一个属于主进程的数字。
是否复用 bool swoole_client-reuse reuse属性值表示连接是新连接的还是复用已存在的是一个布尔值需要与SWOOLE_KEEP配合使用。
例如WebSocket客户端与服务器建立连接后需要进行握手如果连接是复用的就无需再次进行握手直接发送WebSocket数据帧即可。
if($client-reuse){$client-send($data);
}
建立连接 connect connect方法用于建立连接到远程服务器
原型
bool $server_client-connect(string $host, int $port, float $timeout 0.5, int $flag 0
);
参数 参数1string $host 表示远程服务器的地址swoole1.10.0已支持自动异步解析域名即可直接传入域名。 参数2int $port 表示远程服务器的端口 参数3float $timeout 表示网络IO超时秒数包括connect、send、recv单位为秒并支持浮点数默认0.5s即500ms。 参数4int $flag
在UDP类型时表示是否启用udp_connect设定此选项后将绑定$host与$port此UDP将会丢弃非指定host/port的数据包。在TCP类型中$flag1表示设置为非阻塞socketconnect会立即返回。如果将$flag设置为1则在send/recv前必须使用swoole_client_select来检测是否完成了连接。
模式 同步模式 connect方法在同步模式下会发生阻塞直到连接成功并返回true。这个时候就可以向服务器发送数据或接收数据了。如果连接失败则会返回false。另外同步TCP客户端在执行close之后可以再次发起connect创建新连接到服务器。
if($client-connect($host, $port)){$client-send($message);
}else{echo connect failed;
} 异步模式 异步模式下connect连接会立即返回true但实际上连接并未建立所以不能在connect后使用send。此时使用fsConnected()判断也是false。当连接创建成功后系统会自动回调onConnect此时才可以使用send方法向服务器发送数据。
异步客户端执行connect时会增加一次引用计数当连接关闭时会减少引用计数。
版本
小于swoole1.9.11版本中$timeout超时设置在异步客户端中是无效的应用层需要使用Timer::after自行添加定时器来实现异步客户端的链接超时控制。大于等于swoole1.9.11版本中底层会自动添加定时器在规定的时间内未连接成功时底层会触发onError连接失败事件错误码为ETIMEOUT(110)。
失败重连 connect失败后如果希望重连一次必须先进行close关闭旧的socket否则会返回EINPROCESS错误因为当前的socket正在连接服务器客户端并不知道是否连接成功所以无法再次执行connect。
调用close会关闭当前的socket底层会重新创建新的socket来进行连接。
启用SWOOLE_KEEP长连接后close调用的第一个参数需要设置为true来表示强行销毁长连接socket。
//连接失败
if($socket-connect($host, $port) false){$socket-close(true);//关闭旧的socket$socket-connect($host, $port);//重连
}
UDP连接 UDP连接时默认底层并不会启用一个UDP客户端执行连接时底层在创建socket后会立即返回成功此时的socket绑定的地址为0.0.0.0任何其它对端均可向此端口发送数据包。
$client-connect(192.168.1.100, 9502) 此时操作系统会为客户端socket随机分配一个端口其它机器可向这个端口发送数据包。
未开启UDP连接时调用getsockname返回的host为0.0.0.0。
将connect的第四项参数$flag设置为1时将启用UDP连接。
$client-connect(192.168.1.100, 9501, 1, 1) 此时将会绑定客户端和服务器底层会根据服务器的地址来绑定socket绑定的地址例如连接了192.168.1.100当前socket会被绑定到192.168.1.*的本地机器上。启用UDP连接后客户端将不再接收其它主机向此端口发送的数据包。
发送数据 send send方法用于建立连接后发送数据到远程服务器
原型
int $swoole_client-send(string $data);
参数 参数string $data 发送的数据格式为字符串支持二进制。
返回 成功发送返回已发送数据的长度 失败返回false并设置$swoole_client-errCode错误码。模式 1.同步
发送的数据没有长度闲置发送的数据太多时socket缓存区塞满底层会阻塞等待可写。
2.异步
发送数据的长度收到socket_buffer_size限制如果socket缓存区已满swoole的处理逻辑参考swoole_event_write。
注意
如果没有执行连接connect直接调用send会触发PHP警告
接收数据 recv recv方法用于从服务器接收数据
原型 swoole1.7.22-
string $swoole_client-recv(int $size 655335, bool $waitall 0) swoole1.7.22
string $swoole_client-recv(int $size 65535, int $flag 0); swoole1.7.22版本后将原来第二个参数$waitall参数修改为$flags可以接收一些特殊的socket接收设置为了兼容旧的接口如果$flag 1表示 $flag swoole_client::MSG_WAITALL。
$client-recv(8192, swoole_client::MSG_PEEK | swoole_client::MSG_WAITALL);
参数 int $size 表示接收数据的缓存区最大长度此参数设置过大会占用较大内存。 bool $waitall 表示是否等待所有数据到达后返回 如果设置了$waitall就必须设定准确的$size否则会一直等待直到接收的数据长度达到$size。如果未设置$waitall true时$size最大未64K如果设置了错误的$size将会导致recv超时而返回false。
返回 成功成功接收到数据则返回字符串 失败返回false需设置错误码$client-errCode属性 连接关闭返回空字符串
EOL/Length 如果客户端启用了EOF/Length检测后无需设置$size和$waitall参数扩展层会返回完整的数据包或返回false。当接收到错误的包头或包头中长度超过package_max_length设置时recv会返回空字符串PHP代码中应当关闭此连接。
关闭连接 close close用于关闭客户端连接操作成功后返回true。当swoole_client客户端连接被close关闭后不要再次发起连接close。正确的做法是销毁当前的swoole_client然后再重新创建一个swoole_client并发起新的连接。
bool $swoole_client-close(bool $force false); 参数bool $force 表示是否强制关闭连接可用于关闭SWOOLE_KEEP长连接。 注意swoole_client对象在析构时会自动关闭
异步非阻塞客户端close关闭时会立即关闭连接如果发送队列中仍然有数据底层会丢弃。所以请勿在大量发送数据后立即close关闭否则发送的数据未必能真正到达服务器端。
服务器
$ vim server.php ?php
//创建TCP服务器并设置IP和端口
$host 0.0.0.0;
$port 9501;
$server new swoole_server($host, $port);//设置服务器运行时配置
$configs [];
$configs[worker_num] 2;//设置Worker工作进程数量
$configs[task_worker_num] 2;//设置Task异步任务的进程数量
$server-set($configs);//Master主进程 当服务器启动时触发
$server-on(start, function(swoole_server $server){echo PHP_EOL.[start] master {$server-master_pid} manager {$server-manager_pid}.PHP_EOL;
});//Worker工作进程 当客户端有新TCP连接时触发
$server-on(connect, function(swoole_server $server, $fd, $reactor_id){echo PHP_EOL.[connect] worker {$server-worker_pid} reactor {$reactor_id} client {$fd}.PHP_EOL;
});//Worker工作进程 当客户端连接向服务器发送数据时触发
$server-on(receive, function(swoole_server $server, $fd, $reactor_id, $data){echo [receive] worker {$server-worker_pid} reactor {$reactor_id} client {$fd} data:{$data}.PHP_EOL;if($data ! PING){//投递异步任务给TaskWorker异步任务进程程序会立即返回向下执行后续代码。$task_id $server-task($data);echo [receive] task {$task_id} data:{$data}.PHP_EOL;//向客户端文件描述符发送字符串信息$message success;$server-send($fd, $message);echo [receive] client {$fd} send:{$message}.PHP_EOL;}
});//TaskWorker任务进程 处理异步任务
$server-on(task, function(swoole_server $server, $task_id, $reactor_id, $data){echo [task] task {$task_id} reactor {$reactor_id} data:{$data}.PHP_EOL;sleep(10);//模拟异步操作执行时长10秒//返回任务执行的结果$server-finish(finish);
});//Worker进程 处理异步任务完成的结果
$server-on(finish, function(swoole_server $server, $task_id, $data){echo [finish] worker {$server-worker_pid} task {$task_id} data:{$data}.PHP_EOL;
});//Worker进程 监听断开连接时触发
$server-on(close, function(swoole_server $server, $fd, $reactor_id){//连接断开类型$disconnect_type client;//客户端主动断开if($reactor_id 0){$disconnect_type server;//服务器主动断开}echo [close] worker {$server-manager_pid} client {$fd} disconnect {$disconnect_type}.PHP_EOL;
});//启动服务器
$server-start();
客户端
$ vim client.php
客户端发送4MB的数据实际传输可能需要一段时间此时如果立即进行close关闭操作可能只有小部分数据传输成功大部分数据在发送队列中排队等待发送close关闭时会丢失这些数据。
?php
$client new swoole_client(SWOOLE_TCP, SWOOLE_ASYNC);
$client-on(connect, function(swoole_client $client){echo connect.PHP_EOL;//将标准输入添加到事件监听中swoole_event_add(STDIN, function($fp) use($client){$msg trim(fgets(STDIN));$client-send($msg);});
});
$client-on(receive, function(swoole_client $client, $data){echo receive:{$data}.PHP_EOL;$client-send(str_repeat(X, 1024*1024*4).PHP_EOL);$client-close();
});
$client-on(error, function(swoole_client $client){echo error.PHP_EOL;
});
$client-on(close, function(swoole_client $client){echo close.PHP_EOL;
});
$client-connect(127.0.0.1, 9501); 解决方案
配合使用onBufferEmpty等待发送队列为空时进行close操作
?php
$client new swoole_client(SWOOLE_TCP, SWOOLE_ASYNC);
$client-on(connect, function(swoole_client $client){echo connect.PHP_EOL;//将标准输入添加到事件监听中swoole_event_add(STDIN, function($fp) use($client){$msg trim(fgets(STDIN));$client-send($msg);});
});
$client-on(receive, function(swoole_client $client, $data){echo receive:{$data}.PHP_EOL;$client-send(str_repeat(X, 1024*1024*4).PHP_EOL);$client-close();
});
$client-on(error, function(swoole_client $client){echo error.PHP_EOL;
});
$client-on(close, function(swoole_client $client){echo close.PHP_EOL;
});
//配合使用onBufferEmpty等待发送队列为空时进行关闭close操作
$client-on(bufferEmpty, function(swoole_client $client){$client-close();
});
$client-connect(127.0.0.1, 9501); 协议设计为onReceive收到数据后主动关闭连接发送数据时对端主动关闭连接。