上半年在做人脸识别时需要将人脸识别的结果实时发送到手机上,就在网上找推送相关的东西,最后就决定尝试使用智游推送提供的推送功能,它当时是免费的。虽然我用的那个阶段有的时候还不怎么稳定~~。不知现在怎么样了。
网上有很多免费的推送软件,都是第三方服务器提供的。应用较为广泛也有很多,如个推,极光推送,百度云推送,华为推送等等。我不是很了解,但是只要他们提供http协议接口就应该是同样的实现方法。
我最终的目的就是:在电脑上进行人脸识别的时候会返回一个识别结果,是一串字符如“number 3- wxk”,我想将这串字符实时发送到手机上,在手机上能看见这串字符就可以了。模型如下图所示。听似很简单,但我java是完全不懂的,http协议也只是停留在听过概念的程度上,而人脸识别当然是用OPencv、C++和MFC做的,怎么推送呢,开始时真的完全是懵的。像我这种只想应用不用知道怎么开发的怎么入手呢?
我询问了当时智游推送群里的技术,他们却不擅长C++,但是也给我很有用的提示,说他们的推送服务器是提供标准的http协议接口的,任何语言都可以去访问这个接口,但是只支持post方式的请求。
于是去查了查,post方式的请求其实也是请求网页的一种方式,我们平时浏览网页的方式是另一种http请求方式---get请求方式。两者的主要区别是:get是从服务器获取数据,Post是向服务器发送数据;get传输的数据量小,post传输的数据量大;还有其他一些请求格式不一样的地方等。有很多资料在说两者的区别,以及实现方法,针对智游推送又怎么实现呢?
一.需要事先准备的工作有:
1.去智游官网上注册账号,每个账号会得到一个独特的appkey,然后去“在线文档”找“三分钟快速集成”文档,按照文档在线操作,它会把我的账号和appkey集成进去,生成可用的Demo资源即可用的SDK,提示我下载下来,我下载到自己的电脑上。
2.接下来开始给自己的电脑安装Java和Android开发环境。安装JavaJDK,配置环境变量,安装Eclipse,安装Android SDK。配置好。这个环境的搭建网上有很多详细的资料可以参考,需要的是耐心和细心。
3.根据提供的“三分钟快速集成”文档,生成可用的APK包,此包传到手机上,安装成功即可。在手机上就出现推送的软件界面。可以将配置好的安装包安装到多部手机上同时推送。电脑进行推送之前,要进行保证电脑和手机都连接网络。
4.做好这些之后准备工作就做完了,以上步骤都是跟着说明一步一步做好就行了的。就剩编程序从电脑去请求智游推送服务器的http接口了。手机将软件打开等着接收信息就可以了。
二.编程准备过程
1.了解post请求方式。请求的格式分为:请求行;请求头部;空行;请求数据。这几个部分中
(1)请求行组成:
a.请求方法字段(例如:POST);
b.URL字段(例如智游推送提供的接口网址:http://dev.zypush.com/push/api/v3/sendmsg_ver03);
c.HTTP协议版本字段(例如:HTTP/1.1)。
这三个部分之间用空格分隔。
(2)请求头部组成:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔;
如:Host:dev.zypush.com
Connection:Keep-Alive
Content-Length:strlen(parameters)
Cache-Control:max-age=0
Content-Type:application/x-www-form-urlencoded
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
还可以设置其他的信息。也可以减少设置的信息
(3)空行:请求头最后一个参数之后必须有一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
(4)请求数据:需要客户填写表单。表单是有智游推送提供的各种参数设置接口。智游推送的接口参考表单参考“在线文档”—“REST API”—“PUSH API-v3”。如:
2.了解推送的过程:
(1)建立socket连接,端口设置为80;
(2)填写上述post请求的四个部分具体信息;
(3)用send()函数向一个已经连接的socket发送数据;
(4)关闭socket连接。
三.消息推送程序示例代码如下所示:
#include "stdafx.h" #include <afxwin.h> // MFC core and standard components #include <iostream> #include <string> #include <winsock2.h> using namespace std; //函数声明 int request(char* hostname, char* api, char* parameters); //方法调用 int main(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; //initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { _tprintf(_T("Fatal Error: MFC initialization failed\n")); return 1; } char parameter[1024] = {0}; strcat(parameter, "userName=***");//填自己注册的账号 strcat(parameter, "&appKey=****");//填自己的appkey strcat(parameter, "&receiveType=1"); strcat(parameter, "&receiveUsers=3"); strcat(parameter, "&msgType=1"); strcat(parameter, "&msgTitle={\"haha\"}"); strcat(parameter, "&msgTitle=haha"); strcat(parameter, "&msgContent=haha"); strcat(parameter, "&clientPlatform=android"); request("dev.zypush.com", "http://dev.zypush.com/push/api/v3/sendmsg_ver03", parameter); return nRetCode; } //函数实现 int request(char* hostname, char* api, char* parameters) { //建立socket连接 WSADATA WsaData;//WSADATA结构被用来储存调用AfxSocketInit全局函数返回的Windows Sockets初始化信息。 WSAStartup(0x0101, &WsaData);//使用Winsock库函数之前,必须先调用函数WSAStartup,该函数负责初始化动态连接库Ws2_32.dll //初始化socket struct hostent* host_addr = gethostbyname(hostname);//gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针。 if (host_addr == NULL) { cout<<"Unable to locate host"<<endl; return -103; } sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons((unsigned short)80); sin.sin_addr.s_addr = *((int*)*host_addr->h_addr_list); int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { return -100; } //建立连接 if (connect(sock, (const struct sockaddr *)&sin, sizeof(sockaddr_in) ) == -1) //建立socket连线,对于流类套接口(SOCK_STREAM类型),利用名字来与一个远程主机建立连接,一旦套接口调用成功返回,它就能收发数据了 { cout<<"connect failed"<<endl; return -101; } //初始化发送信息 char send_str[2048] = {0}; //头信息 strcat(send_str, "POST "); strcat(send_str, api); strcat(send_str, " HTTP/1.1\r\n"); //\r换行相当于回车/ \n新行 strcat(send_str, "Host: "); strcat(send_str, hostname); strcat(send_str, "\r\n"); strcat(send_str, "Connection: Keep-Alive\r\n"); char content_header[100]; sprintf(content_header,"Content-Length: %d\r\n", strlen(parameters)); strcat(send_str, content_header); strcat(send_str, "Cache-Control: max-age=0\r\n"); strcat(send_str, "Content-Type: application/x-www-form-urlencoded\r\n"); strcat(send_str, "Accept: */*\r\n"); strcat(send_str, "Accept-Encoding: gzip,deflate,sdch\r\n"); strcat(send_str, "Accept-Language: zh-CN,zh;q=0.8\r\n"); //内容信息或者是参数信息 strcat(send_str, "\r\n"); strcat(send_str, parameters); if (send(sock, send_str, strlen(send_str),0) == -1) //向一个已连接的套接口发送数据。int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);s:一个用于标识已连接套接口的描述字。buf:包含待发送数据的缓冲区。len:缓冲区中数据的长度。flags:调用执行方式。 { //发送失败就要显示发送失败 cout<<"send failed"<<endl; return -101; } else { //发送成功就要显示发送的内容 cout<<send_str<<"\r\n\r\n"<<endl; } //获取返回信息 char recv_str[4096] = {0}; if (recv(sock, recv_str, sizeof(recv_str), 0) == -1) { cout<<"recv failed"<<endl;//从一个套接口接收数据。s:一个标识已连接套接口的描述字。buf:用于接收数据的缓冲区。len:缓冲区长度。flags:指定调用方式。 return -101; } //打印发送的信息 cout<<recv_str<<endl; //关闭socket连接 WSACleanup( ); //暂停显示 system("pause"); return 0; }
四.结果。
当把这个程序添加到我自己的人脸识别程序中后,手机端接收的信息如下图所示: