카카오 오픈빌더 가이드 링크 및 방화벽 정책

이문서는 https://marxi.co/ 에서 만들어졌습니다.
%23%23%20%uCE74%uCE74%uC624%20%uC624%uD508%uBE4C%uB354%20%uAC00%uC774%uB4DC%20%uB9C1%uD06C%20%uBC0F%20%uBC29%uD654%uBCBD%20%uC815%uCC45%0A%0A%3E%20%uC774%uBB38%uC11C%uB294%20https%3A//marxi.co/%20%uC5D0%uC11C%20%uB9CC%uB4E4%uC5B4%uC84C%uC2B5%uB2C8%uB2E4.%0A%0A*%20%uC2A4%uD0AC%20%uB4F1%uB85D%uBC95%0A%3E%20https%3A//i.kakao.com/docs/skill-build%23%25EC%258A%25A4%25ED%2582%25AC-%25EC%2588%2598%25EC%25A0%2595%0A%0A*%20%uBE14%uB85D%20+%20%uC2A4%uD0AC%20%20%28%uC548%uC368%uB3C4%20%uB428%20%uAE30%uBCF8%uC2A4%uD0AC%uB85C%20%uB4F1%uB85D%uD574%uC11C..%29%0A%3E%20https%3A//i.kakao.com/docs/skill-block%23%25EB%25B8%2594%25EB%25A1%259D%25EA%25B3%25BC-%25EC%258A%25A4%25ED%2582%25AC-%25EC%2597%25B0%25EA%25B2%25B0%25ED%2595%2598%25EA%25B8%25B0%0A%0A*%20%uC2A4%uD0AC%20API%0A%3E%20https%3A//i.kakao.com/docs/skill-response-format%23%25EC%2598%2588%25EC%25A0%259C-%25EC%25BD%2594%25EB%2593%259C-6%0A%0A*%20%uAE30%uBCF8%20%uC815%uCC45%uC774%20deny%20%uC77C%20%uACBD%uC6B0%20%uD5C8%uC6A9%uD574%uC57C%20%uD558%uB294%20%uBC29%uD654%uBCBD%20IP%20%28inbound%20%uC815%uCC45%29%0A%3E%20219.249.231.40%7E42%20%20%28%uC790%uCCB4%20%uD14C%uC2A4%uD2B8%20%uC11C%uBC84%uB85C%20%uAC80%uC99D%uD558%uC5EC%20%uD655%uC778%uD55C%20%uAC83%uC774%uB77C%20%uC5B8%uC81C%uB4E0%20%uBC14%uB014%20%uC218%20%uC788%uC2B5%uB2C8%uB2E4.%29


 폐쇄망에서 White box로 특정 IP만 허용해야했기에, 위 IP 주소를 직접 체크하였습니다.

 https://i.kakao.com/docs/ 에서 챗봇 사용 방법을 찾아보시면 됩니다.

 기존에 외부 API 형태로 플러스친구 챗봇을 제공하셧던 분이라면 스킬을 통하여 동일하게 구현이 가능하며,
저같은 경우는 편법이지만 폴백 블록에 등록된 시나리오와 스킬을 연결하여 이전 플러스친구 챗봇과 동일하게 구현하였습니다.







 프로젝트 도중 서버에 접속하지 않고도 입력 테스트를 간단하게 진행할 수 있어 텔레그램 앱을 사용하게 되었습니다.

 매번 쉘에 접속하여 테스트 명령어를 치지 않고도 간단히 메신저로 테스트 입력값을 쏠 수 있도록 하였습니다.


 텔레그램에서는 봇을 텔레그램 내부에서 실행되는 '타사 응용 프로그램'으로 정의하고 있습니다.

'타사 응용 프로그램' 에서는 https 요청을 통하여 봇을 제어할 수 있습니다. 인증은 bot 에 대한 token 값으로 진행합니다.


 


시작하기



1. http://core.telegram.org/bots 에 접속한 뒤, BotFather 링크를 클릭합니다.






2. Send Message 링크를 클릭합니다.






3. Telegram 의 BotFather 대화방에 들어오면 시작버튼을 누릅니다.



4. 아래 /newbot 명령어로 봇 생성을 하며,  봇의 이름과 봇의 유저이름을 설정해줍니다.  유저이름의 경우 뒤에 _bot 이 들어가야 합니다.




 

5. 생성되었습니다. BotFather 가 알려준 HTTP API 토큰 값을 잘 기억해두어야 합니다.





