Android NDK, JNI 내부에서 HTTP 통신 사용하기.
지난 포스팅 :
- 안드로이드 스튜디오에서 NDK 로 C++ 빌드하기 (Hello World 예제)
이번시간에는 NDK 를 이용하여 HTTP 통신하는 법을 알아보겠다.
C/C++ 에서 HTTP 를 통신하기 위한 가장 흔한(?) 방법은 curl 을 사용하는 것이다.
그러기 위해서 curl 라이브러리를 android 에서 동작 하도록 컴파일 해야 하는데, 이는 삽질을 동반한 작업들을 해야만 한다.
그래서 이미 빌드된 정적 라이브러리를 사용하는 효율적이고 빠른 방법에 대하여 알아보겠다.
우선 android 용으로 각각의 CPU 별로 깔끔하게 빌드된 정적 라이브러리(.a) 파일을 구해야 한다.
빌드된 curl 을 구하기 위하여 github 와 구글을 한참 검색해 보다가 문득 cocos2d-x 에서 curl 을 사용하던 것을 떠올렸다.
그리하여 cocos2d-x 의 최신버전 패키지를 다운 받고, 아주 이쁘게 빌드된 정적 라이브러리를 얻는데 성공 하였다.
그 과정은 다음과 같다.
- cocos2d-x 다운로드. http://www.cocos2d-x.org/download
- 압축을 풀고 external/curl/prebuilt/android 폴더에 있는 디렉토리와 external/curl/include 에 있는 파일들을 한 곳에 모은다.
- external/curl/prebuilt/ 의 Android.mk 파일의 내용을 현재 프로잭트의 경로에 맞게 수정하여 프로젝트 jni 폴더의 Android.mk 에 복사한다.
주의! 현재까지는 cocos2d-x 내부에서 armeabi, armeabi-v7a, x86, 이 세가지만 빌드되어 있다. 만약 x86_64 옵션을 사용하여 NDK 를 빌드한다면 오류가 발생한다. 너무 당연한 이야기지만 64 비트 호환 CPU 를 쓰는 안드로이드 디바이스 에서도 x86 이나 armeabi 옵션만 주고 빌드한 라이브러리 파일도 모두 읽을 수 있으니 안심하자. |
여기까지가 cocos2d-x 에서 미리 빌드된 curl 정적 라이브러리를 얻는 과정이다. 이렇게만 써 놓으면 다소 복잡해 보일 수 있으니 이제부터 예제와 함께 설명하겠다.
1. 우선 cocos2d-x 에서 가져온 미리 빌드된 curl 정적 라이브러리와 헤더를 압축한 아래의 파일을 다운로드 받는다.
(위 아이콘을 클릭하여 다운받는다,)
2. jni 폴더 내부에 curl 폴더를 만들고 그 안에 방금 다운 받은 파일을 압축 풀어서 붙여넣는다.
3. Android.mk 파일 상단에 다음과 같은 라인을 추가한다.
LOCAL_PATH := $(call my-dir) # --------------- crypto --------------- include $(CLEAR_VARS) LOCAL_MODULE := crypto LOCAL_MODULE_FILENAME := crypto LOCAL_SRC_FILES := curl/$(TARGET_ARCH_ABI)/libcrypto.a LOCAL_EXPORT_LDLIBS := -lz include $(PREBUILT_STATIC_LIBRARY) # --------------- ssl --------------- LOCAL_MODULE := ssl LOCAL_MODULE_FILENAME := ssl LOCAL_SRC_FILES := curl/$(TARGET_ARCH_ABI)/libssl.a include $(PREBUILT_STATIC_LIBRARY) # --------------- libcurl --------------- include $(CLEAR_VARS) LOCAL_MODULE := curl LOCAL_MODULE_FILENAME := curl LOCAL_SRC_FILES := curl/$(TARGET_ARCH_ABI)/libcurl.a LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/curl/include LOCAL_STATIC_LIBRARIES += ssl LOCAL_STATIC_LIBRARIES += crypto include $(PREBUILT_STATIC_LIBRARY) # --------------- your options ---------------
# ...
LOCAL_STATIC_LIBRARIES := curl
# ...
3. 마지막으로 curl 을 사용하려는 C/C++ 소스파일에 #include <curl/curl.h> 으로 include 해주면 된다.
http 통신을 하기 위한 libcurl 의 사용법은 예제 페이지(http://curl.haxx.se/libcurl/c/example.html)를 참고하거나, 구글링으로 쉽게 찾을 수 있다. (이 것은 시간이 나면 따로 정리하겠다.)
아래는 http 통신 예제 코드이며, 예제 프로젝트를 다운받으면 직접 실행해 볼 수 있다.
#include <curl/curl.h> #include <iostream> // Curl 의 Response 처리. size_t responseWriter(char *data, size_t length, size_t bytes, std::string *writerData) { if (writerData == NULL) return 0; long size = length * bytes; writerData->append(data,size); return size; } JNIEXPORT jstring JNICALL Java_kr_re_dev_NdkHttpExample_MainActivity_getHTML(JNIEnv *env, jobject obj, jstring url) { CURL *curl; CURLcode res; std::string response; const char *addr = env->GetStringUTFChars(url, 0); curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, addr); // 웹서버에서 모바일 접속으로 인식할 수 있도록 임의의 User Agent 값을 넣었다. curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Mobile Safari/537.36"); // SSL 관련 옵션. // 3번째 인자값이 0일 경우 인증서 진위 여부에 상관 없이 접속. 기본값 1. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 3번째 인자 값이 0일 경우 인증서 이름에 상관없이 접속. 기본값 2. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // redirecte 사용. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // post 전송시 사용. //curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=daniel&project=curl"); // response 의 response 를 처리하는 함수 설정. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, responseWriter); // 세 번째 인자에는 responseWriter 함수로 처리될 response 객체의 레퍼런스가 들어간다. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); res = curl_easy_perform(curl); // 실패할 경우 메세지 출력. if(res != CURLE_OK) { response = curl_easy_strerror(res); } curl_easy_cleanup(curl); } return env->NewStringUTF(response.c_str()); }
참고용 예제 프로젝트.