<?php
set_time_limit(0);
class SocketService
{
private $address = 'localhost';
private $port = 80;
private $_sockets;
public function __construct($address = '', $port='')
{
if(!empty($address)){
$this->address = $address;
}
if(!empty($port)) {
$this->port = $port;
}
}
public function service(){
//获取tcp协议号码。
$tcp = getprotobyname("SOL_TCP"); # 获取与协议名称关联的协议号
$sock = socket_create(AF_INET, SOCK_STREAM, $tcp); # 创建一个套接字(通讯节点)
socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); # 设置套接字选项
if($sock < 0)
{
throw new Exception("failed to create socket: ".socket_strerror($sock)."\n");
}
socket_bind($sock, $this->address, $this->port); # 绑定
socket_listen($sock, $this->port); # 监听套接字上的连接
$this->_sockets = $sock;
}
public function run(){
$this->service();
$clients[] = $this->_sockets; # 数组存储 每个socket
# 让服务器无限获取客户端传过来的信息
while (true){
$changes = $clients;
$write = NULL;
$except = NULL;
socket_select($changes, $write, $except, NULL);
foreach ($changes as $key => $_sock){
if($this->_sockets == $_sock){ # 判断是不是新接入的socket
if(($newClient = socket_accept($_sock)) === false){ # 接受新的套接字上的连接 socket_accept的作用就是接受socket_bind()所绑定的主机发过来的套接流
die('failed to accept socket: '.socket_strerror($_sock)."\n"); # 返回描述套接字错误的字符串
}
$line = trim(socket_read($newClient, 1024)); # 读取客户端传过来的资源,并转化为字符串 socket_read的作用就是读出socket_accept()的资源并把它转化为字符串
$this->handshaking($newClient, $line);
//获取client ip
socket_getpeername ($newClient, $ip); # 查询给定套接字的远程端,这可能导致主机/端口或UNIX文件系统路径,具体取决于其类型。
$clients[$ip] = $newClient;
} else {
# 读取该socket的信息,注意:第二个参数是引用传参即接收数据,第三个参数是接收数据的长度
$lenght = socket_recv($_sock, $buffer, 2048, 0); # 从已连接的socket接收数据 $lenght 接收到字符串长度
$msg = $this->message($buffer); # 接收到的信息
//在这里业务代码
fwrite(STDOUT, 'Please input a argument:');
$response = trim(fgets(STDIN));
// $this->send($_sock, $response); # 第二个参数是获取数据 要发送的信息
$this->send($_sock, '在线');
}
}
}
}
/**
* 握手处理
* @param $newClient socket
* @return int 接收到的信息
*/
public function handshaking($newClient, $line){
$headers = array();
$lines = preg_split("/\r\n/", $line); # 通过一个正则表达式分隔字符串。
foreach($lines as $line)
{
$line = chop($line); # 移除字符串右端的空白字符或其他预定义字符
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $this->address\r\n" .
"WebSocket-Location: ws://$this->address:$this->port/服务器地址\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
return socket_write($newClient, $upgrade, strlen($upgrade)); # socket_write的作用是向socket_create的套接流写入信息,或者向socket_accept的套接流写入信息
}
/**
* 解析接收数据
* @param $buffer
* @return null|string
*/
public function message($buffer){
$len = $masks = $data = $decoded = null;
$len = ord($buffer[1]) & 127;
if ($len === 126) {
$masks = substr($buff