java層通過JNI調用c++的代碼來向服務器發送用戶的登錄驗證信息,然後c++的再封裝相關的json數據通過libcurl庫向服務器發送登錄驗證信息,服務器來返回驗證結果,login.cpp上再解析返回來的json結果,將解析的結果返回給java層。這就是Android上同服務器交互的大致流程。
封裝Json向服務器發送
json格式
====給服務端的協議====
http://ip:port/login [json_data]
{
username: "gailun",
password: "123123",
driver: "yes"
}
封裝Json的代碼
//(1)封裝一個json字符串
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "username", username);
cJSON_AddStringToObject(root, "password", passwd);
cJSON_AddStringToObject(root, "driver", isDriver);
post_str = cJSON_Print(root);
cJSON_Delete(root);//釋放root的空間
root = NULL;
libcurl訪問服務器
//(2) 想web服務器 發送http請求 其中post數據 json字符串
//1 設置curl url
curl_easy_setopt(curl, CURLOPT_URL, "http://101.200.190.150:7777/login");
//2 開啓post請求開關
curl_easy_setopt(curl, CURLOPT_POST, true);
//3 添加post數據
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str);
//4 設定一個處理服務器響應的回調函數,deal_response是回調函數
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, deal_response);
//5 給回調函數傳遞一個形參
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData);
//6 向服務器發送請求,等待服務器的響應
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:perform ERROR, rescode= [%d]\n",
res);
return JNI_FALSE;
}
服務器返回數據,服務器響應的數據是json類型的格式如下
//成功
{
result: "ok",
}
//失敗
{
result: "error",
reason: "why...."
}
接受服務器返回數據代碼如下:
//(4) 解析服務器返回的json字符串
root = cJSON_Parse(responseData.data);
cJSON *result = cJSON_GetObjectItem(root, "result");
if(result && strcmp(result->valuestring, "ok") == 0) {
//登陸成功
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:login succ!!!");
return JNI_TRUE;
}
else {
//登陸失敗
cJSON* reason = cJSON_GetObjectItem(root, "reason");
if (reason) {
//已知錯誤
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:login error, reason = %s!!!", reason->valuestring);
}
else {
//未知的錯誤
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:login error, reason = Unknow!!!");
}
return JNI_FALSE;
}
login.cpp代碼
//
// Created by Administrator on 2019/10/13.
//
#include "login.h"
#include "cJSON.h"
#include <android/log.h>
#include "curl/curl.h"
//默認服務器返回數據的長度
#define RESPONSE_DATA_LEN 4096
//用來接收服務器一個buffer
typedef struct login_response_data
{
//構造函數
login_response_data() {
//分配空間
memset(data, 0, RESPONSE_DATA_LEN);
data_len = 0;
}
char data[RESPONSE_DATA_LEN];
int data_len;
}response_data_t;
//處理從服務器返回的數據ptr,將數據拷貝到arg中
size_t deal_response(void *ptr, size_t n, size_t m, void *arg)
{
int count = m*n;
response_data_t *response_data = (response_data_t*)arg;
memcpy(response_data->data, ptr, count);
response_data->data_len = count;
return response_data->data_len;
}
/*
* Class: com_ldw_hello_BridgeUtils
* Method: login
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_ldw_hello_BridgeUtils_login
(JNIEnv * env, jobject obj, jstring jni_username, jstring jni_password, jboolean jni_isdriver)
{
const char *username = env->GetStringUTFChars(jni_username, 0);
const char *passwd = env->GetStringUTFChars(jni_password, 0);
const char *isDriver = jni_isdriver == JNI_TRUE?"yes":"no";
char *post_str = NULL;
CURL* curl = NULL;//初始化一個curl指針
CURLcode res;//服務器返回的結果
response_data_t responseData;//專門用來存放從服務器返回的數據
curl = curl_easy_init();
if(curl == NULL){
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login: curl init error \n");
return JNI_FALSE;
}
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login: username = %s, passwd = %s, isDriver = %s",
username, passwd, isDriver);
//封裝一個數據協議
/*
====給服務端的協議====
http://ip:port/login [json_data]
{
username: "gailun",
password: "123123",
driver: "yes"
}
*
*
* */
//(1)封裝一個json字符串
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "username", username);
cJSON_AddStringToObject(root, "password", passwd);
cJSON_AddStringToObject(root, "driver", isDriver);
post_str = cJSON_Print(root);
cJSON_Delete(root);//釋放root的空間
root = NULL;
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login: post_str = [%s]\n", post_str);
//(2) 想web服務器 發送http請求 其中post數據 json字符串
//1 設置curl url
curl_easy_setopt(curl, CURLOPT_URL, "http://101.200.190.150:7777/login");
//2 開啓post請求開關
curl_easy_setopt(curl, CURLOPT_POST, true);
//3 添加post數據
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str);
//4 設定一個處理服務器響應的回調函數,deal_response是回調函數
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, deal_response);
//5 給回調函數傳遞一個形參
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData);
//6 向服務器發送請求,等待服務器的響應
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:perform ERROR, rescode= [%d]\n",
res);
return JNI_FALSE;
}
//(3) 等待服務器的響應 此刻的responseData就是從服務器獲取的數據
/*
//成功
{
result: "ok",
}
//失敗
{
result: "error",
reason: "why...."
}
*
* */
//(4) 解析服務器返回的json字符串
root = cJSON_Parse(responseData.data);
cJSON *result = cJSON_GetObjectItem(root, "result");
if(result && strcmp(result->valuestring, "ok") == 0) {
//登陸成功
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:login succ!!!");
return JNI_TRUE;
}
else {
//登陸失敗
cJSON* reason = cJSON_GetObjectItem(root, "reason");
if (reason) {
//已知錯誤
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:login error, reason = %s!!!", reason->valuestring);
}
else {
//未知的錯誤
__android_log_print(ANDROID_LOG_ERROR,TAG,"JNI-login:login error, reason = Unknow!!!");
}
return JNI_FALSE;
}
return JNI_TRUE;
}
服務器代碼如下http_server.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //for getopt, fork
#include <string.h> //for strcat
//for struct evkeyvalq
#include <sys/queue.h>
#include <event.h>
//for http
//#include <evhttp.h>
#include <event2/http.h>
#include <event2/http_struct.h>
#include <event2/http_compat.h>
#include <event2/util.h>
#include <signal.h>
#include <cJSON.h>
#define MYHTTPD_SIGNATURE "MoCarHttpd v0.1"
//處理模塊
void httpd_handler(struct evhttp_request *req, void *arg) {
char output[2048] = "\0";
char tmp[1024];
//獲取客戶端請求的URI(使用evhttp_request_uri或直接req->uri)
const char *uri;
uri = evhttp_request_uri(req);
#if 0
sprintf(tmp, "uri=%s\n", uri);// /data?cmd=new...
strcat(output, tmp);
#endif
sprintf(tmp, "uri=%s\n", req->uri);
strcat(output, tmp);
//decoded uri
char *decoded_uri;
decoded_uri = evhttp_decode_uri(uri);
sprintf(tmp, "decoded_uri=%s\n", decoded_uri);// /data?cmd= newFile ...
strcat(output, tmp);
//http://127.0.0.1:8080/username=gailun&passwd=123123
//解析URI的參數(即GET方法的參數)
struct evkeyvalq params;//key ---value, key2--- value2// cmd --- newfile fromId == 0
//將URL數據封裝成key-value格式,q=value1, s=value2
evhttp_parse_query(decoded_uri, ¶ms);
//得到q所對應的value
sprintf(tmp, "username=%s\n", evhttp_find_header(¶ms, "username"));
strcat(output, tmp);
//得到s所對應的value
sprintf(tmp, "passwd=%s\n", evhttp_find_header(¶ms, "passwd"));
strcat(output, tmp);
free(decoded_uri);
//獲取POST方法的數據
char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
sprintf(tmp, "post_data=%s\n", post_data);
strcat(output, tmp);
/*
具體的:可以根據GET/POST的參數執行相應操作,然後將結果輸出
...
*/
//入庫
/* 輸出到客戶端 */
//HTTP header
evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE);
evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "close");
//輸出的內容
struct evbuffer *buf;
buf = evbuffer_new();
evbuffer_add_printf(buf, "It works!\n%s\n", output);
//將封裝好的evbuffer 發送給客戶端
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);
}
//處理模塊
void login_handler(struct evhttp_request *req, void *arg) {
printf("got connection ");
char request_data[4096] = {0};
//獲取POST方法的數據
size_t post_size = EVBUFFER_LENGTH(req->input_buffer);
char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
memcpy(request_data, post_data, post_size);
printf("post_data = [%s], len =%ld\n", post_data, post_size);
char username[256] = {0};
char password[256] = {0};
char isDriver[10] = {0};
/*
具體的:可以根據GET/POST的參數執行相應操作,然後將結果輸出
...
*/
/*
====給服務端的協議====
http://ip:port/login [json_data]
{
username: "gailun",
password: "123123",
driver: "yes"
}
*
*
* */
cJSON *root = cJSON_Parse(request_data);
cJSON* username_obj = cJSON_GetObjectItem(root, "username");
strcpy(username, username_obj->valuestring);
cJSON* password_obj = cJSON_GetObjectItem(root, "password");
strcpy(password, password_obj->valuestring);
cJSON* isDriver_obj = cJSON_GetObjectItem(root, "driver");
strcpy(isDriver, isDriver_obj->valuestring);
printf("username = %s, password = %s, isDriver = %s\n", username, password, isDriver);
cJSON_Delete(root);
printf("----\n");
//查詢數據庫 得到查詢結果
//給前段回覆一個響應結果
cJSON*response_root = cJSON_CreateObject();
cJSON_AddStringToObject(response_root, "result", "ok");
char *response_str = cJSON_Print(response_root);
/* 輸出到客戶端 */
//HTTP header
evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE);
evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "close");
//輸出的內容
struct evbuffer *buf;
buf = evbuffer_new();
evbuffer_add_printf(buf, "%s", response_str);
//將封裝好的evbuffer 發送給客戶端
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);
cJSON_Delete(response_root);
free(response_str);
}
void show_help() {
char *help = "http://localhost:8080\n"
"-l <ip_addr> interface to listen on, default is 0.0.0.0\n"
"-p <num> port number to listen on, default is 1984\n"
"-d run as a deamon\n"
"-t <second> timeout for a http request, default is 120 seconds\n"
"-h print this help and exit\n"
"\n";
fprintf(stderr,"%s",help);
}
//當向進程發出SIGTERM/SIGHUP/SIGINT/SIGQUIT的時候,終止event的事件偵聽循環
void signal_handler(int sig) {
switch (sig) {
case SIGTERM:
case SIGHUP:
case SIGQUIT:
case SIGINT:
event_loopbreak(); //終止偵聽event_dispatch()的事件偵聽循環,執行之後的代碼
break;
}
}
int main(int argc, char *argv[]) {
//自定義信號處理函數
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
//默認參數
char *httpd_option_listen = "0.0.0.0";
int httpd_option_port = 7777;
int httpd_option_daemon = 0;
int httpd_option_timeout = 120; //in seconds
//獲取參數
int c;
while ((c = getopt(argc, argv, "l:p:dt:h")) != -1) {
switch (c) {
case 'l' :
httpd_option_listen = optarg;
break;
case 'p' :
httpd_option_port = atoi(optarg);
break;
case 'd' :
httpd_option_daemon = 1;
break;
case 't' :
httpd_option_timeout = atoi(optarg);
break;
case 'h' :
default :
show_help();
exit(EXIT_SUCCESS);
}
}
//判斷是否設置了-d,以daemon運行
if (httpd_option_daemon) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid > 0) {
//生成子進程成功,退出父進程
exit(EXIT_SUCCESS);
}
}
/* 使用libevent創建HTTP Server */
//初始化event API
event_init();
//創建一個http server
struct evhttp *httpd;
httpd = evhttp_start(httpd_option_listen, httpd_option_port);
evhttp_set_timeout(httpd, httpd_option_timeout);
//也可以爲特定的URI指定callback
evhttp_set_cb(httpd, "/", httpd_handler, NULL);
evhttp_set_cb(httpd, "/login", login_handler, NULL);
//循環處理events
event_dispatch();
evhttp_free(httpd);
return 0;
}