一、项目介绍
因为公司需要接触到PHP的WebSocket开发,这是用了一段时间写了一个简单的即时通讯服务端,开发过程中发现在PHP中开发WebSocket和C原因的Socket开发有些像,都有Select模型,但是一直没找到PHP中的poll和epol。项目可以实现用户对用户的消息发送。
二、项目优缺点
这里用到Select就说说Select的有点和缺点:
1、优点:使用的是单线程高并发,开发简单。
2、缺点:
Select每次轮训接收客户的相应都需要将用户的文件描述符从用户态复制到内核态,当用户量大时文件文件描述符的复制都是很大的开销。PHP中使用select是单线程运行所以处理能力有限。
结合优缺点总结,在PHP中使用Select模型做即时通讯可以满足一般的网站的客服系统,如果要做为IM聊天软件的后台服务局限还是很大的。所以这里我只是实现了简单的消息发送功能,之后我会使用Swool实现,并且完成群组的创建于群消息的转发。
三、项目的主要部分
1、项目结构
项目下载地址:https://download.csdn.net/download/qq_34950682/12276042
运行服务端后用打开网页链接,用户链接会从0开始分配ID,网页中可将消息发送到指定ID用户。
2、项目主要代码
//创建一个IPV4 的双功 TCP链接
$socketServer = socket_create(AF_INET, SOCK_STREAM, getprotobyname("tcp"));
// 配置socket 端口重用
socket_set_option($socketServer, SOL_SOCKET, SO_REUSEADDR, 1);
// 绑定端口
socket_bind($socketServer, $this->addr, $this->port);
//设置为非阻塞模型
//socket_set_nonblock($socketServer);
socket_set_block($socketServer);
//设置同时握手客户端数量
socket_listen($socketServer, 100);
$reader_temp = array($socketServer);
$reader_io = $reader_temp;
$writer_io = null; //不用监听写数据流
$except_io = null; //不用监听异常数据流
while (true) {
// select模式 监听写入数据事件
socket_select($reader_io, $writer_io, $except_io, 1);
/**
* 接收新消息将消息路由到指定位置处理
*/
if (!empty($reader_io)) {
foreach ($reader_io as $io_key => $io_item) {
if ($io_item === $socketServer) {
//新用户连接
$client = socket_accept($socketServer);
if ($client === false) {
continue;
}
$line = trim(socket_read($client, 1024));
if ($line === false) {
socket_shutdown($client);
socket_close($client);
continue;
}
$this->handshaking($client, $line);
$resultUUID = $this->messageController->addUser($client);
if ($resultUUID != null) {
// TODO 集群时新用户需要加入到Redis
$reader_temp[] = $client;
}
} else {
$result = socket_recv($io_item, $buffer, 2048, MSG_DONTWAIT);
if ($result == false || empty($buffer) || (ord($buffer[0]) & 15) == 8) {
echo "客户端断开网络";
$this->messageController->removeUser($io_item);
//用户断开需要将用户从在线用户中移除
unset($reader_temp[$io_key]);
}
//空消息不处理
if (strlen($buffer)<=6){
continue;
}
$msg = json_decode($msg);
if(empty($msg)){
echo "消息解码失败\n";
}
echo $msg;
}
}
}
//处理完后需要将在线用户重新添加到Select的监听中
$reader_io = $reader_temp;
$this->getMessageTask();
}
//程序退出关闭所有数据流
foreach ($reader_io as $io_item) {
socket_close($io_item);
}
}