테스트



 만든 봇을 이용하여 테스트를 진행하도록 하겠습니다.

테스트는 Python 으로 아래 링크를 참조하여 테스트를 진행하였습니다.











테스트 환경 구축


1. pip install python-telegram-bot 명령어로 설치를 진행합니다.






2. git clone https://github.com/python-telegram-bot/python-telegram-bot --recursive 명령어를 통해 샘플 코드를 받습니다.






3. examples 폴더의 echobot.py 파일을 편집합니다. (토큰을 입력합니다.)







테스트 진행


1. 텔레그램에서 만든 Bot을 전역검색하여 찾은 뒤, 아래와 같이 텍스트를 입력하였습니다. 현재까지는 아무 응답이 없습니다.






2. 설치한 서버에서 examples 폴더의 echobot.py 파일을 실행시킵니다. 




3. 입력한 문구들에 대한 답장이 한꺼번에 오는 것을 확인할 수 있습니다.  그 뒤, 텍스트를 입력하면 입력한 텍스트에 대한 답장이 바로 오는 것을 확인할 수 있습니다.



4. 아래는 텍스트 입력할 때마다 출력되는 로그입니다. 

  아래 로그는 코드 내의 update 를 출력한 것으로, 이 update 에 포함되어있는 데이터를 기반으로 Rule-based 시스템을 구축할 수 있습니다.








출처 

https://core.telegram.org/bots/ 공식홈페이지

https://github.com/python-telegram-bot/python-telegram-bot/ 파이썬 예제

https://blog.psangwoo.com/coding/2016/12/08/python-telegram-bot-1.html 설명이 잘 되어 있습니다.

1)    gRPC 관련 지식

A.    gRPC

구글에서 처음 개발하여 공개한 원격 프로시저 호출(RPC) 시스템으로 HTTP/2 를 사용하고 인터페이스 설명 언어로 프로토콜 버퍼를 사용함.

 인터페이스 설명 언어가 하나이기 때문에 다양한 언어에서 데이터를 주고 받을 수 있는 장점이 있음

 

B.     HTTP/2

HTTP/1.1의 알려진 성능 제한이 개선된 프로토콜로 헤더 필드 압축과 동일한 연결에서 다중 동시 교환 허용 등을 통한 리소스 비용 절감 및 성능 개선이 진행됨

 

C.     프로토콜 버퍼

구글에서 공개한 직렬화 데이터 구조로 다양한 언어를 지원한다는 장점이 있다. 최대 64M 까지 데이터를 전달할 수 있다고 하며, JSON 과 유연한 데이터 전환이 가능하다.

관련 출처 : http://bcho.tistory.com/1182

 


2)    gRPC 사용 (python to nodejs)

I.      사용한 소프트웨어  (ubuntu 16.04 환경에서 설치함)

l  nodejs (v6.14.4)

n  nodejs grpc module (v1.11.0)


l  mongoDB(v2.6.10)


l  Python (v2.7.12)

n  grpcio, grpcio-tool (v1.16.0)


II.    protobuf 인터페이스 선언

gRPC 를 사용하려면 받는 쪽과 주는 쪽 모두 인터페이스 모델을 정의해주어야 한다.  message 는 각 언어에서 Object 에 해당하는 값이며 Service Function 에 해당하는 값이다.


syntax = "proto3";

package json;

service Json {

  rpc grpcSend (Request) returns (Response) {}

}

message Request {

  string jsonStr = 1;

}

message Response {

  string jsonStr = 1;

}

 

III.   서버 및 클라이언트 설치


l  nodejs 설치

cd ~

curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh

bash nodesource_setup.sh

apt-get install nodejs

apt-get install build-essential

 

 

l  npm 모듈 설치 (아래 package.json 파일 작성 후 npm install 실행)

{

  "name": "myapp",

  "version": "0.1.0",

  "description": "",

  "main": "app_grpc.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "author": "nanum",

  "license": "ISC",

  "bugs": {},

  "homepage": "",

  "dependencies": {

    "body-parser": "^1.18.3",

    "ejs": "^2.3.3",

    "express": "^4.16.3",

    "grpc-web": "^1.0.0",

    "method-override": "^3.0.0",

    "mongoose": "^5.3.0",

    "requests": "^0.2.2",

    "websocket": "^1.0.28",

    "@grpc/proto-loader": "^0.1.0",

    "async": "^1.5.2",

    "google-protobuf": "^3.0.0",

    "grpc": "^1.11.0",

    "lodash": "^4.6.1",

    "minimist": "^1.2.0"

  }

}

 

