常见的协议分3种:
注意:
1、浏览器访问不了,是因为端口没开(或关防火墙)
2、如果你使用curl命令来访问的话,要在新开的(当前shell标签右键单击复制ssh渠道)bash中输入指令
3、开发必读里(守护进程才用的到重启。)
> php 文件名 restart #重启
注意:看图中圈的,客户端的代码要改ip
Ctrl + ] #关闭telnet
在输入quit
注意:如果报错说没有telnet请,可参考https://www.cnblogs.com/ikai/p/7073201.html 安装telnet
建议学习之前先学下websocket。可以参考我这篇博文
http://blog.csdn.net/qq_33862644/article/details/79551729
首先抛开别的不谈,咱先捋明白业务逻辑,不然看了代码也是懵逼
既然做聊天室(说白了就是类似QQ,一对一聊天)
1、聊天首先要有昵称(花名)
2、必须登录后才能聊天
3、聊天还要找到对方(我们开发肯定是找ip,而不是和聊QQ去根据名字找好友)后在发送消息
总思路:客户端发给服务端,服务端处理完,js还要解析显示在html上
1、认证服务器(登录)
1.1、客户端发送登录消息
1.2、服务端处理
使用正则判断,截取用户名
将登录用户的ip和昵称,保存起来,以后做判断用
解析完返回消息(客户端标识登录)
2、发消息
1.1、客户端发送普通消息
并将客户端发送的消息,显示出来
1.2、服务端处理
使用正则判断,截取用户名
判断发消息的客户端,是否通过服务器认证(是否登录了)
通过认证的,才可以往客户端发送消息
1.3、客户端解析服务端返回消息
将解析好的信息,显示出来
3、广播,显示昵称
1.1、服务端
拼接要返回的数据,转成json
遍历,拿当前和服务器连接的客户端,发消息给客户端
1.2、客户端
解析服务端返回的数据,遍历将他显示在昵称列表上
4、单播,一对一聊天
1.1、客户端
通过选择用户昵称
将消息发给某个用户
1.2、服务端,通过服务端转发
解析发过来的数据,取接收方的ip和要发的消息
判断接收方是否登录了,登录才可发送
使用接收方的$connection对象发送数据
1.3、客户端
解析服务端发过来的数据
将其在接收方显示出来
5、关闭客户端
在数组中删除已经登录的用户
6、关闭服务端
将登录标识置为false
以下是写这个例子我参考的优质博客,大家可以参考
http://blog.csdn.net/github_26672553/article/details/54932788
http://blog.csdn.net/github_26672553/article/details/54946302
http://blog.csdn.net/github_26672553/article/details/55098197
客户端的代码:
Title
所有用户:
你的昵称:
回复内容:
服务端的代码:
count = 4;
//2、接收客户端发来的数据
$ws_worker->onMessage = function($connection,$data){
global $clients;
//2.1、用户点击的是(链接服务器)验证客户端
if(preg_match('/^login:(\w{3,20})/i',$data,$result)){
$ip = $connection->getRemoteIp(); //获取当前客户端IP
$port = $connection->getRemotePort(); //获取当前客户端端口
if(!array_key_exists($ip,$clients)){ //判断该ip是否登录过
//$clients[$ip] = $result[1]; //新登录的ip保存起来
$clients[$ip.':'.$port] = ['ipp'=>$ip.':'.$port,'name'=>$result[1],'conn'=>$connection]; //广播的话,不止保存昵称,还要保存ip和当前和服务器连接的那个客户端
//将处理完的信息返回给客户端(给客户端发送任意消息)
$connection->send('notice:success');
$connection->send('msg:你好'.$result[1]);
echo $ip . ':'.$port . '--------' .$result[1] . 'login' . PHP_EOL; //打印看结果
//一旦有用户登录,就把保存的客户端信息发过去(显示出所有用户)
//$connection->send('users:'.json_encode($clients));
//广播(群聊)
$users = 'users:'.json_encode(array_column($clients,'name','ipp')); //返回数组中指定的列
foreach($clients as $ip=>$client){
//拿当前和服务器连接的那个客户端,发送消息
$client['conn']->send($users);
}
}
}else if(preg_match('/^msg:(.*?)/isU',$data,$megset)){
//2.2、处理发来的普通消息
if(array_key_exists($connection->getRemoteIp(),$clients)){ //判断该ip是否存在,存在就是已经登录的
echo '用户:' . $connection->getRemoteIp() . '发的消息是' . $megset[1] . PHP_EOL;
if($megset[1] == 'nihao'){
$connection->send('msg:nihao'.$clients[$connection->getRemoteIp()]);
}
//我认为广播应该在这些,将用户A说的话,显示到页面上,让所有用户都能看见
}
}else if(preg_match('/^dian:\<(.*?)\>:(.*?)/isU',$data,$meg)){
//单播,点对点发消息
$ipp = $meg[1]; //接收消息用户的ip
$msg = $meg[2]; //发送的数据
$name = $clients[$ipp]['name'];
echo "";
var_dump($name);
if(array_key_exists($ipp,$clients)){ //接收的ip也登录了,也就是有这个用户
$clients[$ipp]['conn']->send('dian:'.$msg);
echo $ipp.'==>'.$msg.PHP_EOL;
}
}
};
//客户端关闭
$ws_worker->onClose = function($connection){
global $clients;
//echo $clients[$connection->getRemoteIp()].'客户端已断开'.PHP_EOL;
unset($clients[$connection->getRemoteIp()]);
};
//3、运行
Worker::runAll();
1、黑/白名单访问
onConnect = function ($connection){
// IP 白名单验证
if($connection->getRemoteIP() != '127.0.0.1'){
$connection->close("IP Address Forbidden");
}
};
// 接受发送消息
$worker->onMessage = function ($conn,$data){
$conn->send("Hello World");
};
// 关闭连接
$worker->onClose = function ($connection){
echo "connection close \n";
};
$worker::runAll();
开启Workerman服务
正确的访问:
非本地地址访问:
2、AsyncTcpConnection类的学习思路:但是代码跑不起来
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
require_once __DIR__ . '/Workerman/Autoloader.php';
#百度代理的例子 做代理,为什么要改win上的host文件,因为dns先找本地
#浏览器访问百度的时候,首先会向当前服务器(workerman)发起请求,用当前服务器做代理,把百度的数据下载下来,返回给浏览>器
#思路是这么个思路,但是跑不起来
// 创建一个Worker监听2347端口,不使用任何应用层协议
$tcp_worker = new Worker("tcp://0.0.0.0:443");
# 客户端发起链接之后
$tcp_worker->onConnect = function($connection){
#发起一个百度的链接,因为百度使用的是https,所以端口使用https的端口
$connection_baidu = new AsyncTcpConnection('tcp://www.baidu.com:443'); #发起一个异步的tcp链接
#当百度返回数据之后,use是php的闭包(读取其他函数内部变量,的函数)
$connection_baidu->onMessage = function($connection_baidu,$data) use ($connection){
#把客户端的链接给use进来,方便函数体使用,如果不use是无法使用函数外部的变量的
$connection->send($data); #把数据发给客户端
};
#当浏览器发来数据之后,也要把数据转发给百度
$connection->onMessage = function($connection,$data1) use ($connection_baidu){
#把数据发给百度
$connection->send($data1);
};
#因为$connection_baidu是异步链接,在链接设置回调完之后还要继续链接
$connection_baidu->connect();
};
// 运行worker
Worker::runAll();
?>
做代理,为什么要改win上的host文件,因为dns先找本地
#浏览器访问百度的时候,首先会向当前服务器(workerman)发起请求,用当前服务器做代理,把百度的数据下载下来,返回给浏览>器
#思路是这么个思路,但是跑不起来
// 创建一个Worker监听2347端口,不使用任何应用层协议
$tcp_worker = new Worker("tcp://0.0.0.0:443");
# 客户端发起链接之后
$tcp_worker->onConnect = function($connection){
#发起一个百度的链接,因为百度使用的是https,所以端口使用https的端口
$connection_baidu = new AsyncTcpConnection('tcp://www.baidu.com:443'); #发起一个异步的tcp链接
#当百度返回数据之后,use是php的闭包(读取其他函数内部变量,的函数)
$connection_baidu->onMessage = function($connection_baidu,$data) use ($connection){
#把客户端的链接给use进来,方便函数体使用,如果不use是无法使用函数外部的变量的
$connection->send($data); #把数据发给客户端
};
#当浏览器发来数据之后,也要把数据转发给百度
$connection->onMessage = function($connection,$data1) use ($connection_baidu){
#把数据发给百度
$connection->send($data1);
};
#因为$connection_baidu是异步链接,在链接设置回调完之后还要继续链接
$connection_baidu->connect();
};
// 运行worker
Worker::runAll();
?>
Worker是容器,监听特定端口
当客户端连接到这个端口,会在容器内部产生一个connection对象
Worker容器,可能有很多个connection对象
通过操作connection对象向客户端发送和接收数据等操作
有2个connection类:
1、TcpConnection类(连接类的基类);客户端连接上之后自动产生的connection对象;
2、AsyncTcpConnection(是TcpConnection的子类);当我们在workerman之后需要访问一个web服务,可以通过这个类异步的发起一个http链接,去链接远程的服务端,异步的通讯;该类是客户端连接其他服务端所用到的类