前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >php实现简易Socket封装与一些鉴权思路

php实现简易Socket封装与一些鉴权思路

作者头像
躺平程序员老修
发布2023-09-05 16:07:46
2420
发布2023-09-05 16:07:46
举报
文章被收录于专栏:躺平程序员老修

socket

前言

最近公司做了一个项目,需要使用一个儿童智能手表,这个手表与我们的服务器进行socket通信,于是搭建socket的任务就落在了我身上,这个手表每次发送的参数是一个字符串,而且貌似只能发字符串,形如 KM*8800000015*0001*0032*INIT,1346545,0,k6_BASE_V1.5454,3,2

示例代码

本代码只进行了socket的简单封装,以及鉴权的举例说明,正常情况下需要配合第三方来进行鉴权操作,比如约定一个token来进行验证,本文假装KM既为token,同时一些逻辑操作也需要根据自己的业务和第三方api来进行更改,本文仅为学习示例。

  • SocketController.php
代码语言:javascript
复制
<?php

/**
 * Author huaixiu.zhen
 * http://litblc.com
 * User: litblc
 * Date: 2018/5/23
 * Time: 15:45
 */
class SocketController
{
    private $sockets;
    private $master;
    private $handshake;

    /**
     * 创建连接
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $address
     * @param $port
     * @return resource
     */
    private function createSocket($address, $port)
    {
        //创建socket
        $master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
        or die("socket_create() failed: reason1: " . socket_strerror(socket_last_error()) . "\n");
        socket_bind($master, $address, $port)
        or die("socket_bind() failed: reason2: " . socket_strerror(socket_last_error($master)) . "\n");
        socket_listen($master, 4)
        or die("socket_listen() failed: reason3: " . socket_strerror(socket_last_error($master)) . "\n");
        return $master;
    }

    /**
     * 鉴权处理(如果改服务商,需要修改成对应处理)
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $buffer
     * @return array|bool
     */
    private function authenticate($buffer)
    {
        $data = explode(',', $buffer);
//        $data = json_decode($buffer);
        if(is_array($data) && substr($data[0], 0, 2) == 'KM' && count($data) > 1){
            // todo 目前只知道这么多情况,如果需要入库的话,将再次进行数据库查询是否存在该设备
            $this->handshake = true;
            return $data;
        }

        return false;
    }

    /**
     * 关闭连接
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $socket
     */
    private function close($socket)
    {
        $index = array_search($socket, $this->sockets);
        socket_close($socket);
        if ($index >= 0) {
            array_splice($this->sockets, $index, 1);
        }
    }

    /**
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $response
     */
    private function sendMessageToClient($response)
    {
        foreach ($this->sockets as $socket) {
            socket_write($socket, $response, strlen($response));
        }
    }

    /**
     * 后端逻辑(根据服务商修改成对应处理)
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $dataArr
     * @return array|string
     */
    private function handle($dataArr)
    {
        $case = substr($dataArr[0], -2);
        $result = [$dataArr[0], 0];

        switch ($case) {
            case 'IT':
                $result = $this->init($dataArr);
                break;
            case 'LK':
                $result = $this->lk($dataArr);
        }

        return $result;
    }

    /**
     * 初始化示例操作(根据服务商修改成对应处理)
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $dataArr
     * @return string
     */
    private function init($dataArr)
    {
        return json_encode([
            $dataArr[0],
            1
        ]);
    }

    /**
     * lk示例操作(根据服务商修改成对应处理)
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $dataArr
     * @return string
     */
    private function lk($dataArr)
    {
        return json_encode([
            $dataArr[0],
            'LK_TEST'
        ]);
    }