l  mongoDB 설치

vi /usr/sbin/policy-rc.d

# exit 101 대신 exit 0 으로 변경.

 

#실행까지 같이

apt-get install mongodb-clients mongodb-server

apt-get update

 

 

l  python 관련 grpc 라이브러리 설치

apt-get install python-pip

pip install --upgrade pip

python -m pip install grpcio #--ignore-installed

python -m pip install --user grpcio-tools

 

 

 

IV.  클라이언트 코드


grpc_sender.py (공통 코드)


from __future__ import print_function

import json

import time

import grpc

import json_pb2

import json_pb2_grpc

 

def make_request(_jsonStr):

    return json_pb2.Request(jsonStr=_jsonStr)

 

def grpcSend(_jsonStr):

    with grpc.insecure_channel('10.0.0.133:3002') as channel:

        request = make_request(_jsonStr)

        stub = json_pb2_grpc.JsonStub(channel)

        response = stub.grpcSend(request)

 

    print('Sent : grpc')

 

 



grpc_inference.py (main)


import sum

import base64

import time

import grpc_sender

 

#ws_sender.sendWs()

 

def toJson(message):

    jsonString = json.dumps(message, ensure_ascii=False).encode('utf8')

    return jsonString

 

def get_data(num):

 

    json_path = '/root/client_send/json/api/inference/current_tracking_'+str(num)+'.json'

    img_path = '/root/client_send/images/cam/cam ('+str(num)+').jpg'

    #json_path = '/home/star/generator/images/screenshots/conference.jpg'

    with open(json_path, 'rb') as single_json:

        json_text = single_json.read()

    with open(img_path, 'rb') as single_img:

        img_b64 = base64.b64encode(single_img.read())

 

    json_text = "{\"api-type\": \"inference\", \"current_tracking\":" + json_text + ", \"img\" : \"" + img_b64 + "\"}"

    print(json_text)

    return json_text

 

 

def main():

    type_num = 1

    while type_num <= 21:

 

        tmpString = get_data(type_num)

        print(tmpString)

        grpc_sender.grpcSend(tmpString)

        type_num = type_num+1

 

        time.sleep(2)

 

if __name__ == '__main__':

    print('start')

    main()

    print('end')

 

 

current_tracking_1.json (샘플 json 데이터)

{

    "time": "2018-10-19T14:45:04.080163+09:00",

    "state_name": "daily_ready",

    "status": null,

    "state_id": 140001861004480,

    "intent": "unknown",

    "slots": null,

    "state_group": "ready",

    "domain": "Daily",

    "state_tag": "Daily.ready.daily_ready.Ready",

    "stance": "neutral",

    "speech": "안녕",

    "subject": null

}

 

 

 

cam (1).jpg (샘플 이미지 데이터)

 


 

V.    서버 코드


l  app_grpc.js (주요 핵심 코드)


/*  proto파일에서 service descriptors 로드    */

var PROTO_PATH = __dirname + '/protobuf/json.proto';

var grpc = require('grpc');

var protoLoader = require('@grpc/proto-loader');

// Suggested options for similarity to existing grpc.load behavior

var packageDefinition = protoLoader.loadSync(

    PROTO_PATH,

    {keepCase: true,

     longs: String,

     enums: String,

     defaults: true,

     oneofs: true

    });

var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);

// The protoDescriptor object has the full package hierarchy

var json = protoDescriptor.json;

/*  proto파일에서 service descriptors 로드 end    */

 

 

 

 

function grpcSend(call, callback) {

  //sendWS(call.request.message);

  console.log(call.request.jsonStr);

  broadcast(call.request.jsonStr);

  callback(null, call.request);

}

function grpc_start() {

  var server = new grpc.Server();

  server.addService(json.Json.service, {grpcSend: grpcSend});

  server.bind('0.0.0.0:4996', grpc.ServerCredentials.createInsecure());

  server.start();

}

grpc_start();



 

VI.  통합 테스트

Receive서버 실행

node app_grpc.js

 

Send클라이언트 실행

python grpc_inference.py






 grpc 도 처음이지만, protobuf 라는 것을 처음 써보았다.


사용해본 바로는 Java의 SOAP 와 JNI 를 합친 느낌이었다.



 JNI 인터페이스 처럼 protobuf 인터페이스를 지정해서 언어에 맞는 전처리 작업을 해주고,


