PHP使用socket实现聊天通讯
代码进行简单配置,能直接跑起来。直接上代码
class ws_socket{
public $socket = null; //服务端创建的socket
public $sockts = []; //连接池
public $user= []; //保存所有链接的用户
public $write = null; //socket_select第二个参数
public $except = null; //socket_select第三个参数
public function __construct($ip,$port){
$this->socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); //创建
socket_set_option($this->socket,SOL_SOCKET,SO_REUSEADDR,true); //设置
socket_bind($this->socket,$ip,$port); //绑定
socket_listen($this->socket); //监听
}
//用户连接开始聊天
public function run(){
$this->sockts[] = $this->socket;
while(true){
$tmp_sockets = $this->sockts;
socket_select($tmp_sockets,$write,$except,null); //关键函数,IO复用,多用户链接(1,2,3参数都是引用传值)
foreach($tmp_sockets as $sock){
if($sock==$this->socket){
$conSock = socket_accept($this->socket); //新用户,保存到连接池
$this->sockts[] = $conSock;
$this->user[] = ['socket'=>$conSock,'handshake'=>false];
}else{
$request = socket_read($sock,8024); //接收用户消息
$k = $this->getUserIndex($sock); //找到当前发送用的在连接池的$k
if(strlen($request)==8){ //用户退出 关闭该连接池
$this->close($k); //关闭当前关闭的链接
continue;
}
//判断是否有握手,没有握手先握手,有握手直接发生消息
if(!$this->user[$k]['handshake']){
$response = $this->handleShake($request);
socket_write($sock,$response,strlen($response));
$this->user[$k]['handshake'] = true;
}else{
$msg = $this->decode($request);
$jsonArr = json_decode($msg);
$this->send($jsonArr,$k);
}
}
}
}
}
//关闭链接
private function close($k){
//找到关闭的链接 并在连接池删除
socket_close($this->user[$k]['socket']);
unset($this->user[$k]);
//将连接池清空,再将user重新赋值给连接池
$this->sockts = null;
$this->sockts[] = $this->socket;
foreach($this->user as $v){
$this->sockts[] = $v['socket'];
}
}
//获取用户的$k
private function getUserIndex($sock){
foreach($this->user as $k=>$v){
if($v['socket']==$sock){
return $k;
}
}
}
//编码函数
private function encode($msg) {
$frame = [];
$frame[0] = '81';
$len = strlen($msg);
if ($len < 126) {
$frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
} else if ($len < 65025) {
$s = dechex($len);
$frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
} else {
$s = dechex($len);
$frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
}
$data = '';
$l = strlen($msg);
for ($i = 0; $i < $l; $i++) {
$data .= dechex(ord($msg{$i}));
}
$frame[2] = $data;
$data = implode('', $frame);
return pack("H*", $data);
}
//编码函数
private function decode($buffer) {
$decoded = '';
if ( !empty($buffer[1]) ) {
$len = ord($buffer[1]) & 127;
}else{
$len = '';
}
if ($len === 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} else if ($len === 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
for ($index = 0; $index < strlen($data); $index++) {
$decoded .= $data[$index] ^ $masks[$index % 4];
}
return $decoded;
}
//握手函数
private function handleShake($request){
$buf = substr($request,strpos($request,'Sec-WebSocket-Key:')+18);
$key = trim(substr($buf,0,strpos($buf,"\r\n")));
$new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$new_message = "HTTP/1.1 101 Switching Protocols\r\n";
$new_message .= "Upgrade: websocket\r\n";
$new_message .= "Sec-WebSocket-Version: 13\r\n";
$new_message .= "Connection: Upgrade\r\n";
$new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
return $new_message;
}
//发送消息,业务处理 $msg参数是客服端发送过来的信息
private function send($msg,$k){
//需要发送的数据
$res['replyname'] = '小白';
$res['replyid'] = 1;
$res['msgtype'] = 2;
$res['isline'] = '在线';
$res = $this->encode(json_encode($res));
socket_write($this->user[$k]['socket'],$res,strlen($res)); //发送
}
}
$obj = new Ws(0,8888);
$obj->run();
后面会把webSocket的代码也分享出来,有什么问题评论留言
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库