    /**
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $address
     * @param $port
     */
    public function run($address, $port)
    {
        //配置错误级别、运行时间、刷新缓冲区
        echo "welcome to timelink socket.  \n";
        error_reporting(0);
        set_time_limit(0);
        ob_implicit_flush();

        //创建socket
        $this->master = $this->createSocket($address, $port);
        $this->sockets[] = $this->master;

        //运行socket
        while (true) {
            $sockets = $this->sockets;
            $write = NULL;
            $except = NULL;
            socket_select($sockets, $write, $except, NULL); //$write,$except传引用
            foreach ($sockets as $socket) {

                if ($socket == $this->master) {
                    $client = socket_accept($socket);
                    $this->handshake = false;
                    if ($client) {
                        $this->sockets[] = $client; //加入连接池
                    }
                } else {
                    $bytes = @socket_recv($socket, $buffer, 1024, 0);

                    // 鉴权处理 必须传字符串 形如:
                    // KM*8800000015*0001*0032*INIT,1346545,0,k6_BASE_V1.5454,3,2
                    $data = $this->authenticate($buffer);
                    if (!$data || $bytes <= 6) {
                        $this->close($socket);
                        continue;
                    }

                    // 入库操作 具体逻辑实现
                    $response = $this->handle($data);
                    $this->sendMessageToClient($response, $socket);
                }
            }
        }
    }
}
  • Socket.php
代码语言:javascript
复制
<?php
/**
 * Author huaixiu.zhen
 * http://litblc.com
 * User: litblc
 * Date: 2018/5/23
 * Time: 15:50
 */

require_once 'SocketController.php';

$sc = new SocketController();
$sc->run('192.168.9.116', 886404);

实测

  • 运行Socket服务端:

控制台执行 php Socket.php

dsdf7.png
dsdf7.png
  • 使用Socket Tool进行测试:

我本地ip为 192.168.9.116,服务端设置的端口为886404,代码里可以看到。

20180523172811.png
20180523172811.png

尝试发送垃圾数据,可以看到,它会自动断开当前连接:

912.png
912.png

ok,发送约定好的数据:

123173924.png
123173924.png

ok,大功告成。剩下的就是与第三方厂商协商具体传输数据的问题了。

(局域网测试的话记得关闭防火墙或者修改端口权限;传输机密内容的话应该加密)

后记

后来临时改变厂商与传输方式,要求我做一个转发socket,但是是一个中间人的功能,就是终端传给我一些数据,经过我手里我自己处理一下(copy一份存库之类的操作),然后再原封不动的给第三方服务器,之后把第三方返回的数据原封不动的返回给终端。

于是我决定尝试一下既当服务端,又充当客户端。

在SocketController.php中新添加一个socket Client方法

代码语言:javascript
复制
    /**
     * 客户端 向第三方服务器发送socket
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $host
     * @param $port
     * @param $message
     * @return int|string
     */
    private function sendNewSocket($host, $port, $message)
    {
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        @socket_connect($socket, $host, $port);

        $num = 0;
        $length = strlen($message);
        do
        {
            $buffer = substr($message, $num);
            $ret = @socket_write($socket, $buffer);
            $num += $ret;
        } while ($num < $length);

        $ret = '';
        do
        {
            $buffer = @socket_read($socket, 1024, PHP_BINARY_READ);
            $ret .= $buffer;
        } while (strlen($buffer) == 1024);

        socket_close($socket);


        return $ret;
    }

然后在处理方法init中调用此方法,并返回他的返回值:

代码语言:javascript
复制
    /**
     * 初始化示例操作(根据服务商修改成对应处理)
     * Author huaixiu.zhen
     * http://litblc.com
     * @param $dataArr
     * @return string
     */
    private function init($dataArr, $buffer)
    {
        $newScout = $this->sendNewSocket($newHost = '192.168.9.116', $newPort = '2046', $message = $buffer);

        return json_encode([
            $dataArr[0],
            $newScout
        ]);
    }

ok, 开俩个服务端进行测试,大功告成,既是客户端又充当服务端也是可行的。

d5090750.png
d5090750.png
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 示例代码
  • 实测
  • 后记
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档