SOAP 처럼 gRPC도 원격으로 서버의 함수를 실행시키는 듯한 느낌을 주었다.


  

 웹 소켓으로 되어 있던 모듈을 grpc로 바꾸는 작업이었던 지라, 


웹 소켓과의 속도 비교도 할 수 있었는데, 약 1.2배정도 빠른 것을 확인할 수 있었다.  (63 kb 데이터)


'프로그래밍' 카테고리의 다른 글

이클립스 꿀팁  (0) 2016.02.12


Jquery 기반



용도

* width 가 100% 인 Image에 imageMap을 입히고 싶을때,

* imageMap 을 입힌 Image 에 onresize 를 통해 사이즈를 조절하고 싶을때 (ImageMap 을 동적으로 사용가능)



https://github.com/stowball/jQuery-rwdImageMaps


사용방법

0) Import 부터..

1) image 에 맞는 map 태그를 작성한다. (반드시 본래 사이즈에서 맞추어야 한다.)

2) css또는 script로 원하는 사이즈로 조절한다. 이때 %로도 가능하다. (ex : sytle="width:100%")

3) 아래 script를 포함시킨다.

$(document).ready(function(e) {
    $('img[usemap]').rwdImageMaps();
});

4) 정상적으로 동작하는지 테스트한다.




2016.02.15일부로 

증상

Xcode로 앱 배포시 Missing iOS Distribution signing identity ...  가 뜨며 인증서를 찾지 못하는 오류.



세부증상

키체인 접근으로 들어가서 인증서를 보니 해당 배포 인증서에 "이 인증서는 유효하지 않은 발급자를 가지고 있음" 오류가 발생.
Apple Worldwide Developer Relations Certification Authority 인증서의 사용기간이 만료됨.



원인

애플에서 발급하는 인증서의
중간기관의 인증서 (WDRCA.CER) 의 인증서가 만료되었습니다. (서울 기준 16년 2월 15일 오전3시까지)
따라서 기존에 사용하던 배포용 인증서 또한 유효하지 않은 발급기관에서 발급한 인증서이므로 
인증서를 사용할 수 없습니다.


조치방법
1) 설치
로그인후 - MemberCenter - Certificates - + 버튼 - 최하단의 Apple Worldwide Developer Relations Certification Authority 다운로드 하여 설치.

2) 기존 인증서 삭제
스포트라이트 - 키체인 접근 - 보기 - 숨겨진 인증서 보기 클릭 - 
로그인, 인증서 에 있는Apple Worldwide Developer Relations Certification Authority 인증서 삭제
시스템, 인증서 에 있는Apple Worldwide Developer Relations Certification Authority 인증서 삭제
(기존 만료된 인증서가 로그인탭, 시스템탭 둘 중 하나라도 남아있으면 해결이 안됨.)

3) 기존 "이 인증서는 유효하지 않은 발급자를 가지고 있음" 오류 가 사라져있는것을 확인.



참고 URL





이클립스 각종 팁

[출처] http://blog.naver.com/sungback/90012467207  작성자 : 메멘토


이클립스 ini 파일 최적화

[출처] http://blog.naver.com/sungback/90012467207

http://kwonnam.pe.kr/wiki/eclipse/config


JS 관련 빌드속도증가

[출처] http://start.goodtime.co.kr/2013/10/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4-validation-%EB%8B%B5%EB%8B%B5%EC%A6%9D/ 




※ window -> preference 를 한번씩 확인해보는 것이 좋다.

'프로그래밍' 카테고리의 다른 글

gRPC 예제 / (Python to nodeJS)  (0) 2018.11.12

특정 Activity LifeCycle 에 기능을 구현할 때 Row Coupling 을 위한 코딩 방법


1) 위 기능에 대하여 Activity 를 상속받는 A 클래스를 만든다. (ServerCheckActivity)

2) A 클래스의 기능을 구현한다.

3) 기존의 Activity 들에 대하여 Activity 를 상속받지 않게 하고 A 클래스를 상속받도록 한다.

4) 기존 Activity 의 oncreate 등의 원하는 LifeCycle 에 A 클래스의 적절한 함수를 실행시킨다. (상속받았으므로 사용 가능)

5) 필요한 경우 Callback 함수를 만들어 쓴다.


이제 관련 기능은 A 클래스만 조작하면 될것이다.




원래는 A 클래스를 따로 빼낼려고 했으나, 작업하면 할수록 A 클래스 내에서 UI 이벤트 쪽 구현에 문제가 생겨

아예 Activity 를 상속받는 방안으로 생각하여 진행한 방법이다.


 보통은 사용자 마다 (리눅스의 User)

