写作目的
公司是做楼宇对讲主机设备,设备端运行的是安卓系统,为了远程方便调试设备(如人在深圳控制一台在北京的设备),特做了个远程登录android的小系统,以此记录。
环境介绍
安卓主板一块,一台公网服务器,一台PC
最终效果展示
启动服务器,-p设置端口号为1888
PC端,使用命令./msgsps_pub -i ens33 -t 76000c885342,登录了一台mac地址为76:00:0c:88:53:42的设备
设备端信息
原理介绍
一、 下载MQTT源码
我这里下载的是1.5.1版本,当然也可以尝试下载其他版本
二、源码介绍
源码分为三大块,
1.服务端源码(无需改动,编译后好放入服务器端运行),
2.pub端源码(需要写,这是放在PC端的程序)
3.sub端源码(需要写并移植,这是放在安卓端的程序)
pub源码
/* This provides a crude manner of testing the performance of a broker in messages/s. */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <mosquitto.h>
#include <msgsps_common.h>
static bool run = true;
static int message_count = 0;
static struct timeval start, stop;
void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
//printf("msg->payload = %s\n",msg->payload);
if(msg == NULL || msg->payload == NULL){
return;
}
char oppositeAddr[32] = {0};
char localAddr[32] = {0};
char cmdtype[32] = {0};
int reply = -1;
int cmdid = -1;
int ret;
char *cjsonBuf;
int msglen = strlen(msg->payload);
FILE* cmdfp = NULL;
char *message = (char *)malloc(msglen);
if(message == NULL){
return ;
}
memset(message,0,msglen);
ret = getIntFormCjson(msg->payload ,"cmdid",&cmdid);
if(ret == 0){
//printf("cmdid = %d\n",cmdid);
}
ret = getStrFormCjson(msg->payload ,"oppositeAddr",oppositeAddr,sizeof(oppositeAddr));
if(ret > 0){
//printf("oppositeAddr = %s\n",oppositeAddr);
}
ret = getStrFormCjson(msg->payload ,"localAddr",localAddr,sizeof(localAddr));
if(ret > 0){
//printf("localAddr = %s\n",localAddr);
}
ret = getIntFormCjson(msg->payload ,"reply",&reply);
if(ret == 0){
//printf("reply = %d\n",reply);
}
ret = getStrFormCjson(msg->payload ,"cmdtype",cmdtype,sizeof(cmdtype));
if(ret > 0){
//printf("cmdtype = %s\n",cmdtype);
}
ret = getStrFormCjson(msg->payload ,"message",message,msglen);
if(ret > 0){
printf("\n[\n%s]\n",message);
}
// mosquitto_loop_stop(mosq, true);
// mosquitto_destroy(mosq);
// mosquitto_lib_cleanup();
}
void my_connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
printf("connect succeed!\n");
gettimeofday(&start, NULL);
}
void my_disconnect_callback(struct mosquitto *mosq, void *obj, int result)
{
run = false;
}
void my_publish_callback(struct mosquitto *mosq, void *obj, int mid)
{
message_count++;
if(message_count == MESSAGE_COUNT){
gettimeofday(&stop, NULL);
mosquitto_disconnect((struct mosquitto *)obj);
}
}
int main(int argc, char *argv[])
{
struct mosquitto *mosq;
int i;
double dstart, dstop, diff;
uint8_t *buf;
char mac[18] = {0};
char devid[32] = {0};
char mosid[64] = {0};
int mid = 0;
char ethname[8] ={0};
char oppositeMac[16] = {0};
if(argc < 5){
printf("[-t [opposite mac] \n");
printf("[-i [ethX] \n");
printf("[-i eth0 -t B44B16101010] \n");
return -1;
}
for(i = 0; i <argc ;i ++){
if(strcmp(argv[i],"-i") == 0){
strcpy(ethname,argv[i+1]);
}
if(strcmp(argv[i],"-t") == 0){
strcpy(oppositeMac,argv[i+1]);
}
}
if(ethname[0] == 0 || oppositeMac[0] == 0){
printf("[-t [opposite mac] \n");
printf("[-i [ethX] \n");
printf("[-i eth0 -t B44B16101010] \n");
return -1;
}
getDevMacaddr(ethname, mac, 0);
sprintf(devid,"%s_c",mac);
mosquitto_lib_init();
snprintf(mosid, sizeof(mosid), "msgps_pub_%d", getpid());
mosq = mosquitto_new(mosid, true, NULL);
mosquitto_connect_callback_set(mosq, my_connect_callback);
mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
mosquitto_publish_callback_set(mosq, my_publish_callback);
mosquitto_message_callback_set(mosq, my_message_callback);
mosquitto_connect(mosq, HOST, PORT, 600);
mosquitto_subscribe(mosq, &mid, devid, SUB_QOS);
mosquitto_loop_start(mosq);
char cjsonBuf[1024] = {0};
char inputBuf[1024] ={0};
char ch;
for(;;){
memset(cjsonBuf,0,sizeof(cjsonBuf));
memset(inputBuf,0,sizeof(inputBuf));
gets(inputBuf);
if(strlen(inputBuf) <=0){
continue;
}
if(strcmp(inputBuf,"exit") == 0){
break;
}
createCjsonCmd(cjsonBuf, sizeof(cjsonBuf), 999, 1, oppositeMac, devid, "script", inputBuf);
mosquitto_publish(mosq, NULL, oppositeMac, strlen(cjsonBuf), cjsonBuf, PUB_QOS, false);
}
mosquitto_loop_stop(mosq, true);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
sub源码
/* This provides a crude manner of testing the performance of a broker in messages/s. */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include<errno.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <mosquitto.h>
#include <log/log.h>
#include <msgsps_common.h>
#include "tools_common.h"
#include <cutils/log.h>
#define LOG_TAG "WB-A8HardWare-MQTT"
#define LOGE(...) ALOGE(__VA_ARGS__)
#define LOGD(...) ALOGD(__VA_ARGS__)
#define LOGI(...) ALOGI(__VA_ARGS__)
#define LOGW(...) ALOGW(__VA_ARGS__)
static bool run = true;
static int message_count = 0;
static struct timeval start, stop;
void my_connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
LOGD("rc: %d\n", rc);
}
void my_disconnect_callback(struct mosquitto *mosq, void *obj, int result)
{
run = false;
}
void alarm_hander(int sig){
LOGD("timeou =%d\n!",sig);
}
static int mypopen(const char*cmd,char *recvBuf,int len ,int timeout){
//管道文件描述符
int pfd[2];
int ret;
int readLen = 0;
if(pipe(pfd)!=0){
LOGD("error: %s\n",strerror(errno));
return -1;
}
//创建子进程
pid_t pid=fork();
if(pid==0){
close(pfd[0]);
//重定向 标准输出 和标准错误输出 到 写管道
close(STDOUT_FILENO);
close(STDERR_FILENO);
fcntl(pfd[1],F_DUPFD);
fcntl(pfd[1],F_DUPFD,2);
execl("/system/bin/sh","sh","-c",cmd,NULL);
close(pfd[1]);
exit(0);
}
//关闭写管道
close(pfd[1]);
//wait(0); 防止阻塞,此处不等待
//设置管道成非阻塞模式
ret = fcntl(pfd[0], F_SETFL, O_NONBLOCK);
if (ret != 0) {
goto fail0;
}
for(;;){
struct timeval selcet_tv;
fd_set fds;
FD_ZERO(&fds);
FD_SET( pfd[0], &fds);
selcet_tv.tv_sec = timeout;
selcet_tv.tv_usec = 1000;
do {
ret = select(pfd[0] + 1, &fds, 0, 0, &selcet_tv);
} while (ret == -1 && errno == EINTR);
switch (ret) {
case 0:
LOGD("read timeout!\n");
close(pfd[0]);
return readLen;
case -1:
LOGD("fail to select!\n");
break;
default:
ret = read(pfd[0], recvBuf + readLen, len - readLen);
if(ret <=0){
close(pfd[0]);
return readLen;
}
readLen += ret;
}
}
fail0:
return -1;
}
void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
if(msg == NULL || msg->payload == NULL){
return;
}
#define MAX_LEN (512*1024)
char oppositeAddr[32] = {0};
char localAddr[32] = {0};
char cmdtype[32] = {0};
int reply = -1;
int cmdid = -1;
int ret;
char *cjsonBuf;
int msglen = strlen(msg->payload);
FILE* cmdfp = NULL;
char *message = (char *)malloc(msglen);
if(message == NULL){
return ;
}
memset(message,0,msglen);
ret = getIntFormCjson(msg->payload ,"cmdid",&cmdid);
if(ret == 0){
LOGD("cmdid = %d\n",cmdid);
}
ret = getStrFormCjson(msg->payload ,"oppositeAddr",oppositeAddr,sizeof(oppositeAddr));
if(ret > 0){
LOGD("oppositeAddr = %s\n",oppositeAddr);
}
ret = getStrFormCjson(msg->payload ,"localAddr",localAddr,sizeof(localAddr));
if(ret > 0){
LOGD("localAddr = %s\n",localAddr);
}
ret = getIntFormCjson(msg->payload ,"reply",&reply);
if(ret == 0){
LOGD("reply = %d\n",reply);
}
ret = getStrFormCjson(msg->payload ,"cmdtype",cmdtype,sizeof(cmdtype));
if(ret > 0){
LOGD("cmdtype = %s\n",cmdtype);
}
ret = getStrFormCjson(msg->payload ,"message",message,msglen);
if(ret > 0){
LOGD("message = %s\n",message);
}
if(strcmp(cmdtype,"script") == 0){
if(reply){
int len = 0;
char *fileStr = malloc(MAX_LEN);
char lineStr[MAX_LEN/100] = {0};
memset(fileStr,0,MAX_LEN);
len = mypopen(message,fileStr,MAX_LEN,2);
if(len < 0){
len = 0;
}
cjsonBuf = (char *)malloc(MAX_LEN+CJSON_HEAD_LEN);
memset(cjsonBuf,0,MAX_LEN+CJSON_HEAD_LEN);
createCjsonCmd(cjsonBuf,MAX_LEN+CJSON_HEAD_LEN,cmdid, 0,localAddr,oppositeAddr,"ack",fileStr);
LOGD("sendcjson = %s \n",cjsonBuf);
mosquitto_publish(mosq, NULL, localAddr, MAX_LEN+ CJSON_HEAD_LEN ,cjsonBuf, PUB_QOS, false);
free(fileStr);
free(cjsonBuf);
}
}else if(strcmp(cmdtype,"login") == 0){
if(reply){
cjsonBuf = (char *)malloc(CJSON_HEAD_LEN);
memset(cjsonBuf,0,CJSON_HEAD_LEN);
createCjsonCmd(cjsonBuf,CJSON_HEAD_LEN,cmdid, 0,localAddr,oppositeAddr,"ack","login succeed");
//LOGD("cjsonBuf = %s\n",cjsonBuf);
LOGD("sendcjson = %s\n",cjsonBuf);
mosquitto_publish(mosq, NULL, localAddr, strlen(cjsonBuf) + 1,cjsonBuf, PUB_QOS, false);
free(cjsonBuf);
}
}
fail0:
free(message);
}
static int getHostAndPort(char *host,unsigned short *port){
FILE* popenfp = NULL;
char buf[128];
popenfp = popen("getprop | grep sys.mqtt_server_ip | cut -d ':' -f 2 |cut -d '[' -f 2 | cut -d ']' -f -1", "r"); //执行脚本并返回
if(popenfp == NULL){
return -1;
}
memset(buf,0,sizeof(buf));
if(fgets(buf, sizeof(buf), popenfp) == NULL || (strlen(buf)<= 0 )){
return -1;
}
buf[strlen(buf)-1] = 0;//去掉最后一个字符
LOGD("buf = %s\n",buf);
strcpy(host,buf);
pclose(popenfp);
popenfp = popen("getprop | grep sys.mqtt_server_port | cut -d ':' -f 2 |cut -d '[' -f 2 | cut -d ']' -f -1", "r"); //执行脚本并返回
if(popenfp == NULL){
return -1;
}
memset(buf,0,sizeof(buf));
if(fgets(buf, sizeof(buf), popenfp) == NULL || (strlen(buf)<= 0 )){
return -1;
}
buf[strlen(buf)-1] = 0;
LOGD("buf = %s\n",buf);
*port = atoi(buf);
LOGD("port = %d\n",*port);
pclose(popenfp);
return 0;
}
int main(int argc, char *argv[])
{
double dstart, dstop, diff;
int mid = 0;
char id[50];
struct mosquitto *mosq;
int ret;
char mac[18] = {0};
char serverHost[32] = {0};
unsigned short serverPort;
while ( getDevMacaddr("eth0", mac, 1) != 0){
sleep(1);
}
mosquitto_lib_init();
snprintf(id, 50, "msgps_sub_%d", getpid());
mosq = mosquitto_new(id, true, NULL);
mosquitto_connect_callback_set(mosq, my_connect_callback);
mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
mosquitto_message_callback_set(mosq, my_message_callback);
if( getHostAndPort(serverHost,&serverPort) < 0){
strcpy(serverHost,HOST);
serverPort = PORT;
}
LOGD("serverHost:%s serverPort:%d mac:%s\n",serverHost,serverPort,mac);
for(;;){
ret = mosquitto_connect(mosq, serverHost, serverPort, 600);
LOGD("mosquitto_connect :%d\n",ret);
if(ret == 0){
break;
}
sleep(1);
}
mosquitto_subscribe(mosq, &mid, mac, SUB_QOS);
mosquitto_loop_forever(mosq, -1, 1);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
三、实现原理
设备端和PC端都以MAC为订阅ID,PC端通过MQTT发送数据到指定的ID上,设备端收到数据后,调用execl执行脚本,并将执行的结果返回给PC端
(不明白处可留言,或加我微信gddw1954讨论)