这个列子主要讨论TcpWebSockethttp之间的通讯。长连接和长连接通讯,长连接和短连接通讯。其他协议同理可得

Tcp: 代表硬件设备
WebSocket: 代表客户端
http: 代表网页
复制代码

本列子是基于one框架 github.com/lizhichao/o… 开发.

配置协议 监听端口

由于swoole的模型 WebSocket server 包含 http server , http server 包含 tcp server 。

所以我们配置主服务为 WebSocket server ,添加两个http 和 tcp 监听。配置文件如下:

return ['server' => ['server_type' => \One\Swoole\OneServer::SWOOLE_WEBSOCKET_SERVER,'port' => 8082,'action' => \App\Test\MixPro\Ws::class,'mode' => SWOOLE_PROCESS,'sock_type' => SWOOLE_SOCK_TCP,'ip' => '0.0.0.0','set' => ['worker_num' => 5]],'add_listener' => [// http 监听['port' => 8081,'action' => \App\Server\AppHttpPort::class, 'type' => SWOOLE_SOCK_TCP,'ip' => '0.0.0.0','set' => ['open_http_protocol' => true,'open_websocket_protocol' => false]],// tcp 监听['port' => 8083,'pack_protocol' => \One\Protocol\Text::class, // tcp 打包,解包协议,方便在终端调试 我们使用 text 协议. 换行符 表示一个包的结束'action' => \App\Test\MixPro\TcpPort::class,'type' => SWOOLE_SOCK_TCP,'ip' => '0.0.0.0','set' => ['open_http_protocol' => false,'open_websocket_protocol' => false]]]
];复制代码

接下来去 \App\Test\MixPro\Ws\App\Test\MixPro\TcpPort 实现各种事件处理。 \App\Server\AppHttpPort 是框架内置的,通过路由处理http请求的,配置路由即可。

配置路由


// 首页
Router::get('/mix', ['use'    => HttpController::class . '@index','middle' => [\App\Test\MixPro\TestMiddle::class . '@isLogin'] // 中间件 如果用户登录了 直接跳转到相应的页面
]);Router::group(['middle' => [\App\Test\MixPro\TestMiddle::class . '@checkSession'] // 中间件 让用户登录后 才能进入聊天页面 http websocket 都能获取到这个 session], function () {// websocket 页面Router::get('/mix/ws', HttpController::class . '@ws');// http 页面Router::get('/mix/http', HttpController::class . '@http');// http 轮训消息接口Router::post('/mix/http/loop', HttpController::class . '@httpLoop');// http 发送消息接口Router::post('/mix/http/send', HttpController::class . '@httpSend');});复制代码

配置的都是 http 协议路由。 websocket和tpc我们直接在回调action处理。如果你的项目复杂也可以配置相应的路由。one框架的路由支持任何协议,使用方法也是统一的。

处理tcp协议

其中__construct,onConnect,onClose 不是必须的。
如果你想在服务器运行开始时最一些事情就写到 __construct里面。
onConnect 当有客户端连接时触发,每个客户端触发一次
onClose 当有客户端连接断开时触发,每个客户端触发一次

class TcpPort extends Tcp
{use Funs;private $users = [];/*** @var Ws*/protected $server;/*** @var Client*/protected $global_data;public function __construct($server, $conf){parent::__construct($server, $conf);$this->global_data = $this->server->global_data;}// 终端连接上服务器时public function onConnect(\swoole_server $server, $fd, $reactor_id){$name             = uuid();$this->users[$fd] = $name;$this->sendTo('all', json_encode(['v' => 1, 'n' => $name]));$this->sendToTcp($fd, json_encode(['v' => 4, 'n' => $this->getAllName()]));$this->global_data->bindId($fd, $name);$this->send($fd, "你的名字是:" . $name);}// 消息处理 像某个name 发送消息public function onReceive(\swoole_server $server, $fd, $reactor_id, $data){$arr = explode(' ', $data);if (count($arr) !== 3 || $arr[0] !== 'send') {$this->send($fd, "格式不正确");return false;}$n = $arr[1];$d = $arr[2];$this->sendTo($n, json_encode(['v' => 3, 'n' => $d]));}// 下线 通知所有其他终端,解除与fd的关系绑定。public function onClose(\swoole_server $server, $fd, $reactor_id){echo "tcp close {$fd} \n";$this->global_data->unBindFd($fd);$this->sendTo('all', json_encode(['v' => 2, 'n' => $this->users[$fd]]));unset($this->users[$fd]);}}复制代码

定义了一个公共的traitFuns主要实现两个方法,获取所有的终端(tcp,ws,http),和向某个用户发送消息 。在ws、http都会用到这个
在构造函数我们初始化了一个 global_data 用来保存,名称和fd的关系。你也可以使用方式储存。因为fd没次连接都不同。global_data是one框架内置的。
终端连接上服务器时触发事件 onConnect ,我们给这个终端取个名字,并把关系保存在 global_data。 通知所有终端有个新终端加入,并告诉刚加入的终端当前有哪些终端在线。

处理 websocket 协议

其中__construct,onHandShake,onOpenonClose 不是必须的。

onHandShake,onOpen 是配合使用的,如果onOpen返回false服务器会拒绝连接。 在 onOpenonMessageonClose可以拿到当前用户的session信息和http是相通的。

class Ws extends WsServer
{use Funs;private $users = [];/*** @var Client*/public $global_data = null;public function __construct(\swoole_server $server, array $conf){parent::__construct($server, $conf);$this->global_data = new Client();}// 初始化sessionpublic function onHandShake(\swoole_http_request $request, \swoole_http_response $response){return parent::onHandShake($request, $response);}// ws 发送消息public function onMessage(\swoole_websocket_server $server, \swoole_websocket_frame $frame){$data = $frame->data;$arr  = json_decode($data, true);$n    = $arr['n'];$d    = $arr['d'];$this->sendTo($n, json_encode(['v' => 3, 'n' => $d]));}// 判断用户是否登录 如果没有登录拒绝连接public function onOpen(\swoole_websocket_server $server, \swoole_http_request $request){$name = $this->session[$request->fd]->get('name');if ($name) {$this->users[$request->fd] = $name;$this->sendTo('all', json_encode(['v' => 1, 'n' => $name]));$this->global_data->bindId($request->fd, $name);return true;} else {return false;}}// ws 断开清除信息public function onClose(\swoole_server $server, $fd, $reactor_id){echo "ws close {$fd} \n";$this->global_data->unBindFd($fd);$this->sendTo('all', json_encode(['v' => 2, 'n' => $this->users[$fd]]));unset($this->users[$fd]);}
}复制代码

处理 http 协议

主要是 httpLoop 方法,轮训获取消息。因为http是短连接,发给http的信息我们是先存放在$global_data,然后直接这里读取。防止连接间隙丢信息。


class HttpController extends Controller
{use Funs;/*** @var Ws*/protected $server;/*** @var Client*/protected $global_data;public function __construct($request, $response, $server = null){parent::__construct($request, $response, $server);$this->global_data = $this->server->global_data;}/*** 首页*/public function index(){$code = sha1(uuid());$this->session()->set('code', $code);return $this->display('index', ['code' => $code]);}/*** ws页面*/public function ws(){$name = $this->session()->get('name');if (!$name) {$name = uuid();$this->session()->set('name', $name);}return $this->display('ws',['users' => $this->getAllName(),'name' => $name]);}/*** http 页面*/public function http(){$name = $this->session()->get('name');if (!$name) {$name = uuid();$this->session()->set('name', $name);}$this->global_data->set("http.{$name}", 1, time() + 60);$this->sendTo('all', json_encode(['v' => 1, 'n' => $name]));return $this->display('http', ['list' => $this->getAllName(), 'name' => $name]);}/*** http轮训*/public function httpLoop(){$name = $this->session()->get('name');$this->global_data->set("http.{$name}", 1, time() + 60);$i = 0;do {$data = $this->global_data->getAndDel("data.{$name}");$i++;\co::sleep(0.1);} while ($data === null && $i < 300);if ($data) {foreach ($data as &$v) {$v = json_decode($v, true);}} else {$data = [];}return $this->json($data);}/*** http发送消息*/public function httpSend(){$n = $this->request->post('n');$d = $this->request->post('d');if ($n && $d) {$this->sendTo($n, json_encode(['v' => 3, 'n' => $d]));return '1';}return '0';}public function __destruct(){}public function __call($name, $arguments){return $this->server->$name(...$arguments);}}复制代码

到此基本就完成了。你可以去看完整的代码 : 点这里

其他的一些列子 : github.com/lizhichao/o…

转载于:https://juejin.im/post/5cbad17e6fb9a06888415ea8

一个简单混合协议通讯列子,物联网和互联网通讯。相关推荐

  1. java通讯框架_gim: 一个简单易用,稳定高效的及时通讯框架(java、android)

    gim 一个简单易用,稳定高效的及时通讯框架(java) 简介: gim是基于高性能网络框架getty封装的,能简单上手,稳定高效的及时通讯框架 Getty:[https://github.com/g ...

  2. 【微信小程序控制硬件③】 从软件到硬件搭建一个微信小程序控制esp8266的项目,自定义通讯协议,为面试职位和比赛项目加分!

    微信物联网生态主要分在微信硬件开发平台与腾讯物联开发平台,前者已经停止维护,但依然有着很大的学习价值,而后者作为主推的平台,集成很多功能,包括从微信小程序实现配网到控制: 为了兼顾更多的朋友和自己的学 ...

  3. 串口RS232 RS485最本质的区别!-!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议!

    5分钟看懂!串口RS232 RS485最本质的区别!-4分钟看懂!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议,5分钟看懂! 一.5分钟看懂!串口RS232 RS485最本质的区别! 二 ...

  4. 一个简单的HTTP通讯的例子,使用了CInternetSession,CHttpConnection,CHttpFile三个类

    一个简单的HTTP通讯的例子,使用了CInternetSession,CHttpConnection,CHttpFile三个类. http://blog.csdn.net/STK_tianwen/ar ...

  5. 通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

    首先感谢张队@geffzhang公众号转发了上一篇文章,希望广大.neter多多推广dapr,让云原生更快更好的在.net这片土地上落地生根. 书接上回通过Dapr实现一个简单的基于.net的微服务电 ...

  6. OAuth2.0授权协议的一个简单解释

    本文来简单说下OAuth 2.0授权协议 文章目录 概述 快递员问题 授权机制的设计 互联网场景 令牌与密码 本文小结 概述 OAuth 2.0 是目前最流行的授权机制,用来授权第三方应用,获取用户数 ...

  7. http服务器响应格式,熟悉Http协议的请求和响应格式,编写一个简单的Http服务器。 基本要求:1 正确解...

    熟悉Http协议的请求和响应格式,编写一个简单的Http服务器. 基本要求: 1 正确解 2016-08-23 0 0 0 暂无评分 其他 1 积分下载 如何获取积分? 熟悉Http协议的请求和响应格 ...

  8. 从入门到入土:基于C语言采用UDP协议实现远程控制|详细说明|利用流套接字实现一个简单的远程控制系统|代码展示

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

  9. 从入门到入土:基于C语言采用TCP协议实现远程控制|详细说明|利用流套接字实现一个简单的远程控制系统

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

最新文章

  1. form表单的两种提交方式,submit和button的用法
  2. CentOS安装ClickHouse
  3. 关于 SAP Spartacus SSR 模式和 CSR 模式下重复发送 HTTP 请求调用 OCC API 的问题
  4. 【枚举】LAGNO(jzoj 1149)
  5. 你是菜鸡是有原因的 谈谈提问的艺术
  6. Java—System类和Runtime类
  7. 可临摹的PSD分层模板,拆解上线,高逼格电商设计竟如此简单?
  8. Anciroid的IPC机制-Binder概述
  9. mdstyle暂存备用
  10. (转)趋势因子:利用投资期内所有信息的获利方法
  11. java nio 捕获异常_java nio channel抛出ClosedByInterruptException的情况
  12. 密码编码学初探——数论和有限域
  13. html文字溢出用省列号,关于文字内容溢出用点点点(...)省略号表示
  14. Hexo + yilia 主题实现文章目录
  15. IIS 端口netstat -ano命令
  16. Day16:数据仓库设计
  17. 如何利用fooview实现钉钉自动打卡
  18. android pak文件_游戏中的Pak文件解析
  19. 2019 全球科技行业薪资报告出炉:全栈开发受热捧,40 岁以上程序员收入最高!...
  20. 非监督学习: K 均值聚类(原理、步骤、优缺点、调优)

热门文章

  1. 调用$.ajax不成功,jquery中ajax请求后台数据成功后既不执行success也不执行error的完美解决方法...
  2. java语言就业方向_2019年汉语言专业最全就业方向
  3. python3 copy_Python3 深浅拷贝
  4. python发布_python如何发布模块
  5. 学生电脑哪个牌子好_双开门冰箱哪个牌子好 双开门冰箱什么牌子好
  6. java icache_java手写多级缓存
  7. Oracle查询对应表是否在使用,oracle 中查询当前用户可以看到的表名、表对应的所有字段...
  8. 计算机应用基础-0025,【技工学校《计算机应用基础》课程教学特点及讲授策略】 计算机应用基础...
  9. anconda设置镜像源_三、DockerFile 定制属于自己的专属镜像
  10. html5头像裁剪,移动端 上传头像 并裁剪功能(h5)