.bash_profile 등의 사용자 초기 실행설정에서 

JDK_HOME 이라던가, PATH 등을 정의한다.


 그런데, 한 사용자가 JDK 1.7에서만 돌아가는 프로그램과 JDK 1.8에서만 돌아가는 프로그램 두개를 동시에 돌리게 된다면 어떻게 될까?

둘 중 하나는 지원되지 않는 버전일테고, 그 지원되지 않는 버전일 경우 오류가 발생한다.(UnsupportedClassVersionError)


 이를 해결하기 위해서는 각 프로그램은 각각 다른 패스를 바라볼 필요가 있다.

즉, 패스 관리를 프로그램 별로 하는 것이다.

만약 Java 1.7, Mysql 4.8 기반의 프로그램 이라면 리눅스의 PATH 변수에 두 실행파일의 경로를 넣어주면 되는것이다.


위 내용을 스크립트로 관리하는 것이 아래 내용이다.



 


아래는 Bitnami 의 Redmine Stacks 에서 실제 사용하는 방법이다.


1. 다음과 같은 파일을 만들어서 PATH 부분에 실행할 경로를 넣는다.  (Bitnami Redmine Stack 의 use_Redmine 파일을 참조하였다.)

#use_Redmine 파일

#!/bin/sh

 

PATH="/home/nanum/BitnamiRedmineStack2.5.2/perl/bin:/home/nanum/BitnamiRedmineStack2.5.2/sqlite/bin:/home/nanum/BitnamiRedmineStack2.5.2/ruby/bin:/home/nanum/BitnamiRedmineStack2.5.2/subversion/bin:/home/nanum/BitnamiRedmineStack2.5.2/postgresql/bin:/home/nanum/BitnamiRedmineStack2.5.2/php/bin:/home/nanum/BitnamiRedmineStack2.5.2/mysql/bin:/home/nanum/BitnamiRedmineStack2.5.2/apache2/bin:/home/nanum/BitnamiRedmineStack2.5.2/common/bin:$PATH"

 

exec /bin/bash --noprofile --norc

 

만약 리눅스에 설치되어 있는 기본 JDK 가 1.5인데 1.6 버전을 쓰고싶다면 

PATH="jdk 경로:$PATH" 를 넣어주면 된다.

 

2.  ./"파일이름" 으로 실행하면 쉘이 바로 실행되는데 이 쉘 안에서 실행하고 싶은 프로그램을 돌리면 된다.


   * export PATH=~~~ 가 아닌 PATH=~~~~ 로 되어있다는 것을 알아둬야한다.

     export 명령어를 안썻으니 위의 PATH는 일회용인 것이다.



ex)

./use_Redmine

./redmineCtrl start


 

 

위 방법을 사용하게 된 계기

Jenkins 를 리눅스에서 실행하는데 버전이 낮아서 오류가 났다.

 

 root 의 암호조차 모르던 상태였기 때문에 위 방법을 사용하여 해결하게 되었다.

 

물론 path를 export 한 뒤에 입력해도 된다. 하지만 위 방법이 더 안정성 있다고 생각되고

 

불필요한 작업을 줄인다고 생각하기에 사용하게 되었다.

 

<파일 내용>

#java1.6.sh

#!bin/sh

PATH="/home/alm/jdk1.6.0_45/bin:$PATH"

exec /bin/bash --noprofile --norc


실행 : ./java1.6.sh

  ./Jenkins.jar



 



많고 많은 라이브러리 중 하나.
보통은 json-simple 을 사용하는 것으로 알고 있다.

Json 오브젝트에서 Key의 메타 데이터를 가져올 수 있는 라이브러리가 있는지 확인하는 과정에서 나온 라이브러리. 원하는 건 못찾았다.


          try {
               JSONObject jObject = new JSONObject(jString);

               // menu jsonobject 생성
               JSONObject responseObject = jObject.getJSONObject("response");

              
               JSONObject header = responseObject.getJSONObject("header");
               System.out.println(header.toString());
              
               String resultMsg = header.getString("resultMsg");
               System.out.println(resultMsg);
               String resultCode = header.getString("resultCode");
               System.out.println(resultCode);

              
               JSONObject body = jObject.getJSONObject("response").getJSONObject("body");

               JSONArray item = body.getJSONObject("items").getJSONArray("item");
              
               for(int i=0; i<item.length(); i++){
                    JSONObject item_o =item.getJSONObject(i);
                    System.out.println(item_o.toString());
                   
                    int rnum = item_o.getInt("rnum");
                    System.out.println(rnum);
                    int code = item_o.getInt("code");
                    System.out.println(code);
                    String name = item_o.getString("name");
                    System.out.println(name);
                   
                    /*String rnum = item.getJSONObject(i).getString("rnum");
                    System.out.println(rnum);
                    String code = item.getJSONObject(i).getString("code");
                    System.out.println(code);
                    String name = item.getJSONObject(i).getString("name");
                    System.out.println(name);*/
               }
               int numOfRows = body.getInt("numOfRows");
               System.out.println(numOfRows);
               int pageNo = body.getInt("pageNo");
               System.out.println(pageNo);
               int totalCount = body.getInt("totalCount");
               System.out.println(totalCount);
              
    

          } catch (JSONException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }


