TCP/UDP协议客户端对接说明
自栓Q验证系统V5.1版本起新增了TCP/UDP协议客户端对接,支持http/https、tcp、udp同时使用,但为了能够兼容所有的接口,都需要主动向tcp、udp服务器发送数据包才能接收到来自服务器处理后的响应数据包,服务器并不会主动向客户端进行数据发送(后续的版本可能会更新),tcp和udp协议客户端数据发送格式为JSON,服务端响应数据默认也是JSON(除非自定义设置),http协议请求接口无非就是post、get请求传递参数,而tcp和udp则不同还是有一点差异的不过差异不太大,但接口地址和接口参数是相同的,下面我将用Python语言来实现tcp、udp的通讯对接
部署服务器环境
使用tcp、udp协议需要安装好PHP扩展swoole,找到PHP管理,选择你网站的PHP版本点击设置,点击安装扩展,找到swoole,安装swoole扩展,swoole4装不装都可以
(注意,swoole不支持Windows系统,因此Windows无法使用只能使用默认的http/https协议客户端请求)
如果swoole扩展没有安装,是无法运行该协议端口的,后台的端口协议设置也会报需要安装扩展(如果安装后还是报没安装那么重启一下PHP即可)
TCP、UDP的服务端口部署
栓Q验证系统的tcp、udp服务端口分别默认为9501和9502,支持自定义更改,在系统后台的系统管理的端口协议配置里面进行修改,请确保端口并没有被占用,这里我就用默认的端口了
设置好tcp、udp服务端口之后,如果你购买服务器的管理后台有服务器安全组或防火墙之类的设置,你还需要将你设置好的端口进行开放,否则外部无法进行连接,这里我就不教如何开放了可自行咨询自己购买云服务器客服问如何开放端口(就跟你一开始搭建宝塔一样也需要开放8888端口作为宝塔面板的访问端口一样的)
服务器安全组开放了端口之后,还需要在宝塔的安全设置里面进行开放添加对应的端口规则,否则一样无法连接,点击添加端口规则,协议选择tcp输入设置好的端口9501(注意你如果自定义设置了那么你就写对应的自定义端口),来源选所有ip,策略选允许,备注自定,最后提交即可
同样udp也是一样的,添加好udp的端口策略,如果你不需要udp那么可以不用去添加了
添加好之后,端口就可以被外部连接了
开启或关闭TCP、UDP服务端口
首先你需要连接到你服务器终端,你可以使用宝塔面板自带的终端,也可以使用其他ssh工具连接到终端,然后输入命令执行进入到自己的网站根目录
cd 你搭建部署好的栓Q验证站点绝对根目录
如何知道自己搭建部署好的栓Q验证站点绝对根目录?
在宝塔点击网站管理,找到你对应的网站,找到根目录就是
你如果用的是宝塔面板自带的终端,你可以直接点击这个根目录进入到你的网站目录,然后点击上面的终端,宝塔会自动帮你执行上面的cd命令,你就不需要执行了
然后再执行以下命令运行对应的协议服务端口(tcp和udp的执行方式是一样的,以下我就用tcp演示)
TCP服务端口运行命令:
php think ShuanQTcpService --daemonize=true
UDP服务端口运行命令:
php think ShuanQUdpService --daemonize=true
[点我展开]执行之后如果出现报错(PHP script '/www/wwwroot/xxxxxx/application/tags.php' is protected by SourceGuardian and requires a SourceGuardian loader 'ixed.7.2.lin' to be installed.)
如何给php-cli加载sg11扩展,首先你得知道php-cli.ini文件在什么位置,上面的报错已经告诉你在/www/server/php/72/etc/php-cli.ini这个位置(注意这是我服务器报的位置,实际按你服务器报的为准,要特别注意这个数字72是代表版本7.2,如果这个数字与你网站的PHP版本不一致,那么就是因为你装了多个PHP版本导致的,实际你得找你网站对应的PHP的版本的目录,比如你先装了7.2版本PHP,那么这个数字肯定就是72,然后你最后又装了8.0版本,那么这个数字肯定是80,这个数字取决于你最后安装的PHP版本,如果你只安装了7.2版本那肯定是72),还有一个方法就是在宝塔的软件商店管理,找到你网站的PHP版本(我的是7.2那我就找7.2),里面有个“位置”的文件夹图标,点击它
然后点击etc文件夹进入
进入后就是和上面报错的说要编辑的配置文件路径一样的了,这里面就有php-cli.ini文件
进入目录后只有php.ini文件并没有php-cli.ini文件解决办法,方法1:返回上层目录,点击bin文件夹,里面会有个文件叫php,然后在这里打开终端,执行这个命令【**./php -i | grep "Loaded Configuration File"】或【./php --ini**】就可以看见php-cli.ini文件的所在位置,方法2:卸载重装该版本的PHP(推荐用方案1)
知道php-cli.ini文件在哪里之后,先不管它,先看php.ini文件,我上面说了,你用宝塔安装sg11扩展的时候,宝塔只给php配置并加载了sg11的扩展,并没有给php-cli配置加载,那既然他给php配置了,那就把php的加载配置复制过来放到php-cli.ini里面就行了,点击编辑打开php.ini文件,找到[swoole],你可以快捷搜索,然后复制这三行,分别是两个extension开头的一个[swoole]
最后你再编辑打开php-cli.ini文件,把刚刚从php.ini里面复制的三行配置代码在最后面换个行粘贴即可,别忘了保存,然后你重启一下PHP就行了执行命令就不会报错了
--daemonize=true是一个参数,代表守护进程,看自己需求加不加这个参数,如果不加,那么你执行之后就是这样的
这样虽然也在运行,但是一旦你把终端关闭后,与ssh终端的连接就关闭了 服务端口也就断掉了没在运行,你的用户也就无法连接了,加上--daemonize=true这个参数的话执行后,你关不关终端都没问题它依然在运行(除非服务器关闭了),各有各的好处,不加这个参数你想关闭的时候按快捷键Ctrl+C就可以关闭服务端口但你得一直挂着这个终端不能关闭
如果加了这个参数执行命令执行后是这样的,你感觉它结束了但实际它已经在后台守护进程运行了
在系统后台的端口协议配置里面可以看见tcp服务已经在运行了(没显示最新数据可以点击旁边的刷新状态信息按钮)
以守护进程执行命令运行的话想要关闭就不像上面用快捷键Ctrl+C就可以的,你需要在系统后台的端口协议配置管理找到tcp服务状态点击旁边的按钮结束端口服务进行关闭
在后台关闭服务端口不一定能成功,通常情况下是可以成功结束的,如何证明服务端口到底有没有结束?第一个方法就是你可以在你对接的客户端进行连接,如果连接不超过说明没在运行,第二个方法就是在TCP服务状态旁边点击刷新状态信息,然后可以看见刷新失败,连接端口失败,就代表端口已经结束运行了,或者你刷新之后看tcp的最后一次处理请求时间,如果不是当前实时实际那么就代表结束运行了(因为你点击刷新也算给tcp发送命令处理请求,注意:如果是udp的话刷新只能看最后处理请求判断是否在运行,udp是不需要连接的因此点击刷新后不会像tcp刷新一样会告诉你连接成功没有)
特别注意:你的服务器如果重启了,你需要重新执行一次命令运行对应的协议端口,否则你的客户端无法进行连接,还有一种情况就是栓Q验证系统发布了新版本并且你在后台执行了更新并且该版本的更新内容涉及到对接接口相关的更新内容那也需要手动关闭一次端口然后重新执行命令运行
HTTP与TCP、UDP通信差异
栓Q验证系统的http与tcp、udp协议请求方法和传递的参数数据是不同的,http协议请求地址就是接口的地址,传递的参数格式就是get、post请求的key=value格式;而tcp、udp则没有请求地址这一说,是发送json格式的数据包,JSON结构里面有个成员字段是接口地址,有个成员字段是接口参数列表对象来实现接口请求,请求之后服务器会向客户端发送数据包,这个数据包就是和http请求的响应返回值是一样的;当然了,你如果开启了参数加密、自定义接口地址路由、返回值加密、自定义接口的参数名,那么在tcp、udp协议也是生效的,具体可详细查看下面的说明
tcp、udp客户端向服务端发送数据包JSON格式:
字段名 | 类型 | 必填 | 说明 |
---|---|---|---|
mode | string | 是 | 请求模式,mode=client代表客户端请求接口 |
api | string | 是 | 请求接口地址,可在系统后台的应用管理的接口管理查看对应的接口地址(如果你设置过自定义接口路由地址,那么写自定义的地址后缀即可) |
params | object | 是 | 请求接口传递的参数,如:{"appid":"1","card":"shuanq"},如果你开启了参数kv加密功能,那么这里传递的数据同样也需要进行加密 |
请求 获取应用信息 接口数据格式示例
{"mode":"client","api":"app/get_app_info","params":{"appid":1}}
请求 单码登录检查 接口数据格式示例
{"mode":"client","api":"card_app/check","params":{"appid":1,"card":"a3b61b1c046e1fc145dd2477ae072aa7","machine_code":"shuanq"}}
tcp、udp客户端向服务端发送数据请求接口 后 服务端向客户端发送数据包JSON格式:与http请求接口的返回值一致(如果你开启了返回值验签、加密功能,同样的收到服务器的数据包后需要先判断再解密和验签)
客户端对接开发测试
tcp、udp协议服务端口开启运行之后,就可以进行连接发送数据包了,这里将以Python语言为例进行客户端开发对接,并且我这边是新建的应用,接口地址都是默认的,也没有设置过自定义参数名,也没有开启什么签名验证、参数加密、返回值加密的功能(这些功能开启后实际和http请求一样的,该加密加密改验签验签)
TCP示例
Python代码:
import socket # 导入socket模块
host = '192.168.32.130' # 服务器IP
port = 9501 # 协议端口
# tcp 测试
s = socket.socket() # 创建套接字
s.connect((host, port)) # 主动初始化TCP服务器连接
while True:
send_data = input("\n请输入要发送的数据:\n") # 提示用户输入数据
s.send(send_data.encode()) # 发送TCP数据
# 接受对方发送过来的数据,最大接受10240字节
recvData = s.recv(10240).decode()
print('接收到的数据为:', recvData)
s.close() # 关闭套接字
输入{"mode":"client","api":"app/get_app_info","params":{"appid":1}}的运行结果:
将服务器发送过来的请求接口成功的数据包解析JSON:
{
"code":1,
"message":"获取成功",
"timestamp":1675923373,
"nonceStr":"0c171699ba4a3cd879a16bfae6babf3c",
"apiInfo":"{"id":1,"name":"\u83b7\u53d6\u5e94\u7528\u4fe1\u606f","controller":"App","method":"get_app_info","api":"app\/get_app_info"}",
"data":"{"info":{"name":"单码应用","avatar":"http:\/\/192.168.32.130\/index\/system\/get_custom_image\/name\/dXBsb2Fkcy9pbWFnZXMvc3lzdGVtX2ltYWdlLzIwMjMwMjA5LzYwYzY1MjhmYTJlNWUwNzJiZGRhZTEyZTA1ZWU0Y2Q2LnBuZw==","type":0,"addtime":1675915178,"param_extend_config":{}},"user_safe_config":{},"moreOtherData":{"api_extra_data":"","request_safe_code":""}}",
"signature":"0264fe5e7d47002119f13be5baa66437"
}
UDP示例
Python代码:
import socket # 导入socket模块
host = '192.168.32.130' # 服务器IP
port = 9502 # 协议端口
# udp 测试
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建套接字
while True:
send_data = input("\n请输入要发送的数据:\n") # 提示用户输入数据
s.sendto(send_data.encode(), (host, port)) # 发送UDP数据
# 接受对方发送过来的数据,最大接受10240字节
response, addr = s.recvfrom(10240)
print('接收到的数据为:', response.decode())
输入{"mode":"client","api":"card_app/check","params":{"appid":1,"card":"a3b61b1c046e1fc145dd2477ae072aa7","machine_code":"shuanq"}}的运行结果:
将服务器发送过来的请求接口成功的数据包解析JSON:
服务端回应JSON数据结构说明
参数名 | 类型 | 说明 | 必填 |
---|---|---|---|
code | integer | 业务码(1为成功,其他为失败) | 是 |
message | string | 返回业务信息 | 是 |
timestamp | integer | 当前时间戳(10位) | 是 |
nonceStr | string | 随机字串 | 是 |
apiInfo | string | 请求的接口信息,是一个转JSON字符串的object | 是 |
data | string | 数据信息(通常为接口的业务数据,是一个转JSON字符串的object,若开启响应返回值加密则是加密后的密文) | 是 |
signature | string | 本次响应签名 | 是 |
data字段内的moreOtherData对象字段说明
参数名 | 类型 | 说明 | 必填 |
---|---|---|---|
api_extra_data | string | 接口设置的自定义额外返回数据 | 是 |
request_safe_code | string | 请求防劫持安全验证码 | 是 |
apiInfo字段说明
参数名 | 类型 | 说明 | 必填 |
---|---|---|---|
id | integer | 接口ID | 是 |
name | string | 接口名称 | 是 |
controller | string | 接口控制器名称 | 是 |
method | string | 接口方法名称 | 是 |
api | string | 接口地址 | 是 |