JNI 패키지 컴파일 예제



 본 예제는 JNI 를 사용하였을 때 패키지에서의 컴파일 방법을 설명한 예제이다.

참조 : http://lng1982.tistory.com/153 패키지 컴파일 방법.
참조 : http://buedt.tistory.com/50  패키지 안에서 JNI 활용하기 (일반 방법하고 다르다.)

  * 소스 작성 : ~/simple-test/HelloJni

com.HelloWorld.java   -> com 폴더 안에 HelloWorld.java  JNI 모듈이다.
package com;

public class HelloWorld {
public HelloWorld (){
System.loadLibrary("HelloWorld");
}
public native void printHelloWorld();
}
  
com.main.java   -> com 폴더 안에 main.java    JNI 모듈을 사용할 메인 함수이다.
package com;

public class main {
public static void main(String[] args) {
HelloWorld hw = new HelloWorld();
hw.printHelloWorld();
}
}

  
  

  JNI 모듈을 헤더파일로 변경

javac ./com/HelloWorld.java
javah -cp ./ com.HelloWorld



  * javah 로 작성된 com_HelloWorld.h

com_HelloWorld.h    -> 요거는 com 폴더 안에 있는 것이 아니라 컴파일 루트에 있다.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_HelloWorld */

#ifndef _Included_com_HelloWorld
#define _Included_com_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_HelloWorld
* Method: printHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_HelloWorld_printHelloWorld
(JNIEnv *, jobject);


#ifdef __cplusplus
}
#endif
#endif

  * com_HelloWorld.h 에 맞춰서 작성한 .cpp 클래스

HelloWorld.cpp  -> vi HelloWorld.cpp 로 com 폴더 밖에서 작성.
#include <stdio.h>
#include "com_HelloWorld.h"

JNIEXPORT void JNICALL Java_com_HelloWorld_printHelloWorld
(JNIEnv *env, jobject obj){
printf("hi World \n");
return;
}

  동적 라이브러리 생성
gcc -fPIC -g -c -Wall -I /usr/lib/jvm/java-7-openjdk-amd64/include HelloWorld.cpp
gcc -shared -o libHelloWorld.so.1.0.1 HelloWorld.o -lc
ln -s libHelloWorld.so.1.0.1 libHelloWorld.so

  자바 컴파일 및 실행
javac -d . com/*.java
java -cp . com.main


  실행 모습









* 메모리 누수 관련 내용 추가


.cpp 파일에서 jstring param 의 데이터를 쓰고 난 뒤에는

반드시 Release를 해주어야 한다.

이는 jstring 객체 뿐만 아니라 다른  jobject 들도 마찬가지이다.

안 그러면 메모리 누수가 발생한다.



아래는 올바른 예제. 붉은색 글씨가 Get 하고 Release 하는 부분이다.


#include <jni.h>

#include "MessageType.h"

#include <stdio.h>

JNIEXPORT jstring JNICALL Java_MessageType_printMessage(JNIEnv* env, jobject obj, jstring msg) {

   char buf[128];

   const char *str = (*env)->GetStringUTFChars(env, msg, 0);

   printf("%s", str);

   (*env)->ReleaseStringUTFChars(env, msg, str);

   scanf("%s", buf);

   return (*env)->NewStringUTF(env, buf);

} 



출처 : https://rerethink.tistory.com/entry/jni%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%A0%84%EB%8B%AC-%EC%9D%B8%EC%9E%90



'프로그래밍 > JAVA' 카테고리의 다른 글

JDK 버전 충돌이 날 경우 해결 방법  (1) 2016.02.12
Json Parser 라이브러리  (0) 2016.02.12

+ Recent posts