상세 컨텐츠

본문 제목

모바일 푸시알람 개발 ios android .. 등등 푸시개발 관련

스마트기기개발관련

by AlrepondTech 2020. 9. 20. 07:02

본문

반응형

 

=================================

=================================

=================================

 

 

node.js 구현

 

출처: http://blog.saltfactory.net/android/implement-push-service-via-gcm.html

 

앱 만들기

GCM을 테스트하기 위한 Android 프로젝트를 하나 만들도록 한다. 완성된 예제는 saltfactory-android-tutorial의 gcm-demo 브랜치에서 받을 수 있다.

Android Studio를 열어서 기본 프로젝트를 만든다.

Start New Android Studio project

 

 

New Project

  • Application name : SF-GCM-DEMO
  • Company Domain : saltfactory.net
  • Package name : net.saltfactory.demo.gcm
  • Project location : /Projects/Repository/Saltfactory/saltfactory-android-tutorial/sf-gcm-demo

으로 프로젝트를 만든다. 위 입력은 자신에 맞게 수정하여 입력한다. 주의할 점은 Pacakge name이다. Android의 어플리케이션의 유일한 identifier는 package name으로 인식하기 때문에 자신의 앱과 중복되지 않은 이름을 입력한다.

 

 

Target Android Device

다음은 개발하고 싶은 SDK를 선택한다. 우리는 Android 스마트폰 GCM을 테스트할 예정이기 때문에Phone and Tablet Minimum SDK를 선택한다. 현재 연구에 사용하고 있는 Android 디바이스가 최신형이 아니기 때문에 API 14(Android 4.0)을 선택했다. 자신에 맞는 SDK를 선택하면 된다.

 

 

Add an activity to Mobile

단순히 GCM을 테스트하기 위해서 우리는 Blank Activity를 선택한다. 실제 프로젝트를 새롭게 진행한다면 자신에게 필요한 Activity를 선택하면 된다.

 

 

Customize the Activity

특별히 다른 작업을 하지 않기 때문에 기본 Activity 이름을 그대로 사용하기로 한다. Title만 GCM demo로 입력한다.

  • Activity Name : MainActivity
  • Layout Name : activity_name
  • Title : GCM demo
  • Menu Resource Name : menu_main

 

 

간단하게 Gradle 기반의 Anroid 프로젝트가 만들어졌다.

 

 

앱 등록하기

Android GCM 서비스를 개발하기 전에 GCM을 사용하기 위해 앱을 등록해야한다. Android GCM 사이트에 접속해보자. https://developers.google.com/cloud-messaging/

 

 

Google Cloud Messasing은 이제 Android 디바이스 뿐만 아니라 iOS 디바이스에도 동일한 로직으로 GCM 서비스를 이용하여 메세지를 보낼 수 있다. iOS를 위한 GCM의 소개는 다음 포스트에서 소개하도록 하고 우선 Android GCM을 소개한다. Google의 서비스는 점차적으로 Material Design으로 디자인을 변경하고 있다. GCM 사이트 역시 Material Design이 적용되어 있다. TRY IT ON ANDROID 버튼을 클릭한다.

우리는 SF-GCM-demo라는 프로젝트를 이미 만들었고 GCM을 추가할 것이기 때문에 add Cloud Messaging to your existing app를 클릭한다.

 

 

Get a configuration file

앞에 설명은 각자 읽어보도록 하고 가운데 정도가면 Get a configuration file이 보일 것이다. GET A CONFIGURATION FILE 버튼을 클릭하자.

Enable Google services for your app

페이지가 전환되면서 Android 앱을 등록하는 화면이 나온다. GCM을 사용하기 위해서 앱을 등록하는 화면이다.

  • App name : 앱 이름을 입력한다. 영문으로만 등록이 가능하다.
  • Android package name : net.saltfactory.demo.gcm

 

 

Continue Choose an configure services 버튼을 클릭한다. 등록하는 시간을 기다리면 다음과 같이 SF-GCM-DEMO 앱이 net.saltfactory.demo.gcm 패키지명으로 등록된 것을 확인할 수 있다. 앱을 등록하면 Google Sigin-in, Cloud Messaging, Analytics 서비스를 사용할 수 있다. 우리는 Cloud Messaging를 상용할 것이기 때문에 다른 설명은 생략한다.

 

 

Google Cloud Messaging 탭을 보면 ENABLE GOOGLE CLOUD MESSAGING 버튼이 보인다. 이 버튼을 눌러줘야 GCM 서비스가 활성화 된다. 버튼을 클릭하자. 그러면 아래와 같이 GCM 서비스가 활성화 된 것을 확인할 수 있다. 이제 Google 서비스에 GCM에 관한 설정은 모두 마친 것이다.

 

 

아래에 보면 Generate configuration files 라는 버튼이 보인다. 이전 GCM에서의 복잡한 설정을 간단하게 configuration file로 처리를 할 수 있게 GCM 서비스가 업그레이드 되었다. 설정 파일을 만들어보자.

Generate Configuration files

GCM 서비스가 업그레이드 되면서 설정하는 프로세스가 매우 직관적으로 변경되었다. 앞에서 설명하듯 차례차례 문서대로 진행하면 다음과 같이 Server API key와 Sender ID를 만들 수 있다. 이전에 블로그에 포스팅한 글의 가장 많은 질문이 Project ID를 어떻게 생성하는지에 대한 질문이였는데 GCM 업그레이드 이후 간단하게 생성할 수 있게 되었다.

 

 

설정 파일 또한 쉽게 생성이 되었다. 상단에 Download google-services.json 버튼을 클릭해서 설정파일을 다운로드한다. 다운로드 받은 google-services.json 파일은 앞에서 만든 Android 프로젝트 디렉토리 안에 app/ 디렉토리 안으로 복사한다. 이 파일이 /app 디렉토리안에 들어있지 않으면 GCM 프로젝트를 빌드할 때 R.string.gcm_defaultSenderId 리소스를 찾을 수 없다는 에러가 발생하면서 빌드가 되지 않는다.

 

 

google-services.json

설정 파일을 열어보자. 이전에 질문이 가장 많았던 Project ID와 Project Number가 자동으로 만들어진 것을 확인할 수 있다.

{
  "project_info": {
    "project_id": "sf-gcm-demo-ae3be",
    "project_number": "636926190444",
    "name": "SF-GCM-DEMO"
  },
  "client": [
    {
      "client_info": {
        "client_id": "android:net.saltfactory.demo.gcm",
        "client_type": 1,
        "android_client_info": {
          "package_name": "net.saltfactory.demo.gcm"
        }
      },
      "oauth_client": [],
      "services": {
        "analytics_service": {
          "status": 1
        },
        "cloud_messaging_service": {
          "status": 2,
          "apns_config": []
        },
        "appinvite_service": {
          "status": 1,
          "other_platform_oauth_client": []
        },
        "google_signin_service": {
          "status": 1
        },
        "ads_service": {
          "status": 1
        }
      }
    }
  ]
}

build.gradle 설정

다음은 Build를 설정해야한다. Android Studio로 Android 프로젝트를 개발하게 되면 이전과 다리 Gradle을 사용하게 되는데, 처음에는 복잡해 보일지 모르지만 기존의 Ant 방식과 달리 다양한 설정을 할 수 있고, 프로젝트에 필요한 dependencies 라이브러리를 자동으로 다운받기 때문에 classpath를 설정해주는 복잡한 과정을 생략할 수 있는 장점이 있다.

Google Cloud Messaging 문서에서는 Add the configuration file to your project를 참고한다.

Android Studio에서 build.gradle 파일을 열어서 아래와 같이 classpath를 추가한다. Android Studio에서 gradle 파일을 수정하면 자동으로 변경된 내용을 반영한다. 만약 자동으로 반영이 되지 않을 경우 Android Studio 오른쪽 Gradle Projects 패널을 열어서 새로 고침 버튼을 클릭하면 Gradle이 자동으로 갱신되어 필요한 라이브러리들을 다운받고 빌드를 진행할 준비를 하게 된다.

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
//        classpath 'com.android.tools.build:gradle:1.2.3'
        classpath 'com.android.tools.build:gradle:1.3.0-beta1'
        classpath 'com.google.gms:google-services:1.3.0-beta1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}


allprojects {
    repositories {
        jcenter()
    }
}

 

 

Set Up Google Play Services

GCM은 Google Play Services로 통합이 되었다. 이전 글에서도 Google Play Services 라이브러리를 사용하여 GCM 서비스를 만드는 방법을 소개했는데 Gradle을 사용하면 아주 쉽게 설정할 수 있다.

app을 설정하는 build.gradle을 열어서 필요할 라이브러리 play-services를 다운 받기 위해서 다음과 같이 dependencies에 compile "com.google.android.gms:play-services:7.5.+”을 추가한다. 이것은 컴파일할 때 play-services 라이브러리가 필요하니 없으면 다운 받아서 컴파일할 때 사용하라는 의미이다.

 

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "net.saltfactory.demo.gcm"
        minSdkVersion 14
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.gms:play-services-gcm:7.5.+'
    compile 'com.android.support:appcompat-v7:22.1.1'

}

 

이제 필요한 라이브러리 설치와 클래스패스 설정을 모두 완료하였다. Gradle을 사용하여 아주 쉽게 설정을 하였다.

AndroidManifest.xml 설정

이젠 Android 앱의 메타정보를 설정하는 AndroidManifest.xml 파일을 열어서 GCM 서비스를 만들기 위한 메타정보를 정의해야한다.

AndroidManifest.xml에 추가해야할 핵심 내용은 다음과 같다.

  • GCM Permission : 디바이스에 GCM 서비스를 사용하기 위한 권한 설정
  • GCM Receiver : GCM을 받았을 때 동작하기 위한 리시버
  • GCM Listener Service : GCM을 요청을 대기하고 있는 리스너 서비스
  • InstanceID Listener Service : InstanceID 요청을 대기하고 있는 리스너 서비스
  • GCM Registration Service : GCM을 등록하기 위한 서비스

위 내용을 입력하기 전에 우선 <!-- comment -->로 AndroidManifest.xml에 추가해보자.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.saltfactory.demo.gcm">

    <!-- [START gcm_permission] -->
    <!-- [END gcm_permission] -->

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- [START gcm_receiver] -->
        <!-- [END gcm_receiver] -->

        <!-- [START gcm_listener_service] -->
        <!-- [END gcm_listener_service] -->

        <!-- [START instanceId_listener_service] -->
        <!-- [END instanceId_listener_service] -->

        <!-- [START gcm_registration_service] -->
        <!-- [END gcm_registration_service] -->
</manifest>

uses-permission

먼저 GCM Permission을 설정하자. 디바이스에서 GCM을 사용하기 위해서는com.google.android.c2dm.permission.RECEIVE와 android.permission.WAKE_LOCK 권한이 필요하다.

  • com.google.android.c2dm.permission.RECEIVE : GCM은 원래 c2dm 이름으로 베타 운영되다가 정식으로 GCM 이름으로 변경이된다. 그래서 패키지 이름이 c2dm 그대로 유지하게 된듯핟.
  • android.permission.WAKE_LOCK : 디바이스가 잠금이되어 화면이 꺼져있을 경우에도 GCM을 받을 수 있기 위해서 디바이스를 깨우는 권한이 필요하다.
  <!-- [START gcm_permission] -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- [END gcm_permission] -->

GCM Reciever

GCM을 받기 위한 리시버를 만들어야하는데 GCM 리시버는 특별히 구현할 필요가 없다. GCM 라이브러리 안에 이미 구현체가 있기 때문에 정의만하면 된다.

   <!-- [START gcm_receiver] -->
        <receiver
            android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="net.saltfactory.demo.gcm" />
            </intent-filter>
        </receiver>
        <!-- [END gcm_receiver] -->

GCM Listener Service

GCM 리스너 서비스는 GCM 메세지가 디바이스로 전송이되면 메세지를 받아서 처리하는 프로그램을 서비스로 정의한다. GCM 받아서 실제 Notification Center에 어떻게 나타내는지를 정의한다. 이후에 살펴볼MyGcmListenerService.java에 내용을 구현할 것이다.

<!-- [START gcm_listener_service] -->
        <service
            android:name="net.saltfactory.demo.gcm.MyGcmListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
        <!-- [END gcm_listener_service] -->

InstanceID Listener Service

최근 GCM 서비스에서는 Instance ID를 사용한다. 이것은 Android, iOS의 고유한 ID로 GCM에서 디바이스를 구분하기 위한 것이다. Instance ID를 위한 리서너를 MyInstanceIDListener.java에서 구현할 것이다.

     <!-- [START instanceId_listener_service] -->
        <service
            android:name="net.saltfactory.demo.gcm.MyInstanceIDListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.gms.iid.InstanceID" />
            </intent-filter>
        </service>
        <!-- [END instanceId_listener_service] -->

GCM Registration Service

실제 디바이스에서 Instance ID를 사용하여 디바이스를 GCM에 등록하고 디바이스 고유 토큰을 생성하기 위한 서비스를 RegistrationIntentService.java에서 구현할 것이다.

    <!-- [START gcm_registration_service] -->
        <service
            android:name="net.saltfactory.demo.gcm.RegistrationIntentService"
            android:exported="false"></service>
        <!-- [END gcm_registration_service] -->

AndroidManifest.xml

GCM을 사용하기 위한 메니페스트 파일 전체 내용은 다음과 같다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.saltfactory.demo.gcm">

    <!-- [START gcm_permission] -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- [END gcm_permission] -->

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- [START gcm_receiver] -->
        <receiver
            android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="net.saltfactory.demo.gcm" />
            </intent-filter>
        </receiver>
        <!-- [END gcm_receiver] -->

        <!-- [START gcm_listener_service] -->
        <service
            android:name="net.saltfactory.demo.gcm.MyGcmListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
        <!-- [END gcm_listener_service] -->

        <!-- [START instanceId_listener_service] -->
        <service
            android:name="net.saltfactory.demo.gcm.MyInstanceIDListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.gms.iid.InstanceID" />
            </intent-filter>
        </service>
        <!-- [END instanceId_listener_service] -->

        <!-- [START gcm_registration_service] -->
        <service
            android:name="net.saltfactory.demo.gcm.RegistrationIntentService"
            android:exported="false"></service>
        <!-- [END gcm_registration_service] -->
    </application>

</manifest>

GCM Demo

GCM Demo를 좀더 사용할만한 데모 앱을 만들기 위해서 ButtonTextView 그리고 ProgressBar를 사용하여 UI를 구성하였다.

 

 

 

 

string.xml

데모에 사용하기 위한 스트링을 정의한 app/src/main/res/values/string.xml을 다음 내용으로 저장한다.

<resources>
    <string name="app_name">GCM Demo</string>
    <string name="registering_message_ready">InstanceID 토큰 가져오기</string>
    <string name="registering_message_generating">InstanceID 토큰 생성중...</string>
    <string name="registering_message_complete">완료!</string>
</resources>

style.xml

데모에 사용하기 위한 스타일을 정의한 app/src/main/res/values/style.xml을 다음 내용으로 저장한다.

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="android:textColor">@android:color/white</item>
        <item name="android:textSize">16sp</item>
    </style>

</resources>

demens.xml

데모에 사용하기 위한 크기를 정의한 app/src/main/res/values/dimens.xml을 다음 내용으로 저장한다.

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

layout.xml

app/src/main/res/layout/activity_main.xml 레이아웃 파일을 열어서 다음 내용을 입력하고 저장한다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:background="@color/blue_grey_700"
    android:orientation="vertical" tools:context=".MainActivity">

    <Button android:id="@+id/registrationButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="@string/registering_message_ready" />

    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/informationTextView"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/registrationProgressBar" />

</LinearLayout>

QuickstartPreferences.java

이 파일은 GCM Demo에서 사용하는 LocalBoardcast의 액션을 정의한 파일이다.app/src/main/java/net/saltfactory/demo/gcm/QuickstartPreferences.java에 아래내용을 저장한다.

package net.saltfactory.demo.gcm;

/**
 * Created by saltfactory on 6/8/15.
 */
public class QuickstartPreferences {

    public static final String REGISTRATION_READY = "registrationReady";
    public static final String REGISTRATION_GENERATING = "registrationGenerating";
    public static final String REGISTRATION_COMPLETE = "registrationComplete";

}

MainActivity.java

GCM Demo의 메인 엑티비티 클래스를 정의하자.app/src/main/java/net/saltfactory/demo/gcm/MainActivity.java 파일을 열어서 다음 내용을 추가한다. 핵심 메소드는 다음과 같다.

  • onCreate() : UI를 정의하고 이벤트와 핸들러를 정의한다.
  • onResume() : 화면이 보여질때 LocalBroadcastManager를 정의한다.
  • onPause() : 화면이 사라질때 LocalBoradcastManager에 등록된 것을 제거한다.
  • checkPlayService() : Google Play Service를 사용할 수 있는 환경인지 체크한다.
  • registBroadcastReciever() : LocalBroadcast 액션에 해당하는 작업을 정의한다.
  • getInstanceIdToken() : GCM을 등록하고 Instance ID에 해당하는 token을 가져온다.

자세한 내용은 코드의 주석으로 대신하고 다음 내용을 저장한다.

package net.saltfactory.demo.gcm;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;

public class MainActivity extends AppCompatActivity {

    private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
    private static final String TAG = "MainActivity";

    private Button mRegistrationButton;
    private ProgressBar mRegistrationProgressBar;
    private BroadcastReceiver mRegistrationBroadcastReceiver;
    private TextView mInformationTextView;

    /**
     * Instance ID를 이용하여 디바이스 토큰을 가져오는 RegistrationIntentService를 실행한다.
     */
    public void getInstanceIdToken() {
        if (checkPlayServices()) {
            // Start IntentService to register this application with GCM.
            Intent intent = new Intent(this, RegistrationIntentService.class);
            startService(intent);
        }
    }

    /**
     * LocalBroadcast 리시버를 정의한다. 토큰을 획득하기 위한 READY, GENERATING, COMPLETE 액션에 따라 UI에 변화를 준다.
     */
    public void registBroadcastReceiver(){
        mRegistrationBroadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();


                if(action.equals(QuickstartPreferences.REGISTRATION_READY)){
                    // 액션이 READY일 경우
                    mRegistrationProgressBar.setVisibility(ProgressBar.GONE);
                    mInformationTextView.setVisibility(View.GONE);
                } else if(action.equals(QuickstartPreferences.REGISTRATION_GENERATING)){
                    // 액션이 GENERATING일 경우
                    mRegistrationProgressBar.setVisibility(ProgressBar.VISIBLE);
                    mInformationTextView.setVisibility(View.VISIBLE);
                    mInformationTextView.setText(getString(R.string.registering_message_generating));
                } else if(action.equals(QuickstartPreferences.REGISTRATION_COMPLETE)){
                    // 액션이 COMPLETE일 경우
                    mRegistrationProgressBar.setVisibility(ProgressBar.GONE);
                    mRegistrationButton.setText(getString(R.string.registering_message_complete));
                    mRegistrationButton.setEnabled(false);
                    String token = intent.getStringExtra("token");
                    mInformationTextView.setText(token);
                }

            }
        };
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        registBroadcastReceiver();

        // 토큰을 보여줄 TextView를 정의
        mInformationTextView = (TextView) findViewById(R.id.informationTextView);
        mInformationTextView.setVisibility(View.GONE);
        // 토큰을 가져오는 동안 인디케이터를 보여줄 ProgressBar를 정의
        mRegistrationProgressBar = (ProgressBar) findViewById(R.id.registrationProgressBar);
        mRegistrationProgressBar.setVisibility(ProgressBar.GONE);
        // 토큰을 가져오는 Button을 정의
        mRegistrationButton = (Button) findViewById(R.id.registrationButton);
        mRegistrationButton.setOnClickListener(new View.OnClickListener() {
            /**
             * 버튼을 클릭하면 토큰을 가져오는 getInstanceIdToken() 메소드를 실행한다.
             * @param view
             */
            @Override
            public void onClick(View view) {
                getInstanceIdToken();
            }
        });

    }

    /**
     * 앱이 실행되어 화면에 나타날때 LocalBoardcastManager에 액션을 정의하여 등록한다.
     */
    @Override
    protected void onResume() {
        super.onResume();
        LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
                new IntentFilter(QuickstartPreferences.REGISTRATION_READY));
        LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
                new IntentFilter(QuickstartPreferences.REGISTRATION_GENERATING));
        LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
                new IntentFilter(QuickstartPreferences.REGISTRATION_COMPLETE));

    }

    /**
     * 앱이 화면에서 사라지면 등록된 LocalBoardcast를 모두 삭제한다.
     */
    @Override
    protected void onPause() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
        super.onPause();
    }


    /**
     * Google Play Service를 사용할 수 있는 환경이지를 체크한다.
     */
    private boolean checkPlayServices() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
                GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                        PLAY_SERVICES_RESOLUTION_REQUEST).show();
            } else {
                Log.i(TAG, "This device is not supported.");
                finish();
            }
            return false;
        }
        return true;
    }
}

RegistrationIntentService.java

이 파일은 Instance ID를 가지고 토큰을 가져오는 작업을 한다. GCM Demo 앱을 위해 토큰을 가져오기 전에 ProgressBar를 동작시키고, 토큰을 가져오는 작업을 완료하면 ProgressBar를 멈추고 TextView에 토큰 정보를 업데이트하기 위한 LocalBoardcast 액션을 추가하였다.app/src/main/java/net/saltfactory/demo/gcm/RegistrationIntentService.java 파일에 아래 내용을 저장한다. 자세한 내용은 주석으로 대신한다.

package net.saltfactory.demo.gcm;

import android.annotation.SuppressLint;
import android.app.IntentService;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;

import java.io.IOException;

/**
 * Created by saltfactory on 6/8/15.
 */
public class RegistrationIntentService extends IntentService {

    private static final String TAG = "RegistrationIntentService";

    public RegistrationIntentService() {
        super(TAG);
    }

    /**
     * GCM을 위한 Instance ID의 토큰을 생성하여 가져온다.
     * @param intent
     */
    @SuppressLint("LongLogTag")
    @Override
    protected void onHandleIntent(Intent intent) {

        // GCM Instance ID의 토큰을 가져오는 작업이 시작되면 LocalBoardcast로 GENERATING 액션을 알려 ProgressBar가 동작하도록 한다.
        LocalBroadcastManager.getInstance(this)
                .sendBroadcast(new Intent(QuickstartPreferences.REGISTRATION_GENERATING));

        // GCM을 위한 Instance ID를 가져온다.
        InstanceID instanceID = InstanceID.getInstance(this);
        String token = null;
        try {
            synchronized (TAG) {
                // GCM 앱을 등록하고 획득한 설정파일인 google-services.json을 기반으로 SenderID를 자동으로 가져온다.
                String default_senderId = getString(R.string.gcm_defaultSenderId);
                // GCM 기본 scope는 "GCM"이다.
                String scope = GoogleCloudMessaging.INSTANCE_ID_SCOPE;
                // Instance ID에 해당하는 토큰을 생성하여 가져온다.
                token = instanceID.getToken(default_senderId, scope, null);

                Log.i(TAG, "GCM Registration Token: " + token);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // GCM Instance ID에 해당하는 토큰을 획득하면 LocalBoardcast에 COMPLETE 액션을 알린다.
        // 이때 토큰을 함께 넘겨주어서 UI에 토큰 정보를 활용할 수 있도록 했다.
        Intent registrationComplete = new Intent(QuickstartPreferences.REGISTRATION_COMPLETE);
        registrationComplete.putExtra("token", token);
        LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
    }
}

MyInstanceIDListenerService.java

이 파일은 Instance ID를 획득하기 위한 리스너를 상속받아서 토큰을 갱신하는 코드를 추가한다.app/src/main/java/net/saltfactory/demo/gcm/MyInstanceIDListenerService.java 파일에 아래 내용을 저장한다.

package net.saltfactory.demo.gcm;

import android.content.Intent;

import com.google.android.gms.iid.InstanceIDListenerService;

/**
 * Created by saltfactory on 6/8/15.
 */
public class MyInstanceIDListenerService extends InstanceIDListenerService {

    private static final String TAG = "MyInstanceIDLS";

    @Override
    public void onTokenRefresh() {
        Intent intent = new Intent(this, RegistrationIntentService.class);
        startService(intent);
    }
}

MyGcmListenerService.java

이 파일은 GCM으로 메시지가 도착하면 디바이스에 받은 메세지를 어떻게 사용할지에 대한 내용을 정의하는 클래스이다. * onMessageReceived() : GCM으로부터 이 함수를 통해 메세지를 받는다. 이때 전송한 SenderID와 Set 타입의 데이터 컬렉션 형태로 받게된다. * sendNotification() : GCM으로부터 받은 메세지를 디바이스에 알려주는 함수이다.app/src/main/java/net/saltfactory/demo/gcm/MyGcmListenerService.java 파일에 아래 내용을 저장한다. 자세한 내용은 주석으로 대신한다.

package net.saltfactory.demo.gcm;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.google.android.gms.gcm.GcmListenerService;

/**
 * Created by saltfactory on 6/8/15.
 */
public class MyGcmListenerService extends GcmListenerService {

    private static final String TAG = "MyGcmListenerService";

    /**
     *
     * @param from SenderID 값을 받아온다.
     * @param data Set형태로 GCM으로 받은 데이터 payload이다.
     */
    @Override
    public void onMessageReceived(String from, Bundle data) {
        String title = data.getString("title");
        String message = data.getString("message");

        Log.d(TAG, "From: " + from);
        Log.d(TAG, "Title: " + title);
        Log.d(TAG, "Message: " + message);

        // GCM으로 받은 메세지를 디바이스에 알려주는 sendNotification()을 호출한다.
        sendNotification(title, message);
    }


    /**
     * 실제 디바에스에 GCM으로부터 받은 메세지를 알려주는 함수이다. 디바이스 Notification Center에 나타난다.
     * @param title
     * @param message
     */
    private void sendNotification(String title, String message) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);

        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_stat_ic_notification)
                .setContentTitle(title)
                .setContentText(message)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }
}

 

기타 필요한 resources들은 github에 올려놓은 예제에서 받아서 복사하여 사용하면 된다.

GCM Demo 실행

이제 GCM 서비스를 만들기 위한 Android에 관한 모든 설정이 끝났다. gradle을 사용하여 디바이스로 실행하면 앱이 실행이 될 것이다. 간단하게 Android Studio에서 Run을 실행하면 된다. 앱을 실행하여 토큰을 획득하면 아래와 같이 Instance ID에 해당하는 token을 가져올 것이다.

 

 

Node.js를 이용한 GCM Provider 만들기

Android 디바이스에서 GCM을 사용하기 위한 토큰을 획득하였다. 이제 서버에서 Android 디바이스에 GCM을 사용하여 메세지를 보내도록 하자. 이전 블로그에서도 Node.js로 GCM을 사용하여 메세지를 보내기 위한 코드를 소개한적이 있는데 그대로 사용하면 된다.

우선 node-gcm 이 필요하기 때문에 설치를 한다. (시스템에 node.js가 설치되어 있다고 가정한다.)

npm install node-gcm

이제 GCM을 사용하여 메세지를 보내기 위한 간단한 provider 소스코드를 작성하자. gcm-provider.js라는 파일로 다음 내용을 저장한다. 서버에서 GCM을 사용하여 메세지를 보낼때 2가지 중요한 변수가 있다.

  • Server API Key : 처음 GCM 웹 페이지에서 앱을 등록하고 획득한 Server API Key 이다.
  • InstanceID token : Android 디바이스에서 Instance ID의 token을 획득한 것을 사용한다.

 

 

var gcm = require('node-gcm');
var fs = require('fs');

var message = new gcm.Message();

var message = new gcm.Message({
    collapseKey: 'demo',
    delayWhileIdle: true,
    timeToLive: 3,
    data: {
        title: 'saltfactory GCM demo',
        message: 'Google Cloud Messaging 테스트’,
        custom_key1: 'custom data1',
        custom_key2: 'custom data2'
    }
});

var server_api_key = ‘GCM 앱을 등록할때 획득한 Server API Key’;
var sender = new gcm.Sender(server_api_key);
var registrationIds = [];

var token = ‘Android 디바이스에서 Instance ID의 token’;
registrationIds.push(token);

sender.send(message, registrationIds, 4, function (err, result) {
    console.log(result);
});

 

이제 메세지를 발송해보자. 정상적으로 발송되면 아래와 같은 화면이 나타난다.

node gcm_provider.js

 

 

이제 Android 디바이스에 GCM으로 메세지가 전송되었는지 Notification Center를 확인하자.

 

 

HTTP Connection Server

GCM 서비스가 지속적으로 업그레이드되면서 더이상 GCM 서버를 만들 필요가 없게 되었다. 이제 서버구현없이 HTTP로 바로 GCM 메시지 전송을 보낼 수 있기 때문이다. 아래와 같이 GCM에서 제공하는HTTP Conection Server를 사용하기 위해서 터미널에서 특별한 프로그램없이 바로 GCM을 사용할 수 있다. Server API Key만 있으면 https://gcm-http.googleapis.com/gcm/send 로 인증과 함께 메세지를 바로 전송할 수 있다.

  • $server_api_key : GCM에 앱을 등록할 때 획득한 Server API Key
  • $token : 디바이스 Instance ID의 token 값
curl --header "Authorization: key=$server_api_key" \
--header Content-Type:"application/json" \
https://gcm-http.googleapis.com/gcm/send \
-d "{\"data\":{\"title\":\"saltfactory GCM demo\",\"message\":\"Google Cloud Messaging 테스트\"},\"to\":\"$token\"}"

 

 

결론

이전에 블로그에 Node.js와 Google Play Service를 이용하여 안드로이드 푸시서비스 구현하기(GCM) 라는 글을 작성하고 많은 질문을 받았다. Google의 푸시 서비스는 C2DM 부터 설정이 꽤 복잡했다. GCM이라는 이름을 변경하고 Google Play Service로 GCM을 통합하면서 gcm.jar를 사용하여 그 방법을 개선하다가, Android Studio와 Gradle의 조합으로 최근에 GCM 서비스를 개발하기에는 매우 간편해졌다. (아직 조금은 복잡하지만) 이 블로그의 글로 최신 GCM을 사용하는데 조금이라도 도움이 되길 바란다. 이번 GCM 업데이트에서는 Instance ID와 HTTP connection Server의 등장으로 GCM을 위한 서버를 구축하지 않아도 되어 GCM 서비스를 구축하는데 더욱 간편해졌다. 한간지 더! 이번 GCM 업데이트로 iOS도 GCM으로 푸시서비스를 구현할 수 있다는 것이다. 이제 Android, iOS 디바이스로 푸시를 발송하기 위해서 따로 푸시 서버(Push Provider)를 구축하지 않아도 된다는 말이다. iOS 디바이스에 GCM을 사용하여 푸시 서비스를 구현하는 방법은 이후에 블로그를 통해 소개할 예정이다.

소스코드

참고

  1. http://blog.saltfactory.net/node/implementing-push-notification-service-for-android-using-google-play-service.html
  2. https://developers.google.com/cloud-messaging/
  3. https://developers.google.com/cloud-messaging/android/start
  4. https://developers.google.com/cloud-messaging/registration
  5. https://developers.google.com/cloud-messaging/android/client
  6. https://developers.google.com/cloud-messaging/http

 

 

=================================

=================================

=================================

 

 

출처: http://hoiogi.tistory.com/

 

APNS Provider 서버를 이용하기 위한 인증서 제작 가이드

iOS SDK / 2013/05/10 16:37

 

 

개요 : Apple Push Notification Service(APNS)를 이용하여 특정 iOS Device에 Remote Message를 전달하기 위해서는 Provider Server가 필요하고 상황에 맞는(개발용, 앱스토어 배포용) 인증서가 필요합니다. 본 문서는 Provider Server를 위한 인증서 제작 방법에 대한 내용을 설명합니다.

 

 

그림1. Push notifications from multiple providers to multiple devices

(출처 : http://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Art/remote_notif_multiple.jpg)



 

 

그림2. Provider-to-Service Connection Trust

(출처 : http://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Art/service_provider_ct.jpg)




인증서 만들기

 

1. https://developer.apple.com/ -> Member Center -> (Login) -> Certificates, Identifiers & Profiles -> Certificates 로 이동합니다.

 




2. “+” 버튼을 눌러 원하는 Type을 선택하고 다음으로 이동합니다.

 

또는

 



3. APNS를 위한 App ID를 선택하고 다음으로 이동합니다.

 

 

4. Keychain(키체인 접근) 을 이용하여 CSR파일 만들기

- 키체인 접근을 실행하고 메뉴 -> 키체인 접근 -> 인증 기관에서 인증서 요청...

 

 

- 인증서 정보 입력하고 .certSigningRequest 파일 저장

 

 

5. 서버에 .certSigningRequest 파일 올리고 인증서 생성하기

 

 

 

 

6. 생성된 인증서를 다운로드 받고 실행시키면 키체인 접근에 아래와 같이 인증서가 등록되었음을 볼 수 있습니다.

 

 



 

 

7. Provider를 위한 .pem 파일 만들기

 

- 아래와 같이 인증서, 키 를 따로 내보내기 합니다. (인증서는 cert.p12 , 키는 key.p12 로 내보내기 하여 적당한 곳에 저장합니다.)

 

 

- 다음은 터미널을 열어 위에서 저장한 .p12 파일 위치로 이동 한 후 아래와 같은 순서로 명령을 실행합니다.

#1> openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12#2> openssl pkcs12 -nocerts -out key.pem -in key.p12

 

주의) openssl pkcs12 -nocerts -out key.pem -in key.p12 명령을 실행 후 “Enter PEM pass phrase:  “ 라고 비밀번호를 입력해야 하는데 #3 단계에서 필요하므로 임의로 7자리 이상의 비밀번호를 입력하여 줍니다.

 

#3> openssl rsa -in key.pem -out key.unencrypted.pem

#4> cat cert.pem key.unencrypted.pem > apns_testapp_development.pem

 

 

#1~#4 까지 처리해주는 자동화 스크립트 추가.

 

 make_push_pem.sh

 

$> Usage: make_push_pem.sh [options] [args]

$> You can get more information: './make_push_pem.sh -h'

 

 

 

* Distribution을 위한 인증서도 위와 같은 방법으로 생성합니다.

 

 

=================================

=================================

=================================

 

 

 

출처: http://www.androidside.com/bbs/board.php?bo_table=b49&wr_id=80531

 

[안드로이드] 

구글 GCM Client/Server 완벽 예제(서버페이지 사용X)스압주의

요번에 C2DM이 GCM으로 정식서비스를 하여서 많은 분들이 혼란을 겪고 있는거 같아

저같은 초보분들 삽질하지 마시라고 가이드를 적어봅니다. (근데 왜 강좌/학습 게시판에 글작성이 안되나요??ㅜㅜ)

몇몇 블로그에 예제들을 보았지만 제가 하려는것과 속성이 틀려서 결국

되지도 않는 영어실력으로 개발자문서를 보며 만들었습니다. ㅜㅜ

저는 웹페이지를 거치지않고 자바단에서만 동작하게 하였습니다(필요시 웹DB 사용)

여기서 사용되는 소스의 주 타겟은 컨텐츠서비스를 하는 어플입니다.

무슨 말이냐하면 유저들 정보를 가지고 있으면 한꺼번에 푸쉬하는거죠(이벤트알림이나 공지사항 등등)

여기선 간단히 설명드리고 자세한건 첨부파일을 받아서 봐주세요.

-------------------- GCMMain.java --------------------

private static final String TAG = "GCM";
 private static final String PASSWORD = "1111";
 //자신의 Project ID 를 넣어주세요
 private static final String SENDER_ID = "자신의 프로젝트 아이디"; 

    private EditText managerPassword;
    private Button managerButton;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //GCM 등록여부
       final String regId = GCMRegistrar.getRegistrationId(this);
       //등록된 ID가 없으면 ID값을 얻어옵니다
       if (regId.equals("") || regId == null) {
        GCMRegistrar.register(this, SENDER_ID);
       }else{
        Log.w(TAG, "Already Registered : " + regId);
       }
        setInit();
    }

------------------------------------------------------

GCMMain.java 에서는 위에 SENDER_ID에 자신의 Project ID를 넣어주시면 됩니다.

 

-------------------- GCMIntentService.java --------------------

private static final String TAG = "GCM";
 private static final String INSERT_PAGE = "http://자신의 서버 아이피/insert_registration.php";
 private static final String SENDER_ID = "자신의 프로젝트 아이디";
 private GCMHttpConnect httpConnect = null;
 private GCMHttpConnect.Request httpRequest = new GCMHttpConnect.Request() {
  
  @Override
  public void OnComplete() {
   // TODO Auto-generated method stub
   showToast();
  }
 };
 
 public GCMIntentService() {
  super(SENDER_ID);
 }

 @Override
 protected void onMessage(Context context, Intent intent) {
  if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
   showMessage(context, intent);
  }
 }

 @Override
 protected void onError(Context context, String msg) {
  // TODO Auto-generated method stub
  Log.w(TAG, "onError!! " + msg);
 }



 @Override
 protected void onRegistered(Context context, String regID) {
  // TODO Auto-generated method stub
  if(!regID.equals("") || regID != null){
   Log.w(TAG, "onRegistered!! " + regID);
//   단일전송일때 주석처리
//   insertRegistrationID(regID);
  }
 }

 @Override
 protected void onUnregistered(Context context, String regID) {
  // TODO Auto-generated method stub
  Log.w(TAG, "onUnregistered!! " + regID);
 }
 
 public void showToast(){
  Toast.makeText(this, "RegID 등록 완료", Toast.LENGTH_LONG).show();
 }
 
 private void showMessage(Context context, Intent intent){
  String title = intent.getStringExtra("title");
  String msg = intent.getStringExtra("msg");
  String ticker = intent.getStringExtra("ticker");
  
  NotificationManager notificationManager = 
  (NotificationManager)context.getSystemService(Activity.NOTIFICATION_SERVICE);
  
//  해당 어플을 실행하는 이벤트를 하고싶을 때 아래 주석을 풀어주세요
//  PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, 
//    new Intent(context, 어플이 처음 시작되는 클래스명.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0);
  PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(), 0);
  
  Notification notification = new Notification();
  notification.icon = R.drawable.ic_launcher;
  notification.tickerText = ticker;
  notification.when = System.currentTimeMillis();
  notification.vibrate = new long[] { 500, 100, 500, 100 };
  notification.sound = Uri.parse("/system/media/audio/notifications/20_Cloud.ogg");
  notification.flags = Notification.FLAG_AUTO_CANCEL;
  notification.setLatestEventInfo(context, title, msg, pendingIntent);
  
  notificationManager.notify(0, notification);
 }
 
 public void insertRegistrationID(String id){
  httpConnect = new GCMHttpConnect(INSERT_PAGE + "?regID=" + id, httpRequest);
  httpConnect.start();
 }

-------------------------------------------------------------

GCMIntentService.java 에서는 INSERT_PAGE와 SENDER_ID가 있습니다.

SENDER_ID는 GCMMain.java 와 같이 자신의 Project ID를 넣어주면 됩니다.

INSERT_PAGE에는 메세지를 일괄전송시에 RegID값들을 DB에서 불러오기 위한 서버주소입니다.

서버가 없고 RegID값을 DB가 아닌 어플내에서 관리한다면 필요없는 부분입니다.

 

그 밑에 PendingIntent 에서 주석으로 된 부분에 어플이 시작되는 클래스명을 적어주면

메세지 수신시 인디케이터에 메시지가 뜨면서 클릭시 해당 액티비티를 실행시키는 겁니다.

 

그리고 insertRegistrationID(String id)부분은 마찬가지로 웹서버에서 DB데이터를 뽑아오는

함수입니다. "?regID=" 부분은 파라메터 인자값이고요. 역시 서버가 없다면 주석처리 하시면 됩니다.

 

-------------------- GCMSendMessage.java --------------------

private Sender    gcmSender;    //GCM Sender
 private Message    gcmMessage;   //GCM Message
 private Result     gcmResult;    //GCM Result(단일 전송)
 private MulticastResult  gcmMultiResult;  //GCM Multi Result(일괄 전송)
 
 //일괄전송에 필요한 List 변수
 private List<String> registrationIds = new ArrayList<String>();
 //단일전송에 필요한 변수
 private String     registrationId = "이곳에 RegId를 입력하세요";
 //DB에서 RegID를 가져오기 위해 만들어진 서버 페이지 주소 
 private static final String SELECT_PAGE = "http://자신의 서버 아이피/select_registration.php";
 //파싱하기 위해 데이터를 담을 변수
 private static String JSON = null;
 //개발자 콘솔에서 발급받은 API Key
 private static String   API_KEY = "자신이 발급받은 API KEY를 입력하세요";
 //메세지의 고유 ID(?)정도로 생각하면 됩니다. 메세지의 중복수신을 막기 위해 랜덤값을 지정합니다
 private static String   COLLAPSE_KEY = String.valueOf(Math.random() % 100 + 1);
 //기기가 활성화 상태일 때 보여줄 것인지. 
 private static boolean  DELAY_WHILE_IDLE = true;
 //기기가 비활성화 상태일 때 GCM Storage에 보관되는 시간
 private static int   TIME_TO_LIVE = 3;
 //메세지 전송 실패시 재시도할 횟수
 private static int    RETRY = 3;
 
 private EditText pushTicker;
 private EditText pushTitle;
 private EditText pushMessage;
 private TextView pushLength;
 
 private Button pushShow;
 private Button pushTrans;
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.send_message);
     
     setLayout();
//     단일전송시에는 주석처리
//     getJson(SELECT_PAGE);
//     단일전송시에는 주석처리
//     getToken();     
 }
 
 public void setMessage(){
  gcmSender = new Sender(API_KEY);
  gcmMessage = new Message.Builder()
     .collapseKey(COLLAPSE_KEY)
     .delayWhileIdle(DELAY_WHILE_IDLE)
     .timeToLive(TIME_TO_LIVE)
     .addData("ticker", pushTicker.getText().toString())
     .addData("title", pushTitle.getText().toString())
     .addData("msg", pushMessage.getText().toString())
     .build();
 }
 
 public void sendMessage(){
  //일괄전송시에 사용
//  try {
//   gcmMultiResult = gcmSender.send(gcmMessage, registrationIds, RETRY);
//  } catch (IOException e) {
//   Log.w(TAG, "IOException " + e.getMessage());
//  }
//  Log.w(TAG, "getCanonicalIds : " + gcmMultiResult.getCanonicalIds() + "\n" + 
//    "getSuccess : " + gcmMultiResult.getSuccess() + "\n" + 
//    "getTotal : " + gcmMultiResult.getTotal() + "\n" + 
//    "getMulticastId : " + gcmMultiResult.getMulticastId());
  
  //단일전송시에 사용
  try {
   gcmResult = gcmSender.send(gcmMessage, registrationId, RETRY);
  } catch(IOException e) {
   Log.w(TAG, "IOException " + e.getMessage());
  }
  Log.w(TAG, "getCanonicalIds : " + gcmResult.getCanonicalRegistrationId() + "\n" + 
    "getMessageId : " + gcmResult.getMessageId());
 }

-----------------------------------------------------------------

메세지를 보내는 GCMSendMessage.java 부분입니다.

String registrationId 는 단일전송시에 직접 입력하는 부분입니다. 등록시 얻은 RegID를 적어주시면 됩니다.

List<String> registrationIds 는 일괄전송시에 사용되는 변수로 onCreate에서

데이터를 얻어오고 그 수만큼 add하고 있습니다.

SELECT_PAGE는 DB에서 RegID들을 얻어오기 위한 서버페이지입니다.

역시 서버가 없다면 무시해도 됩니다.

API_KEY는 Api Console 센터에서 발급받은 Key입니다. 뭐 Project ID와 API_KEY 는 전부 아시리라 믿습니다.

 

 

---------- 정리 ------------

- 단일전송일 경우(한명에게 보내고자 할 경우 or 테스트용)

위에서 말한 API_KEY, SENDER_ID, RegID 변수에 자신의 값들을 넣어주시고 테스트하시면 됩니다.

 

- 일괄전송일 경우

(개인 서버가 있는경우)

위 소스에 있는 주석들을 풀어준 후 자신의 서버주소를 적어주시면 됩니다.

(개인 서버가 없는경우)

RegID 값들을 알 수 있다면 List에 직접 add하여 주시거나

SharedPreferences를 이용하여 데이터를 불러와 add하셔도 됩니다.

혹시 서버페이지를 만드시려고 하는분이 있으시면 참고되시라고 같이 올렸습니다.

---------------------------

 

--------- 필수로 해야할것들 ---------

이 소스를 이용하여 자신의 프로젝트에 적용시킬 때 주의하실점이 있습니다.

- Manifest GCM관련 퍼미션등록 여부와 리시버 등록 여부 확인 (첨부된 프로젝트와 비교하세요)

- 각종 퍼미션 (WAKE_LOCK, INTERNET, GET_ACCOUNTS, VIBRATE) <- 요거안해주면 고생많이 합니다.

- !!!GCM라이브러리 추가!!!(gcm.jar  gcm-server.jar   json_simple-1.1.jar)

제일 중요합니다. 꼭 libs폴더에 넣어서 빌드패스에 추가하셔야합니다.

특히 json_simple-1.1.jar 요놈 없으면 NodefClassException이라는 상큼한 에러가 나옵니다.

라이브러리는 다음과 같은 경로에 있습니다.(개발환경마다 경로가 다를 수 있습니다)

SDK/android-sdk/extras/google/gcm/gcm-client/dlist/gcm.jar

SDK/android-sdk/extras/google/gcm/gcm-server/dlist/gcm-server.jar

SDK/android-sdk/extras/google/gcm/gcm-server/lib/json_simple1.1.jar

-----------------------------------

 

이상으로 글을 마칩니다. 모르시는 부분이 있으면 코드 조금만 분석해보시면

금방 알 수 있을거에요. ㅎㅎ 워낙 허접한 코드라서..

 

저같은 초보분들에게 바칩니다. 해보시고 잘되시면 칭찬한번씩만 해주세요.

회사에서 하라는 일은 안하고 이런짓하고 있어요.

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://scaleup.tistory.com/5

 

JavapnsTest.zip
2.27MB

 

 

지하 깊숙한곳부터 삽질해서 만들지는 않았다.
언제나 그렇듯 개발을 시작하기 전에는 해결하고자 하는 문제에대한 손쉬운 해결책을 검색하게 된다.
눈앞에있는 문제의 본질이 원리를 깨닫고 더 나아가는 것이 아니라면 나는 언제나 오픈소스를 활용한다.
그렇게 세이브한 시간을 좀더 재미있는 일에 투자한다. 각설하고!

http://code.google.com/p/javapns 에 가면 Java로 APNS를 보낼 수 있는  오픈소스가 있다.
예제 코드가 잘되어 있고 사용도 쉬워서 javapns를 이용하기로 결정했다.
아래는 간단히 Wrapping한 코드이다.

import javapns.back.PushNotificationManager;

import javapns.back.SSLConnectionHelper;

import javapns.data.Device;

import javapns.data.PayLoad;



public class JavapnsTest {

	public static int RUN_MODE_DEVELOPMENT = 1;

	public static int RUN_MODE_PRODUCTION = 2;

	

	public void sendApns(int runMode, String deviceToken, String alertMessage, int badgeCount, String soundFile)throws Exception {

		try {

			PayLoad payLoad = new PayLoad();

			payLoad.addAlert(alertMessage);

			payLoad.addBadge(badgeCount);

			payLoad.addSound(soundFile);



			PushNotificationManager pushManager = PushNotificationManager.getInstance();

			pushManager.addDevice("iPhone", deviceToken);



			String host = null;

			String certificatePath = null;

			if (runMode == RUN_MODE_DEVELOPMENT) {

				host = "gateway.sandbox.push.apple.com";

				certificatePath = "./keystore/your_apns_development_key.p12";

			} else if (runMode == RUN_MODE_PRODUCTION) {

				host = "gateway.push.apple.com";

				certificatePath = "./keystore/your_apns_production_key.p12";

			}

			

			int port = 2195;

			String certificatePassword = "Your password";

			pushManager.initializeConnection(host, port, certificatePath, certificatePassword, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);



			Device client = pushManager.getDevice("iPhone");

			pushManager.sendNotification(client, payLoad);

			pushManager.stopConnection();



			pushManager.removeDevice("iPhone");

		} catch (Exception ex) {

			ex.printStackTrace(); 

		}

	}

	

	public static void main(String... args) throws Exception{

		JavapnsTest apns = new JavapnsTest();

		apns.sendApns(

				RUN_MODE_DEVELOPMENT,

				"5c0de8bdc3414816a0eb728751f76be119e9a705e43cdbf0ed16c1772a63218a",

				"Test삼~",

				9999,

				"default");

	}

}


정말 간단하다 -ㅅ-; 굳이 설명할 가치도 못느끼겠다.
간단하게 파라미터만 설명 해보자.

1. runMode : APNS는 Development용과 Production용을 구별하고 있다. 그것에 따라서 사용해야하는 키와 게이트웨이가 다르다.

2. deviceToken : 특정 디바이스에 특정 앱에 부여되는 APNS를 위한 고유값 정도로 알아두자. 이값은 유니크하지만 불변의 값은 아니다. 64자리의 문자열 값이다. 앱에서 아래와 같은 방법으로 얻을 수 있다.

[[UIApplication sharedApplication] registerForRemoteNotificationTypes:

	 (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];

....


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {

#if !TARGET_IPHONE_SIMULATOR

	// ' ', '<', '>' 제거

    NSString *deviceToken = 
    [[[[devToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];

	

NSLog(@"deviceToken : %@", deviceToken);

#endif

} 

 

3. alertMessage : 노티를 받았을 때 뜨는 알럿창의 메세지다.

4. badgeCount : 스프링보드 아이콘에 붙는 빨콩안의 숫자값이다.

5. soundFile : 노티를 받았을 때 재생되는 사운드 파일명이다. 자세한 내용은 깔끔하게 까먹었다.


APNS에 Custom key-value를 추가 할 수 있지만, 이런 부분까지는 각자의 몫으로 하자!

APNS 자체에대해서 궁금하다면 Apple의 문서를 참고하자. 
p12파일을 만드는 방법이 궁금하다면 여기를 참고하자.

 

 

=================================

=================================

=================================

 

 

출처: http://arrkaize86.tistory.com/entry/GCM-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84-%EC%98%88%EC%A0%9C3JAVA%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%91%B8%EC%8B%9C-%EC%84%9C%EB%B2%84

 

 

* GCM 서버 구현 예제(3)_JAVA를 이용한 푸시 서버

 

 - 사용할 jar 파일들 : gcm-server.jar / json-simple-1.1.1.jar

gcm-server.jar
0.02MB
json-simple-1.1.1.jar
0.02MB

 

 

 

1. JAVA 프로젝트 생성

 

 

 

 

 

 

 

 

2. gcm-server.jar 파일과 json-simple-1.1.1.jar 파일을 라이브러리에 추가

 

 

 

 

 

 

 

 

3. Server Key(API) 와 단말의 RegID를 확인

 

4. 아래 소스에 API Key 와 RegID만 추가하여 복사해준다.

 [예문 소스_GcmTestServer.java]
public static void main(String[] args) {
// TODO Auto-generated method stub
Sender sender = new Sender(“프로젝트 서버 API Key 입력"); // 서버 API Key 입력
String regId = “GCM 으로부터 발급받은 단말기 RegID 입력"; // 단말기 RegID 입력



Message message = new Message.Builder().addData("msg", "push notify")
.build();

List<String> list = new ArrayList<String>();
list.add(regId);

MulticastResult multiResult;
try {
multiResult = sender.send(message, list, 5);

if (multiResult != null) {
List<Result> resultList = multiResult.getResults();
for (Result result : resultList) {
System.out.println(result.getMessageId());
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

 

5. 실행하여 메시지 전송을 확인

 

 

 

 

 

 

 

 

 

=================================

=================================

=================================

 

 

 

 

[간략소개]

출처 : http://musasin84.blog.me/60212095039

GCM의 그림

 

 

 

 

 

Google에서 제공하는 메시지 발송 서비스이다. Google Cloud Messaging 의 약어로, 기존에는 C2DM이었으나 C2DM 서비스를 종료하고 GCM으로 바꾸었다. 이 서비스를 이용하기 위해서는 ANDROID Froyo (API Lev 8) 이상이 되어야 한다.

 

메시지는 최대 4KB 크기로 보낼 수 있으며, 이 서비스를 통해 카카오톡, 채팅, 알림 등의 서비스 구현이 가능하다. 100%의 성공률 수신은 장담할 수 없지만 과거 C2DM에 비해 GCM은 성공률이 많이 좋아졌다고 한다. 

 

GCM 발송 프로세스

 

 

 

 

 

1. 앱 어플리케이션에서 RegistrationID를 발급요청

   이 때, GCM 에서 신청한 Project ID를 이용하여 발급요청

2. 발급된 Registration ID를 서버에 저장 시킨 후 이 것을 이용하여 메시지를 전달한다.

   이 때, GCM 에서 신청한 API Key, 앱에서 생성된 RegistrationID, 메시지를 GCM Push Server

   에 전달한다.

3. GCM Push Server에서는 2번에서 보낸 항목들을 확인 후 단말기에 메시지를 보낸다.

[출처] GCM (Google Cloud Messaging) 해보기 - GCM 소개|작성자 

 

 

[GCM설정]

1. GCM 프로젝트 등록

- 우선 GCM을 사용하기 위해서는 GCM에 프로젝트를 등록하고 API를 획득해서 사용해야한다. GCM에 프로젝트를 등록하기 위해서는 Google Developers Console을 연다.

 

Google Developer Console

 

Create Project를 눌러 프로젝트 생성 후(프로젝트 이름 / 프로젝트 ID 입력)

 

 

 

 

위화면에 보이는 Project Number를 잘 기억해둔다.

(1. 앱 어플리케이션에서 RegistrationID를 발급요청 이 때, GCM 에서 신청한 Project ID를 이용하여 발급요청.)

 

2. Google Cloud Messaging for Anroid 활성화

 

[APIs & auth -> APIs] 를 클릭하면 다음 화면이 나온다.

 

 

 

 

여기에서 'Cloud Messaging for Android'로 들어가면 해당 API를 활성화시키는 [Enable API]버튼이 있다. 이를 눌러준다.

 

 

 

 

 

 

3. API Access Key 생성

 

[Credentials] 로 들어가 'Create new Key'를 눌러 API access key를 생성한다. 여러 환경에서 사용할 key를 만드는데, 우리는 Android key를 선택한다.

 

 

 

 

 

 

 

 

 

 

 

 

드디어 API Key를 받급받았다. 이 API Key는 Client에서 GCM Push Server을 이용해 푸시를 받을 수 있는 registration_id값을 획득하는데 사용된다.

(2. 발급된 Registration ID를 서버에 저장 시킨 후 이 것을 이용하여 메시지를 전달한다.

   이 때, GCM 에서 신청한 API Key, 앱에서 생성된 RegistrationID, 메시지를 GCM Push Server

   에 전달한다.)

 

[GCM예제코드]

1. 구글에서 이미 GCM관련 코드를 제공. 소스 다운로드

 

 

 

https://github.com/google/gcm

 

 

2. Google Play Service SDK 다운로드

  인터넷에서 GCM 구현에 관련된 글들을 찾아보면 대부분 gcm.jar를 사용하는데 gcm.jar 역시 deprecate 되었다. 그래서 위에서 다운받은 gcm-client-deprecated를 살펴보면 dist 디렉토리 안에 gcm.jar가 포함되어 있는 것을 확인할 수 있다. 

  하지만 Google에서는 Cloud Messaging Service는 모두 Google Play Service 로 통합되었기 때문에 gcm.jar를 사용하는 포스팅을 참조하기 보다는 Google Play Service SDK를 사용한 예제를 참조하는것이 좋다. 

  Google Play Service SDK는 기본적으로 android SDK를 다운 받는다고 설치되는 것이 아니라 extra 로 따로 다운 받아야 한다. android SDK Manager를 열어서 Google Play Service SDK를 다운로드한다.

 

3. 소스 추가

다운 받은 소스코드의 내용 중, gcm-client 부분을 이클립스에서 프로젝트 추가로 추가시켜놓는다.

 

 

 

 

 

 

 

 

여기에서 추가로 특정 lib를 설정해놓아야한다.

[Google Play Service]

\sdk\extras\google\google_play_services\libproject\google-play-services_lib\libs

[android-support-v4]

\sdk\extras\android\support\v4

 

두개의 라이브러리를 libs폴더를 생성한 후, 추가시킨다.

 

 

 

 

4. 개인 프로젝트를 따로 만들어서 프로젝트를 만들 경우 다음 파일들을 추가한다.

[Google Play Service version.xml 파일]

\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\version.xml

[리소스 파일]

이전에 다운받은 gcm-client디렉토리에서 resources들을 복사

gcm-client\GcmClient\src\main\res\모든폴더

(소스 내용 중, Notofication에서 아이콘 설정시, 쓰임. 만약 본인의 앱에 아이콘이 따로 있다면 설정하지 말고 소스 내용중 Notification관련 설정 부분을 수정하면됨.)

 

 

 

 

5. AndroidManifest.xml 수정

GCM설정을 위해 다음 코드를 추가한다.

?

<!-- GCM connects to Google Services. -->
    <uses-permission android:name="android.permission.INTERNET">


    <!-- GCM requires a Google account. -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS">


    <!-- Keeps the processor from sleeping when a message is received. -->
    <uses-permission android:name="android.permission.WAKE_LOCK">


    <!--
     Creates a custom permission so only this app can receive its messages.


     NOTE: the permission *must* be called PACKAGE.permission.C2D_MESSAGE,
           where PACKAGE is the application's package name.
    -->
    <permission android:name="com.google.android.gcm.demo.app.permission.C2D_MESSAGE" android:protectionlevel="signature">
    <uses-permission android:name="com.google.android.gcm.demo.app.permission.C2D_MESSAGE">


    <!-- This app has permission to register and receive data message. -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE">


<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version">


<!--
          BroadcastReceiver that will receive intents from GCM
          services and handle them to the custom IntentService.


          The com.google.android.c2dm.permission.SEND permission is necessary
          so only GCM services can send data messages for the app.
        -->
        <receiver android:name=".GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <!-- Receives the actual messages. -->
                <action android:name="com.google.android.c2dm.intent.RECEIVE">
                <!-- Receives the registration id. -->
                <action android:name="com.google.android.c2dm.intent.REGISTRATION">
                <category android:name="com.google.android.gcm.demo.app">
            </category></action></action></intent-filter>
        </receiver>


        <!--
          Application-specific subclass of GCMBaseIntentService that will
          handle received messages.


          By default, it must be named .GCMIntentService, unless the
          application uses a custom BroadcastReceiver that redefines its name.
        -->
        <service android:name=".GcmIntentService">
    </service></meta-data></application>






</uses-permission></uses-permission></permission></uses-permission></uses-permission></uses-permission>

 

 

 

6. 프로젝트 Clean -> Build -> Run 과정을 거치면 정상적으로 registrationid를 획득할 수 있을 것이다.!!!

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://blog.saltfactory.net/node/implementing-push-notification-service-for-android-using-google-play-service.html

 

 

Node.js와 Google Play Service를 이용하여 안드로이드 푸시서비스 구현하기(GCM)

Jan 16, 2014

서론

번 사내 프로젝트는 아이폰, 안드로이드 푸시 프로바이더를 springframework에서 Node.js로 마이그레이션하는 작업이 진행되었다. 첫번째 포스팅으로 "Node.js로 푸시서비스 구현하기 1. 아이폰(iOS) 푸시서버 구현하기"에서는 node-apn을 이용해서 아이폰 푸시 프로바이더를 구현한 간단한 예제를 소개하였고, 이번 포스팅에서는 node-gcm을 이용해서 안드로이드 푸시 프로바이더를 구현하는 방법을 소개하고자 한다.

우리는 기존에 C2DM(https://developers.google.com/android/c2dm/?csw=1)을 사용해서 안드로이드 푸시를 구현했었다. 그러나 링크를 보면 알겠지만 C2DM은 2012년 6월 26일부터 더이상 업데이트를 지원하고 있지않다. 그럼 안드로이드 푸시는 어떻게 구현할 수 있는가? 구글에서는 푸시서비스를 위해서 GCM(Google Cloude Messaging) http://developer.android.com/google/gcm/index.html 을 제공하고 있다.

이 글은 2015-06-09-최신 Android Studio, GCM(Google Cloud Messaging), Node.js를 이용하여 Android 푸시 서비스 구현하기 글로 업데이트 되었습니다.

GCM 프로젝트 등록

우선 GCM을 사용하기 위해서는 GCM에 프로젝트를 등록하고 API를 획득해서 사용해야한다. GCM에 프로젝트를 등록하기 위해서는 Google Developers Console을 연다.

CREATE PROJECT 를 눌러서 안드로이드 푸시 프로젝트를 생성한다.

 

 

 

 

프로젝트를 생성하면 고유 Project ID를 입력하고(Project ID는 고유한 값으로 직접 등록할 수 있다.)Project Number를 획득한다.

Google Cloud Messaging for Android 활성화

다음은 APIs & Auth 메뉴를 선택한다. 기본적으로 Google Cloud 서비스 API가 활성화 되어 있는데 GCM을 사용할 때는 필요없기 때문에 모두 비활성화 시킨다. 우리가 필요한 것은 Google Cloud Messaging for Android API 이기 때문에 이 항목을 찾아서 활성화 시킨다.

 

 

API Access Key 생성

다음은 Credentials 메뉴를 선택해서 API access key를 생성한다. CREATE NEW KEY 를 선택하면 여러가지 환경에 사용할 key를 만들 수 있는데, 우선 안드로이드 디바이스에서 API access key를 생성해야하기 대문에 Android Key를 선택한다.

 

 

 

 

 

 

이렇게 등록한 Android API access key는 안드로이드 디바이스에서 GCM을 이용해서 푸시를 받을 수 있는 registration_id 값을 획득하는데 사용된다. 아이폰에서는 device token과 같은 개념이 registration_id 이다. 다음은 안드로이드에 registration_id를 획득하는 라이브러리를 추가하고 코드를 작성한다.

GCM 예제 코드 다운로드

구글에서는 GCM에 관련된 클라이언트 코드를 이미 제공하고 있으니 그 코드를 사용하기로 하자.https://code.google.com/p/gcm/

구글도 이젠 소스코드를 git로 관리하고 있다. GCM에 관련된 코드를 git를 사용해서 clone을 할 수 있다.

git clone https://code.google.com/p/gcm/

 

 

Google Play Service SDK 다운로드

인터넷에서 GCM 구현에 관련된 글들을 찾아보면 대부분 gcm.jar를 사용하는데 gcm.jar 역시 deprecate 되었다. 그래서 위에서 다운받은 gcm-client-deprecated를 살펴보면 dist 디렉토리 안에 gcm.jar가 포함되어 있는 것을 확인할 수 있다. 하지만 Google에서는 Cloud Messaging Service는 모두 Google Play Service 로 통합되었기 때문에 gcm.jar를 사용하는 포스팅을 참조하기 보다는 Google Play Service SDK를 사용한 예제를 참조하는것이 좋다. Google Play Service SDK는 기본적으로 android SDK를 다운 받는다고 설치되는 것이 아니라 extra 로 따로 다운 받아야 한다. android SDK Manager를 열어서 Google Play Service SDK를 다운로드한다.

 

 

이렇게 다운받은 파일은 android-sdk-mac/sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs 라는 디렉토리 안에 존재하게 된다.

 

 

안드로이드 프로젝트 생성

이제 안드로이드 프로젝트를 생성해서 GCM 획득하는 코드를 추가할 것이다. 이름은 sf-push-demo 라고 만들었다. 안드로이드 개발은 여러가지 IDE로 개발할 수 있으니 보통 eclipse로 한다. 우리는 연구소에서 IntelliJ를 Java IDE를 사용하고 있기 때문에 IntelliJ 환경으로 설명하도록 하겠다.

 

 

 

 

프로젝트 이름은 sf-push-demo 라고 하였고, 패키지이름은 net.saltfactory.tutorial.sfpushdemo로 지정하였다.

Google Play Service SDK 라이브러리 복사

그리고 앞에서 다운받은 Google Play SDK를 프로젝트 디렉토리의 libs 안으로 복사한다.

 

 

Google Play Service version.xml 파일 복사

Google Play Service를 사용하기 위해서는 AndroidManifest.xml 파일 안에 <application>태그 안에<meta-data> 값으로 Google Play Service 버전 정보를 넣어줘야하는데 이 정보는 android-sdk-mac/sdk/extras/google_play_services/libproject/google-paly-services_lib/res/values/version.xml 에 존재한다. 이 파일을 복사해서 새로 생성한 프로젝트의 res/values/ 디렉토리 안에 추가한다.

 

 

Android Support 라이브러리 추가

안드로이드에서는 다양한 sdk 버전을 커버하기 위해서 android에서도 확장 라이브러리를 가지고 있는데 GCM 구현에서는 android v4 확장 라이브리가 필요하다. 이것은 android-sdk-mac/sdk/extras/android/support/v4/android-support-v4.jar 에 존재하는데 이것도 libs 디렉토리에 복사해서 넣는다.

 

 

예제 Resources 파일 추가

그리고 앞에서 clone 받은 gcm-client 디렉토리에서 resources들을 복사한다.

 

 

GCM 클래스 추가

이젠 GCM을 사용할 준비를 마쳤으니 코드를 추가하자. gcm-client 안에 있는 소스 중에서src/com/google/android/gcm/demo/app/ 디렉토리 안에서 GcmBroadcastReceiver.java와GcmIntentService.java를 복사해서 프로젝트에 생성된 패키지 안에다 넣는다.

 

 

GcmIntentService 파일 수정

그러면 다음과 같이 GcmIntentService 파일에 에러가 발생하는데 예제로 사용된 gcm-client에서 푸시가 도착하면 DemoActivity를 열려고해서 그런것이다. 이것을 우리가 예제로 생성한 MyActivity가 열리도록 수정을 한다.

// Put the message into a notification and post it. 
// This is just one simple example of what you might choose to do with 
// a GCM message.

private void sendNotification(String msg) {
    mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this,
    MyActivity.class), 0);
    
    NotificationCompat.Builder mBuilder = 
    new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_gcm).setContentTitle("GCM Notification").setStyle(new NotificationCompat.BigTextStyle().bigText(msg)).setContentText(msg);
    
    mBuilder.setContentIntent(contentIntent);
    mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}

AndroidManifest.xml 파일 수정

다음은 안드로이드에 GCM을 사용할 수 있는 설정을 해야하기 때문에 AndroidManifest.xml을 열어서 다음과 같이 수정한다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sfpushdemo" android:versionCode="1" android:versionName="1.0">
    <!--<uses-sdk android:minSdkVersion="19"/>-->
    <!-- GCM requires Android SDK version 2.2 (API level 8) or above. -->
    <!-- The targetSdkVersion is optional, but it's always a good practice to target higher versions. -->
    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <!-- GCM connects to Google Services. -->
    <uses-permission android:name="android.permission.INTERNET" /> <!-- GCM requires a Google account. -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <!-- Keeps the processor from sleeping when a message is received. -->
    <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Creates a custom permission so only this app can receive its messages. NOTE: the permission *must* be called PACKAGE.permission.C2D_MESSAGE, where PACKAGE is the application's package name. -->
    <permission android:name="com.google.android.gcm.demo.app.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.google.android.gcm.demo.app.permission.C2D_MESSAGE" /> <!-- This app has permission to register and receive data message. -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
        <activity android:name="MyActivity" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity> <!-- BroadcastReceiver that will receive intents from GCM services and handle them to the custom IntentService. The com.google.android.c2dm.permission.SEND permission is necessary so only GCM services can send data messages for the app. -->
        <receiver android:name=".GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <!-- Receives the actual messages. -->
                <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <!-- Receives the registration id. -->
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="com.google.android.gcm.demo.app" />
            </intent-filter>
        </receiver> <!-- Application-specific subclass of GCMBaseIntentService that will handle received messages. By default, it must be named .GCMIntentService, unless the application uses a custom BroadcastReceiver that redefines its name. -->
        <service android:name=".GcmIntentService" />
    </application>
</manifest>

 

 

긴 작업이였지만 위의 순서대로 진행하면 오류없이 안드로이드 디바이스로 빌드가 될 것이다. 그리고 빌드가 성공하면 TextView에 안드로이드 디바이스 정보가 나오는 것을 확인할 수 있다.

 

 

이렇게 획득한 registration_id 는 푸시 프로바이더에서 푸시를 발송할 때 사용된다.

node-gcm 설치

이제 안드이드 디바이스는 푸시를 받을 준비를 모두 마쳤다. 이젠 안드로이드 디바이스로 푸시를 발송하는 푸시 프로바이더를 구현할 것이다. 우리는 Node.js를 이용해서 푸시 프로바이더 구현하기로 했다. node-gcm은 이 과정을 매우 간단하게 만들어준다. 먼저 node-gcm을 설치하자.

npm install node-gcm

 

 

Push Provider GCM Server Access Key 생성

우리는 앞에서 안드로이드의 registration_id를 획득하기 위해서 엑세스키를 만드는 과정을 한번 진행했었다. 이제 동일한 작업으로 server key를 생성한다.

 

 

서버는 IPs 접근을 제한하는 항목이 보일것이다. 이제 푸시 프로바이더의 access key를 구했으니 푸시 프로바이더 코드를 작성하자.

var gcm = require('node-gcm'); 

// create a message with default values 
var message = new gcm.Message(); 

// or with object values
var message = new gcm.Message({
    collapseKey: 'demo',
    delayWhileIdle: true,
    timeToLive: 3,
    data: {
        key1: '안녕하세요.',
        key2: 'saltfactory push demo'
    }
});
var server_access_key = '푸시 프로바이더 서버 access key 값';
var sender = new gcm.Sender(server_access_key);
var registrationIds = [];
var registration_id = '안드로이드 registration_id 값'; // At least one required 

/** * Params: message-literal, registrationIds-array, No. of retries, callback-function **/
registrationIds.push(registration_id); 
sender.send(message, registrationIds, 4, function(err, result) {
    console.log(result);
});

 

이제 이 파일을 실행한다.

node sf-push-provider.js

잠시 후 안드로이드 디바이스로 GCM으로 메세지가 전송된 것을 확인할 수 있다. (아래와 같이 출력하기 위해서는 안드로이드의 GCM 코드에 약간의 변경을 가해야한다. 구글에서 제공하는 기본 코드는 푸시 프로바이더의 payload로 전송한 key값을 출력하는 것이 아니라 object자체를 출력하게 되어 있다.) 이 포스팅에 진행된 모든 소스코드는 github를 통해서 공개하고 있으니 참조하기 바란다.

 

 

결론

안드로이드 푸시는 아이폰의 푸시보다 복잡하게 구현한다. 더구나 이전에 C2DM과 gcm.jar를 이용하는 방법의 포스팅이 많아서 대부분 deprecate 되어 있는 자료로 푸시를 구현하고 있어서 Google Play Service를 이용하여 푸시를 전송하는 방법을 소개했다. 더구나 푸시의 핵심은 바로 Push Provider를 구축하는 것인데 기존의 구글에서 제공하는 GCM-Server를 열어보면 Java로 매우 긴 코드로 복잡하게 되어 있다. 우리는 사내 프로젝트로 Springframework를 도입했는데, 단순히 푸시 서버를 구현하는데 너무 많은 자원을 사용하고 복잡도가 높았기 때문에 이번에 Node.js로 마이그레이션을 진행하였다. 이에 우리 연구소에서 진행한 자료를 보다 간단하게 만들어서 공개하기로 결정했으며, 안드로이드 및 아이폰 푸시 서비스를 구축하는 개인 개발자와 소규모 중소기업에 조금이라도 도움이 되길 바란다.

이 글은 2015-06-09-최신 Android Studio, GCM(Google Cloud Messaging), Node.js를 이용하여 Android 푸시 서비스 구현하기 글로 업데이트 되었습니다.

소스코드

참고

  1. http://developer.android.com/google/gcm/index.html
  2. http://developer.android.com/google/gcm/gs.html
  3. https://github.com/ToothlessGear/node-gcm

연구원 소개

 

 

=================================

=================================

=================================

 

 

반응형

 

728x90

 

 

 

출처: http://blog.saltfactory.net/node/implementing-push-notification-service-for-ios.html

 

Node.js를 이용하여 iOS 푸시서비스 구현하기

Jan 15, 2014

서론

이번 프로젝트에서는 Springframework고 구현되어 있는 Push Provider를 nodejs로 마이그레이션하는 작업을 진행하였다. Provider는 일단 간단하게 푸시 전송할 데이터를 사내 데이터베이스 서버에서 RESTful API로 푸시 전송할 항목을 가져와서 Provider 서버의 데이트베이스에 저장하고 순차적으로 push를 전송하는 간단한 로직을 가지고 있는데 Springframework로 구현하니 단순히 Push 서비스만하는데 너무 큰 프레임워크를 도입한 것 같아서 보다 간단한 프레임워크 선정이 필요했고, async push provider를 구현하기 위해서 nodejs를 최종으로 결정해서 구현하기로 했다. 다른 내부적인 로직은 다른 포스팅에서 소개하기로 하고, nodejs 푸시서비스 구현하기 글에서는 Node.js로 아이폰과 안드로이드 Push Provider 서버를 구현하는 글을 소개한다. 첫번째로 아이폰 푸시서버 구현하기에서는 iOS 기계에 푸시를 전송하기 위해서 Push Provider 서버를 구현하는 방법을 간단히 소개한다.

iOS 프로젝트를 생성

데모를 보여주기 위해서 SFPushDemo 라는 이름으로 프로젝트를 만들었다. 단순하게 푸시가 아이폰으로 전송되는 것만 테스트할 것이기 때문에 빈 프로젝트로 만들었다.

 

 

 

 

Code Sign

Xcode는 버전이 올라가면서 눈에 띄는 업데이트가 많이 일어나는 것 같다. 이젠 Xcode 툴 자체에서 개발자 인증 및 프로비저닝 코드 싸인을 검사하고 keychain에 없을경우 웹에서 등록하고 추가하는 대신에 Xcode 자체에서 되도록 업데이트 된것 같다. 점점 웹 없이 Xcode 자체로 개발될 수 있는것 같은 느낌이 든다.

 

 

프로젝트의 code sign이 맞지 않을 겨우 No matching code signing identify found 에러가 나타나는데 이때, Fix issue를 누른다.

 

 

개발자 등록이 되어 있으면 개발자 계정을 Xcode에 추가하면 된다. 이번 포스팅에서 이 내용을 더 깊게 다루지는 않겠다. 다만 이렇게 Xcode가 많이 발전하고 있다는 소개를 잠시 했다.

다시 본론으로 돌아와서 아이폰 앱을 개발하기 위해서는 개발 프로비저닝 파일이 필요하고, 푸시를 보내기 위해서는 푸시 Cetificates 파일이 필요하다. 애플개발자 사이트에 들어가본다.

http://developer.apple.com

App Identifiers 추가

iOS 앱은 유일한 Identifier를 가지고 있고 이것을 이용해서 앱을 식별할 수 있게 된다. 푸시 데모 앱을 위해서 net.saltfactory.tutorial 이라는 identifier를 만들었다. 여기서 identifier는 통상 도메인 이름을 거꾸로 적는다. 여러분들의 필요에 따라서 다른 이름으로 생성하면된다.

 

 

Provisioning Profile 추가

이제 Xcode에서 아이폰 개발을 할 때 사용할 provisioning profile을 추가한다. 추가하고 난 뒤 Download를 눌러 프로비저닝 파일을 다운 받아서 Xcode에 드래그해서 넣어주거나, 프로비저닝 파일을 더블클릭하면 자동적으로 Xcode에 추가된다.

 

 

iOS Certificates(Development) APNs Development iOS 추가

마지막으로 나중에 Push Provider 서버에 사용하기 위한 Certificates 파일을 추가한다. 나중에 푸시 프로바이더 서버를 구현할때 Cetificates 파일이 필요하다. 다운받아 둔다. apn_development.cer 이란 파일로 저장이 될 것이다.

 

 

AppDelegate.m에서 device token 획득

Xcode에서 AppDelegate.m 파일을 열어서 device token을 획득하는 코드를 추가한다.

// 
// SFAppDelegate.m 
// SFPushTutorial 
// 
// Created by saltfactory@gmail.com on 1/14/14. 
// Copyright (c) 2014 saltfactory.net. All rights reserved. 
//

#import "SFAppDelegate.h" 
@implementation SFAppDelegate


-(BOOL) application: (UIApplication * ) application didFinishLaunchingWithOptions: (NSDictionary * ) launchOptions {
    self.window = [
        [UIWindow alloc] initWithFrame: [
            [UIScreen mainScreen] bounds
        ]
    ];
    
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    // 푸시서비스 등록
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert | 
    UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
    
    return YES;
}

이제 아이폰을 Xcode에 연결해서 빌드를 실행한다. 아이폰에서는 푸시서비스를 허용할 것인지 알람이 뜨고 확인을 하면 Xcode 콘솔에서 로그를 확인하면 디바이스 토큰 정보를 확인할 수 있다.

 

 

이제 모바일 디바이스에서는 푸시를 받을 준비를 다 했으니, 푸시 프로바이더 서버를 만들 차례이다.

node-apn 설치

node package는 npm을 이용해서 간단히 설치할 수 있다. 만약 맥을 사용하는데 npm이나 node 명령어가 없을 경우 homebrew를 이용해서 설치하면 간단하게 설치할 수 있다. 맥에서 Node.js는 (http://blog.saltfactory.net/197) 글을 참조해서 설치하면 된다. 푸시 프로바이더 서버는 node라는 디렉토리를 생성 후 그 밑에서 작업을 하였다.

npm install apn

 

 

APNs Certificates 파일과 인증키 생성

우린 앞에서 APNs Development Certificates 을 하나 생성해서 다운 받은 파일이 있다.aps_development.cer 이란 파일이다. 이 파일을 더블 클릭하면 Mac에서 key를 관리하는 KeyChain Access 라는 프로그램에 자동으로 등록이 된다. 열어서 확인해보자.

 

 

Apple Development iOS Push Service:net.saltfactory.tutorial 을 선택하여 오른쪽 마우스를 클릭하여 export를 한다. 그러면 Certificates.p12 파일로 저장이 될 것이다. 이름은 변경해도 무방하다.

 

 

파일을 저장할 때 인증 비밀번호를 물어보는데 비밀번호를 입력하고 기억해둔다.

 

 

위의 두 파일을 keys라는 디렉토리를 만들고 파일을 디렉토리에 복사를 한다.

 

 

node-apn은 pem 파일 포멧을 사용하기 때문에 다음과 같이 두 파일을 pem 파일로 변경한다.

openssl x509 -in aps_development.cer -inform DER -outform PEM -out cert.pem openssl pkcs12 -in Certificates.p12 -out key.pem -nodes

 

 

푸시 프로바이더 구현

이제 푸시 프로바이더를 구현해보자. 소스코드는 간단하게 node-apn에서 제공하는 예제 코드를 이용하면 된다.

var apn = require('apn');
var options = {
    gateway: "gateway.sandbox.push.apple.com",
    cert: './keys/cert.pem',
    key: './keys/key.pem'
};
var apnConnection = new apn.Connection(options);
var token = '앞에서 Xcode로 build 하면서 획득한 아이폰 디바이스 토큰을 입력한다.'
var myDevice = new apn.Device(token);
var note = new apn.Notification();
note.badge = 3;
note.alert = 'saltfactory 푸시 테스트';
note.payload = {
    'message': '안녕하세요'
};

apnConnection.pushNotification(note, myDevice);

이제 이 코드를 실행시켜보자.

node sf_push_provider.js

 

 

결론

이번 포스팅에서는 node-apn을 이용해서 간단한 푸시 프로바이더를 구현하였고, 아이폰 디바이스에 푸시를 전송하는 방법을 소개하였다. 이번 포스팅에 사용된 Node.js 의 모듈은 node-apn을 사용하였다. 우리가 푸시 프로바이더를 동작하기 위해서는 간단한 몇줄의 코드와 인증파일을 생성하는 일이 전부였다. 물론 이렇게 간단한 코드로 서비스를 구현했다고 말하기 어렵다. 앞으로 많은 코드와 결합해서 더욱 멋진 푸시 프로바이더가 만들어질 것인데, APNs 서비르를 구현하기 위해서 가장 필요하면서도 복잡한 부분이 바로 푸시 프로바이더가 애플 푸시 서버로 디바이스정보와 함께 푸시 발송을 요청하는 부분인데 이 부분을 node-apn으로 할 수 있음을 소개하였다. 다음 포스팅은 node-gcm을 이용해서 안드로이드 디바이스에서 푸시 프로바이더를 구현하는 간단한 예제를 소개하도록 하겠다.

소스코드

참고

  1. https://github.com/argon/node-apn

연구원 소개

 

 

=================================

=================================

=================================

 

 

 

출처: http://baramziny.tistory.com/entry/GCM-%ED%91%B8%EC%8B%9C%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%B4%9D-%EC%A0%95%EB%A6%AC

 

 

 

배경지식 ( 이외에 여러사이트 참조 함.. )

http://prev.kr/apps/ColorScripter/  // 코드를 이쁘게 올려주는 사이트

쿼리 ( http://www.w3schools.com/sql/ )

select 문

SELECT column_name,column_name
FROM table_name;

and

SELECT * FROM table_name;

 

예제

The following SQL statement selects the "CustomerName" and "City" columns from the "Customers" table:

Example

SELECT CustomerName,City FROM Customers;

The following SQL statement selects all the columns from the "Customers" table:

Example

SELECT * FROM Customers;

 

Insert 문

It is possible to write the INSERT INTO statement in two forms.

The first form does not specify the column names where the data will be inserted, only their values:

INSERT INTO table_name
VALUES (value1,value2,value3,...);

The second form specifies both the column names and the values to be inserted:

INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);

Example

INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country)
VALUES ('Cardinal','Tom B. Erichsen','Skagen 21','Stavanger','4006','Norway');

 

Delete 문

The DELETE statement is used to delete rows in a table.

SQL DELETE Syntax

DELETE FROM table_name
WHERE some_column=some_value;

 

Assume we wish to delete the customer "Alfreds Futterkiste" from the "Customers" table.

We use the following SQL statement:

Example

DELETE FROM Customers
WHERE CustomerName='Alfreds Futterkiste' AND ContactName='Maria Anders';

Delete All Data

It is possible to delete all rows in a table without deleting the table. This means that the table structure, attributes, and indexes will be intact:

DELETE FROM table_name;

or

DELETE * FROM table_name;

 

쓰레드

RegID를 등록하기 위해서는 네트워크를 사용해야 한다. 사용자의 네트워크  상태에 따라 작업을 할 경우 오랫동안 응답이 없을 수 있다. 안드로이드는 5초 이상 응답이 없을 경우 애플리케이션을 강제 종료 시켜버리므로, 따라서 네트워크 작업은 쓰레드에서 해주어야 한다.

쓰레드의 사용 방법은 두가지가 존재하며, 아래의 블로그를 참조하면 더 자세한 정보를 얻을 수 있다.

1. 런어블을 구성하여 쓰레드에 넘기는 방법

2. 쓰레드 클래스를 상속받는 방법

1. Runnable 인터페이스를 구현
자바에서 인터페이스는 클래스 보다 좀 더 추상적인 개념이다. 가령, 로봇에 대한 속성(filed)이나 동작(method) 등을 클래스로 정의한다면, 어떻게 동작해야 하는 지 까지 새부 소스 코드를 작성해야 한다. 그러나, 인터페이스의 경우 어떠한 동작을 한다라고 까지만 명시할 뿐, 내부적으로 그 동작을 수행 하기 위한 소스 코드를 작성할 필요가 없다. 
 
Runnable  인터페이스는 run() 이라는 메소드(method-수행해야 하는 동작) 하나만 달랑 가지고 있다. 그러므로 Runnable 인터페이스를 구현(implements) 한 클래스에서 run() 메소드를 오버라이딩(overridng) 하여 스레드가 동작하면 어떠한 작업을 수행할 것인지를 run() 메소드 내에서 명시해 줘야 한다.


그 사용 예는 아래와 같다.








2. Thread 클래스를 상속


Thread 클래스 자체는 Runnable 인터페이스를 구현했다. 물론, run() 메소드는 개발자가 다시 오버라이딩 하여 원하는 작업을 수행하도록 정의해야 한다. 아래는 그 예이다.




두 가지 예에서 공통점이 모두 start() 를 호출하여 스레드를 시작한다는 점이다. 


위 두 가지 방법 중 더 자주 쓰이는 방법은 1 번의 방법이다. 자바는 C++ 처럼 다중 상속을 허용하지 않는다. 즉, extends class1, class2 처럼 동시에 두 가지 클래스를 상속할 수 없으며, extends class1 이든 extends class2 이든 한 번에 하나만 상속해야 한다. 그러므로, Thread 클래스를 상속하여 subclass 를 만들 경우, 동시에 다른 클래스를 상속할 수 없는 단점이 있다. 그러므로 Runnable 인터페이스를 구현하는 방법이 더 많이 쓰인다.
[출처] JAVA :: Thread :: 스레드 만들고 시작하기(thread, runnable)|작성자 오바나딩요

 [ 인용 ] http://blog.naver.com/PostView.nhn?blogId=myca11&logNo=80130599386
 [ 좀 더 확실한 설명이 되어 있는 책 ] HeadPlus Java 쓰레드 편을 참조하면 개념을 이해가 더 쉽다.

서버에 올려주는 객체를 만들기 위해서 쓰레드를 상속받아서 사용하기로 하였다. 그 코드를 하나씩 설명하면 아래와 같다.

 

데이터 형식

x-www-form-urlencode
 
http 프로토콜에서 request 나 response 메세지가 발생할 시, 그 메세지는 헤더(header)를 가지고 있다. 이 헤더는 http 트랜젝션이 동작하는 것에 대해서 기술한다. 
http header 에 대해서 살펴보는 것은 다음으로 하고, 오늘 살펴 볼 것은 http header 의 속성 중에 content-type 이라는 것이 있다. 이는 request 나 response 메세지 모두에서 해당하는 값이다. 집중적으로 살펴볼 것은 request 메세지를 보낼 때 content-type 이 어떤 값을 가져야 하는 것인가이다.


request 라는 말 자체에서도 알 수 있듯이 우리가 서버에 무언가를 요청한다는 의미이고, 이 때 서버에 뭔가를 요청할 때, 무엇을 요청할 것인지에 대한 정보 정도는 보내줘야 한다. 가령, 우리가 무언가를 검색한다고 치자. 그럼 무엇을 검색했는 지 검색어 정도는 서버에 보내줘야 한다. 


좀 더 구체적으로 예를 들어보자. 여기는 네이버 블로그이니까 네이버에서 'OECD' 에 대해서 검색한다 치면, 검색창에 OECD 라고 치고 나서 엔터키를 누르게 될 것이다. 이 때 엔터를 치면, request 메세지가 네이버 검색 서버로 넘어간다. 이 때, request 메세지에는 OECD 라는 정보를 가지고 있어야 할 것이다. 그럼 서버에서는 request 메세지 안에서 OECD 라는 값을 찾아낸 뒤에, 해당 OECD 에 해당하는 웹 페이지들을 DB 에서 검색할 것이다. 그리고 (요청한 사람에게 보여주기 위해서) 검색해서 받은 데이터들을 가지고 html 파일을 만든 뒤, 요청한 사람에게 해당 html 파일을 보내준다. 약간 곁 가지를 좀 더 치자면,  네트워크 상에서 다양한 종류의 데이터들이 전송될 수 있고, 그러므로, 각 타입에 따라 서로 다른 방식으로 전송되야 할 수도 있다. 그래서 타입마다 공통된 약속을 정해 놓고 약속에 따라 해당 데이터를 전송하게 된다. html 과 같이 하이퍼텍스트(hypertext) 문서를 보낼 때는 http 라는 프로토콜(약속 혹은 규약) 에 따라 데이터를 주고 받게 된다. (http 은 hypertext transfer protocol 의 약자이다. 우리는 http://www.naver.com 처럼 주소창에 http 라고 적혀있는 것을 많이 봤을 것이다. 이는 html (Hypertext Markup Language) 과 같은 hypertext 문서를 네트워크 상에서 전송할 때, 어떠한 방식으로 주고 받을 것인지를 정해놓은 약속 혹은 규약이다.) 


자, 그럼 진짜 본론으로 들어가서 content-type 은, (OECD 처럼) request 메세지에 포함되어야 하는 정보가 있을 때, 그 데이터 타입이 어떠해야 하는지를 나타낸다. 그런데 모든 request 메세지에 다 지정해 줄 필요가 있는 것은 아니고, request 메세지가 전송되는 방식은 여러가지가 있는데, 그 중 post 나 put 의 경우 content-type 을 지정해줘야 한다. content-type 이 가질 수 있는 값은 MIME type 의 값 들이다.


그럼 MIME type 이 뭔지 또 살펴보자. 에고 공부할게 많다. 인터넷 메디아 타입(internet media type) 이라고도 불리는 이 타입은, Multipurpose Internet Mail Extension 의 약자이다. 처음에 MIME 타입은, SMTP(인터넷 프로토콜(IP) 에서 e-mail 을 보낼 때 사용되던 프로토콜) 를 통해 e-메일을 보낼 때 사용되는 것이 지금은 다른 프로토콜에서도 확장되어 사용되고 있다. MIME 타입은 아스키코드(ASCII code) 로 기술 될 수 없는 메세지를 인코딩 하여 보내는 것을 가능하게 해준다. 즉, 영어 외의 일본어 한국어 같은 것들도 표현하여 메세지를 보낼 수 있다는 의미이다. 또한 그림, 음악, 영화, 컴퓨터 프로그램과 같은 8비트 바이너리 파일을 전자우편으로 보낼 수 있도록 한다. (참고 : http://ko.wikipedia.org/wiki/MIME#Content-Type)


MIME type 은 크게 두 부분으로 나누어 진다. type 과 subtype 으로 나누어 지는데, subtype 에 따라 추가적으로 파라미터를 가질 수 있다. 아래 예를 보자. 


<%@ page contentType="text/html; charset=utf-8" pageEncoding="euc-kr" %>


JSP 파일을 작성할 때 맨 서두에 포함되는 부분이다. 여기도 contentType 이라고 적힌 부분을 볼 수 있다. 여기서 type 에 해당하는 부분이 text, subtype 에 해당하는 부분이 html 이며, charset=utf-8 은 subtype 이 html 일 때 가질 수 있는 파라미터에 해당하는 값이다.
 
이제 마지막이다. request 메세지를 post 방식으로 서버에 보낼 때, MIME type 은 무엇이어야 하는가?? post 방식으로 보낸 다는 것은, request 메세지가 헤더 부분 말고도 따로 데이터를 저정할 수 있는 저장 공간이 있으며, 그러므로 보내려는 정보를 이 공간에다 저장해서 보내는 방식이 post 방식이다. 웹 브라우저에서 web form 엘리먼트로 부터 post 방식으로 데이터를 보낼 때, 표준 MIME type 이 바로 application/x-www-form-urlencoded 이다. (web form 엘리먼트라는 말은 우리가 검색창에서 검색어를 입력한 뒤, 그 입력값을 전달할 수 있는 것처럼, 사용자의 input 을 받아서 처리할 수 있도록 해주는 엘리먼트를 의미한다.)
 
application/x-www-form-urlencoded 방식을 선택하면, key-value 형태로 인코딩 하게 된다. 가령, 위의 예제에서 검색어가 OECE 일 경우, search=OECD 이런 식으로 인코딩 될 것이다. 만약 검색을 OECD UN 이렇게 두 단어로 했다면, search=OECD&search=UN 이렇게 인코딩 될 것이다. 참고로, 스페이스의 경우, 인코딩 되면 + 로 바뀐다. 아래는 application/x-www-form-urlencoded 타입으로 인코딩 했을 때를 보여준다.
[출처] network :: content-type, MIME, application/x-www-form-urlencoded|작성자 오바나딩요
 [ 인용 ] http://blog.naver.com/myca11/80131096119

 

기타 HTML 코드

http://www.zetswing.com/bbs/board.php?bo_table=JS_TIP&wr_id=81  // 페이지 이동 없이 PHP에서 데이터를 처리하는 코드

 

PHP 서버단 코드

시스템 구조

 

 

 

메시지를 등록하여 보내는 코드

Colored By Color Scripter

<script language="javascript"> 
function save_fr() { 
        var form = document.msg_form;
        form.target = "hid_fun"  
        form.submit(); 
        return false; 

</script> 
style="display:none"
<iframe name="hid_fun" ></iframe> 


<form name="msg_form" action="test_send.php"  method="post">
 <p>메세지: <input type="text" name="msg_a" /></p>
 <p><input type="button" onClick="save_fr();" value="제출"/></p>
</form>

< 실행 화면 >

 

 

 

PHP 용 서버, 디바이스에서 받은 register_id를 데이터베이스에 등록하는 PHP파일

Colored By Color Scripter

<?php
    $HOST="localhost";
    $DBNAME="lamp_test";
    $DBUSER="student";
    $DBPW="1234";
    
    $response=array();
    $data=array();


    if(isset($_POST['reg_id'])&&(isset($_POST['msg'])))
    {
        $reg_id=$_POST['reg_id'];
        $msg=$_POST['msg'];


        $data["reg_id"] = $reg_id;    
        $data["msg"] = $msg;


        echo json_encode($data);


        $connect=mysql_connect($HOST,$DBUSER,$DBPW) or die("Failed to connect MY-SQL");
        // DB접속에 성공했다면 $result_select_db는 null이 아니다.
        $result_select_db = mysql_select_db($DBNAME,$connect);
        if($result_select_db == null) { echo("DB접속에 실패했습니다."); }
        else 
        {
            if ($msg == "register") $result = mysql_query("INSERT INTO test_table(reg_id,msg) VALUES('$reg_id','$msg')");
            else $result = mysql_query("Delete From test_table WHERE reg_id='$reg_id'");
            if ($result) {
                // successfully inserted into database
                $response["success"] = 1;
                $response["message"] = "Success.";
                echo json_encode($response);
            } else  {
                // failed to insert row
                $response["success"] = 0;
                $response["message"] = "Oops! An error occurred.";


                echo json_encode($response);
            }
        } // end else
    } // end if
    else {
        // required field is missing
            $response["success"] = 0;
            $response["message"] = "Required field(s) is missing";


           echo json_encode($response);
    } // end else


?> 


 



 

데이터베이스에서 reg_id를 불러와서 데이터를 보내는 PHP소스코드

Colored By Color Scripter

<?php
    /*
    * 데이터베이스에 접속하는데 필요한 정보를 입력한다.
    */
    $HOST="localhost";
    $DBNAME="lamp_test";
    $DBUSER="student";
    $DBPW="1234";


    $headers=array('Content-Type:application/json','Authorization:key=AIzaSyD1B-0C71NUgv-aaeCe8H0rfVC3vWoqfok');
    
    $arr=array();
    $arr['data']=array();
    $arr['data']['msg']=urlencode($_POST['msg_a']);
    $arr['registration_ids']=array();


    $i = 0;


    $connect=mysql_connect($HOST,$DBUSER,$DBPW) or die("Failed to connect MY-SQL");
    $result_select_db = mysql_select_db($DBNAME,$connect);
    if($result_select_db == null) { echo("DB접속에 실패했습니다."); }
    else 
        {
            /*
            * 테이블에서 reg_id를 가져오는 구분
            */
            
            mysql_query('SET CHARACTER SET utf8');
            $result = mysql_query("SELECT reg_id FROM test_table");
                while($row = mysql_fetch_array($result))
                {
                    $arr['registration_ids'][$i]=$row['reg_id'];                    
                    $ch=curl_init();
                    curl_setopt($ch,CURLOPT_URL,'http://android.googleapis.com/gcm/send');
                    curl_setopt($ch,CURLOPT_HTTPHEADER, $headers);
                    curl_setopt($ch,CURLOPT_POST, true);
                    curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
                    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
                    curl_setopt($ch,CURLOPT_POSTFIELDS,json_encode($arr));
                    $reponse=curl_exec($ch);
                    
                    
                    // 만약 디바이스가 Uninstall 되었다면, GCM에서 메시지를 받아서 DB를 삭제한다.
                    $obj=json_decode($reponse,true);
                    var_dump($obj);


                    if ( $obj['results'][0]['error'] == "NotRegistered" ) 
                    {
                        $reg_id = $row['reg_id'];
                        mysql_query("Delete From test_table WHERE reg_id=''");
                    }
                }
        }
?>

 

 

JAVA 파일

 

 

 

 

제대로 DB에 연결되면, 애플리케이션의 앱에서 로그로 아래와 같이 보여준다.

 

 

 

 

 

 

 

CommonUtility.java

Colored By Color Scripter

package com.example.pushservertest;


import org.json.JSONException;
import org.json.JSONObject;


import android.content.Context;
import android.content.Intent;


/**
 * Helper class providing methods and constants common to other classes in the
 * app.
 */
public final class CommonUtilities {
    


    /**
     * Google API project id registered to use GCM.
     */
    static final String SENDER_ID = "10777641425";  // Google API console ?먯꽌 ?살? Project ID 媛믪쓣 ?ㅼ젙 
    static final String SERVER_URL = "http://server.kdml.co.kr:7380/push/wh/test_receiver.php";  // 데이터베이스를 등록하는 서버
  
    /**
     * Intent used to display a message in the screen.
     */
    static final String DISPLAY_MESSAGE_ACTION="com.google.android.gcm.pushservertest.app.DISPLAY_MESSAGE";


    /**
     * Intent's extra that contains the message to be displayed.
     */
    static final String EXTRA_MESSAGE = "message";


    /**
     * Intent's extra that contains the message to be displayed.
     */
    public static String PROPERTY_REG_ID = "registration_id";
    
    /**
     * Notification's Icon
     */
    public static int noti_icon = R.drawable.ic_launcher;
    
    /**
     * Notifies UI to display a message.
     * <p>
     * This method is defined in the common helper because it's used both by
     * the UI and the background service.
     *
     * @param context application's context.
     * @param message message to be displayed.
     */
    
    /**
     * Message to server to request
     *
     */
    public static String register = "register";
    public static String delete = "delete";
    
    
    static void displayMessage(Context context, String message) {
        Intent intent = new Intent(DISPLAY_MESSAGE_ACTION);
        intent.putExtra(EXTRA_MESSAGE, message);
        context.sendBroadcast(intent);
    }
    
     static String JsonParser(String Jsondata)
     {
         String resultStr = "";
         try {
            
             JSONObject jObj = new JSONObject(Jsondata);
             resultStr += jObj.getString("msg");
             
             
        } catch (JSONException e) {
            // TODO: handle exception
        }
         
         return resultStr;
     }


}

 

GCM3rdPartyRequest.Java

Colored By Color Scripter

package com.example.pushservertest;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;


import android.os.AsyncTask;
import android.util.Log;


public class GCM3rdPartyRequest extends Thread {


    public final String TAG = "GCM3rdPartyRequest";
    private String mAddr;
    private String mResult;
    private String reg_id;
    private String msg;


    public GCM3rdPartyRequest(String addr, String reg_id, String msg)
    {
        mAddr=addr;
        mResult="";
        this.reg_id=reg_id;
        this.msg = msg;
    }
    
    @Override
    public void run() {
        Log.i(TAG, "In Run");
        // Thread를 싱행한다.
        super.run();
        
        StringBuilder html=new StringBuilder();
        
        // 서버를  연다. 
        URL url;
        try {
            // 서버를  연다.
            Log.i(TAG, "서버 연결");
            url = new URL(mAddr);
            HttpURLConnection conn=(HttpURLConnection)url.openConnection();    
            
            Log.i(TAG, "서버세팅");
            // 서버를 세팅한다. 서버 전송 방식은 POST를 사용한다.
            conn.setDefaultUseCaches(false); 
            conn.setDoInput(true); // 서버에서 읽기 모드 지정
            conn.setDoOutput(true); // 서버로 쓰기 모드 지정 
            conn.setRequestMethod("POST");
            
            // 데이터 전송양식은 x-www-form-urlencoded 로 사용한다. 필요하다면 , json을 사용해도 된다. 
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");


            StringBuffer buffer = new StringBuffer();
            buffer.append("reg_id").append("=").append(reg_id).append("&");
            buffer.append("msg").append("=").append(msg);
            
            Log.i(TAG, "데이터 전송");
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8"));
            pw.write(buffer.toString());        //write하는순간 서버인(php)로  buffer 데이터 넘어감.
            pw.flush();


            Log.i(TAG, "데이터 읽어오기");
            BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));


            while(true){
            String line=br.readLine();
            if(line==null) break;
            html.append(line);
            }
            br.close();
            mResult=html.toString();
            Log.i(TAG,mResult);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            
    }
}

GCMIntentService.java

Colored By Color Scripter

package com.example.pushservertest;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;


import android.os.AsyncTask;
import android.util.Log;


public class GCM3rdPartyRequest extends Thread {


    public final String TAG = "GCM3rdPartyRequest";
    private String mAddr;
    private String mResult;
    private String reg_id;
    private String msg;


    public GCM3rdPartyRequest(String addr, String reg_id, String msg)
    {
        mAddr=addr;
        mResult="";
        this.reg_id=reg_id;
        this.msg = msg;
    }
    
    @Override
    public void run() {
        Log.i(TAG, "In Run");
        // Thread를 싱행한다.
        super.run();
        
        StringBuilder html=new StringBuilder();
        
        // 서버를  연다. 
        URL url;
        try {
            // 서버를  연다.
            Log.i(TAG, "서버 연결");
            url = new URL(mAddr);
            HttpURLConnection conn=(HttpURLConnection)url.openConnection();    
            
            Log.i(TAG, "서버세팅");
            // 서버를 세팅한다. 서버 전송 방식은 POST를 사용한다.
            conn.setDefaultUseCaches(false); 
            conn.setDoInput(true); // 서버에서 읽기 모드 지정
            conn.setDoOutput(true); // 서버로 쓰기 모드 지정 
            conn.setRequestMethod("POST");
            
            // 데이터 전송양식은 x-www-form-urlencoded 로 사용한다. 필요하다면 , json을 사용해도 된다. 
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");


            StringBuffer buffer = new StringBuffer();
            buffer.append("reg_id").append("=").append(reg_id).append("&");
            buffer.append("msg").append("=").append(msg);
            
            Log.i(TAG, "데이터 전송");
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8"));
            pw.write(buffer.toString());        //write하는순간 서버인(php)로  buffer 데이터 넘어감.
            pw.flush();


            Log.i(TAG, "데이터 읽어오기");
            BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));


            while(true){
            String line=br.readLine();
            if(line==null) break;
            html.append(line);
            }
            br.close();
            mResult=html.toString();
            Log.i(TAG,mResult);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            
    }
}

MainActivity.java

Colored By Color Scripter

package com.example.pushservertest;


import com.google.android.gcm.GCMRegistrar;


import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;


public class MainActivity extends Activity {


    private static final String TAG = "GCM";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        registerReceiver(mHandleMessageReceiver,
                new IntentFilter(CommonUtilities.DISPLAY_MESSAGE_ACTION));
        // GCM 등록 여부를 확인한다.
        try {
            GCMRegistrar.checkDevice(this);
            GCMRegistrar.checkManifest(this);
        } catch (Exception e) {
            // TODO: handle exception
            Log.e(TAG,"This device can't use GCM");
        }
        
        final String regId = GCMRegistrar.getRegistrationId(this);
        if ( regId.equals(""))
        {
            GCMRegistrar.register(this, CommonUtilities.SENDER_ID);
            Log.i(TAG, "Device registered");
            Log.i(TAG, "Device registered: regId = " + regId);
        }
        else 
        {
            Log.i(TAG,"Already registered");
            Log.i(TAG, "Already Device registered: regId = " + regId);
        }
    }


    
    
    @Override
    protected void onDestroy() {
        
        unregisterReceiver(mHandleMessageReceiver);
        GCMRegistrar.onDestroy(getApplicationContext());
        super.onDestroy();
    } 
    
    // BR 보내온 데이터를 받아오는 역활을 한다. 받아오는 파일의 형식은 Extra 이다.
    private final BroadcastReceiver mHandleMessageReceiver =
            new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {            
            
        }
    }; // end BroadcastReceiver
} // end class 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://lyb1495.tistory.com/90

 

JAVA로 GCM, APNS 사용하기

본 포스팅은 java 서버 기반으로 apns, gcm을 활용해 모바일 기기로 PUSH 메세지를 전송하는 방법에 대해 설명합니다.

모바일 기기 단에서 처리 코드는 포함하지 않습니다 :)

 

1. APNS 사용하기

애플의 아이폰 등의 기기에 PUSH 메세지를 전송하기 위해서는 다음과 같은 준비물이 필요합니다.

 

* javaPNS 라이브러리 : http://code.google.com/p/javapns/

* javaPNS에서 사용하는 Bouncy Castle 라이브러리 : http://www.bouncycastle.org/

* PUSH 인증서/인증서 비밀번호

* 디바이스 UDID

* 모바일앱

 

javaPNS는 Apple Push Notification Service(APNS)를 사용하기 쉽게 만들어진 오픈소스 라이브러리로 GNU Lesser GPL 라이선스를 따릅니다. 

 

본 포스팅에서 사용되는 javaPNS 버전은 2.2 입니다. 

JVM 버전은 1.5 이상을 쓰면 되지만 현재 1.7 버전에서는 SSL connection오류가 발생할 수 있음에 주의합니다. 

또한 javaPNS는 BouncyCastle에서 제공하는 암복호화 라이브러리에 의존성이 있습니다. BouncyCastle를 온전히 사용하기 위해서는 JCE Policy 파일 패치가 필요할 수 있습니다.

 

이상 위 준비과정을 마쳤다면, APNS는 아주 쉽게 사용할 수 있습니다.

기본 코드는 아래와 같습니다.

view plaincopy to clipboardprint?

public boolean send(String certificate, String password, boolean production, String udid, String message, Map<String, String> extra) {  
    try {  
        PushNotificationPayload payload = PushNotificationPayload.complex();  
        payload.addAlert(message);  
        payload.addBadge(-1);  
        payload.addSound("default");  
              
        if( extra != null ) {  
            Iterator<String> keys = extra.keySet().iterator();  
            while( keys.hasNext() ) {  
                String key = keys.next();  
                String value = extra.get(key);  
                  
                payload.addCustomDictionary(key, value);  
            }  
        }  
  
        PushedNotifications notifications = Push.payload(payload,   
                certificate, password, production, udid);  
              
        return (notifications != null && notifications.size() > 0 && notifications.get(0).isSuccessful());  
              
    } catch (Exception e) {  
        logger.error(e, e);  
    }   
          
    return false;  
}  

certificate는 인증서 경로, password는 인증서 비밀번호, udid는 디바이스 고유키 입니다.

extra는 전송 메세지 외에 커스텀 딕셔너리에 넣을 데이터를 처리합니다.

 

APNS 전송이 수 만건 단위로 많아질 경우 위 코드를 그대로 사용하는 것은 매우 비효율적입니다.  

 

APNS의 경우 한번 커넥션이 맺어질 경우 해당 커넥션을 통해 다수의 PUSH 데이터를 한번에 전송하는 방식을 사용해야 합니다. 필요에 따라 멀티쓰레드 방식을 통해 PUSH 데이터를 나누어 전송할 수도 있습니다. 

 

Message Broadcasting, payloadPerDevice, multithread, connection pool과 같은 고급 주제들 또한 javaPNS 문서와 소스에서 힌트를 얻을 수 있으니 반드시 참고하기를 권장합니다.

 

 

2. Google Cloud Messaging for Android(GCM) 사용하기

GCM을 사용하기 위해서는 다음과 같은 준비물이 필요합니다.

 

* Google API Console을 통해 프로젝트 생성

* GCM 활성화

* API Key 및 프로젝트 ID 얻기

* 디바이스 토큰

* 모바일 앱

* gmc-server 라이브러리

* json-simple 라이브러리

 

먼저 https://code.google.com/apis/console/ 에 처음 방문하면 다음 그림과 같이 프로젝트를 개설하기 위한 과정이 나타납니다.

 

 

 

프로젝트를 생성한후 좌측 메뉴의 Services에서 "Google Cloud Messaging for Android"를 활성화 해줍니다.

 

 

 

 

그 다음 좌측 메뉴의 API Access에서 아래 그림과 같이 Create New Server Key를 생성합니다.

 

 

 

 

서버 키를 생성하면서 접근 가능한 서버의 IP를 입력하는 과정이 있습니다. 

정상적으로 서버 키가 생성됬다면 아래와 같이 API키와 허용 IP목록을 확인할 수 있씁니다.

 

 

 

 

프로젝트 ID는 다음 그림과 같이 URL의 project: 다음의 일련번호를 통해 확인할 수 있습니다.

 

 

 

 

gcm-server 라이브러리는 안드로이드 SDK의 extras > google > gcm > gcm-server > dist 에서 찾을 수 있씁니다.

만약 gcm이 없다면 안드로이드 SDK 매니저를 통해 설치해야합니다.

 

gcm-server는 json-simple 라이브러리에 의존성이 있습니다. 

json-simple은 http://code.google.com/p/json-simple/ 에서 다운로드 받을 수 있습니다.

 

기본 코드는 다음과 같습니다.

view plaincopy to clipboardprint?

public boolean send(String apiKey, String token, String message, Map<String, String> extra) {  
    Sender sender = new Sender(apiKey);  
          
    try { message = URLEncoder.encode(message, "UTF-8"); }   
    catch (UnsupportedEncodingException ignore) {}  
          
    Message.Builder messageBuilder = new Message.Builder();   
    messageBuilder.delayWhileIdle(false);  
    messageBuilder.timeToLive(1800); // 30min  
    messageBuilder.addData("msg", message);  
          
    if( extra != null ) {  
        Iterator<String> keys = extra.keySet().iterator();  
        while( keys.hasNext() ) {  
            String key = keys.next();  
            String value = extra.get(key);  
                  
            messageBuilder.addData(key, value);  
        }  
    }  
          
    try {  
        Result result = sender.send(messageBuilder.build(), token, 5);  
        String messageId = result.getMessageId();  
              
        return (messageId!=null);  
    } catch(Exception e) {  
        logger.error(e, e);  
    }             
          
    return false;  
}  

APNS와 마찬가지로 많은 양의 PUSH 메세지를 위 코드를 통해 처리하는 것은 매우 비효율 적입니다.

 

C2DM에서 GCM으로 넘어오며 얻을 수 있는 큰 장점중의 하나는 Message Broadcasting이 가능해졌다는 것입니다.

아직 경험적/통계적으로 GCM 에서의 적절한 connection과 multithread 갯수는 파악하지 못했지만, Message Broadcasting 기술과 delayWhileIdle, timeToLive의 설정값을 통해 PUSH 메세지 도달율을 C2DM보다 높힐 수 있을것이라 기대합니다. 

 

 

=================================

=================================

=================================

 

 

 

출처: http://qnibus.com/blog/how-to-make-certification-for-apns/

 

APNS 포스팅 개요

1년만에 다시 푸쉬서비스를 만들려고 하니 기억도 안나고 검색해서 찾아보니 너무 옛날 데이터들이라 헷갈리기도 해서 “이참에 좀 자세하게 기록해놓자!!” 하는 마음에 포스팅을 했습니다. 총 3단계로 나누어 인증서 설치, 서버단에서 푸시 보내기, 앱단에서 푸시 처리하기로 해서 포스팅을 시작하고자 합니다.
APNS란 용어는 애플 개발자 레퍼런스에도 너무 자세하게 나와있고 검색해도 수도 없이 나오기때문에 생략하고 철저하게 개발 적용에만 촛점을 맞추도록 하겠습니다.

 

APNS 인증서 발급 조건

  • 발급일 2013년 7월경
  • Mac OS  X 버전 10.8.4 (산사자)
  • Xcode 버전 4.6.3
  • iOS7이 베타3까지 출시

 

APNS 인증서 발급을 위한 개인 인증서 만들기

우선 애플(Apple) 사이트에서 Notification 인증서를 만들기 위해서는 아래와 같이 키체인을 이용해
CertificateSigningRequest.certSigningRequest 라는 인증서를 만들어야 합니다.

이 인증서는 Notification 인증서를 발급받을 때 반드시 필요하니 우선 따라서 만들어 봅시다.
키체인(Keychain)을 실행한 후 다음과 같이 따라 해보세요!

 

 

키체인을 실행한 후 키체인접근> 인증서지원 > 인증 기관에서 인증서 요청을 누릅니다.

 

 

 

디스크저장됨을 선택하고 본인이 키 쌍 정보 저장을 체크하세요! 특히 이메일은 개발하는 애플 개발자 아이디를 넣어주시기 바랍니다.

 

 

그림과 같이 설정하시고 다음으로 넘어가세요!

 

 

인증서 서명을 하기 위한 요청 파일이 완성되었습니다.

이렇게 작업을 완료하시고 Finder에서 보기를 눌러 보시면 다음과 같은 파일이 만들어집니다.

CertificateSigningRequest.certSigningRequest

 

 

APNS 인증서 발급받기

반드시 애플 개발자 계정을 가지고 계셔야 접근하실 수 있습니다. 일반 애플 계정과는 다르니 안되시는 분은 개발자 등록부터 해주시기 바랍니다.

이제 개인 인증서를 만들었으니 애플 개발자 사이트로 이동합니다.
개발자 사이트에 접속하시면 상단에 Member Center라는 곳이 있습니다.
접속하시면 바로 아래의 이미지와 같은 화면을 보실 수 있습니다. 그림을 보시면서 순서대로 따라하시면 금방 APNS용 인증서를 발급받을 수 있습니다.
그럼, 멤버센터에서 Certificates, Identifiers & Profiles > Identifiers > APP ID’s 메뉴로 이동하세요!

 

 

 

멤버센터 메인 화면에서 빨간 박스의 Certificates, Identifiers & Profiles를 클릭하세요!

 

 

APP IDs 메뉴를 눌러 들어온 화면입니다. 자신이 만들어놓은 앱아이디를 한눈에 보실 수 있습니다. 그럼, 노티를 원하는 앱아이디를 선택하세요!

 

 

선택하시면 해당 앱에서 무슨 서비스가 연동되고 사용되는지 한눈에 보실 수 있습니다. 그중 Push Notification 서비스가 Enable 되어있는지 확인하고 Disable 되어있으면 하단의 Edit 버튼을 누릅니다.

 

 

저는 이미 만들어 놓아 인증서 내역이 보이지만 몇개고 만들 수 있습니다. 개발용과 배포용으로 만들 수 있으며 우선 개발용으로 해보겠습니다. 자 그럼, Create Certificate를 눌러주세요!

 

 

이과정은 아까 맥에서 만들었던 인증서를 만드는 방법에 대해서 안내해주는 화면입니다. 우린 이미 만들었으니 다음 단계로 넘어가시죠!

 

 

아까 만들었던 인증서를 업로드해야 합니다. Choose File을 눌러 CertificateSigningRequest.certSigningRequest 파일을 연결해주세요!

 

 

자! 그럼 Generate를 눌러 APNS 인증서를 발급받아보세요!

 

 

드디어 인증서가 만들어졌습니다. 다운로드를 눌러주세요!

 

 

인증서가 이제 다운되었습니다. 다음은 인증서를 클릭해 실행해주세요! 그럼 키체인에 인증서가 추가됩니다.

 

이렇게 해서 인증서 발급을 과정을 마쳤습니다. 생각보다 어렵지 않습니다.
자 그럼, 이제부터가 중요한데요! 서버에 직접 설치할 인증서를 만들어 보겠습니다.

 

APNS 서버인증서 만들기

서버인증서를 만드는 작업을 하기전에 우선 키체인을 열어 방금 설치한 Apple Development(or Production) iOS Push Services: kr.co.godo.godo 인증서를 찾아서 Export(내보내기)를 해서 .p12 파일로 만들어줘야 합니다.
다음의 그림을 보면서 따라하시면 되겠습니다. 그림과 같이 인증서 옆에 화살표 아이콘이 있고 그걸 누르면 개인키가 나타나오니 참고하시면서 작업하시기 바랍니다.

 

 

저는 개발과 출시용 Push Service 인증서를 둘다 설치해서 2개가 나타나며 원하는 것을 선택해서 만들어주시면 됩니다.

 

 

우선 그림과 같이 마우스 오른쪽을 눌러 보내기 메뉴를 눌러주세요!

 

 

그럼 이러한 창이 뜰 것이고 별도저장(Save as)에 cert라고 넣어주세요!

 

 

저장을 누르면 패스워드를 넣으라는 창이 나타납니다. 이곳에 원하는 패스워드를 넣고 승인을 눌러주세요!

 

 

다음은 개인키입니다. 그림과 같이 열쇠모양의 개인키를 선택해 오른쪽 마우스 눌러 보내기를 클릭합니다.

 

 

별도저장(Save as)에 key라고 입력하고 저장을 눌러줍니다.

 

 

외우기 쉽도록 cert에서 만든 패스워드를 동일하게 넣어주세요!

 

이작업을 하시면 바탕화면(여러분이 지정한 경로)에 cert.p12와 key.p12 파일들이 만들어집니다.
이제부터 중요합니다. 인증서 형식을 변환하기 위한 작업으로 Terminal에서 작업을 할 것 입니다.
딱 4가지 명령만 실행하시면 됩니다.  ———- 이걸로 표시된 부분은 결과값을 보여주려고 나타낸 것이니 터미널에서는 입력하지 말아주세요!

 

openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12


----------------------------------------------------------
Enter Import Password:
Mac verified OK
----------------------------------------------------------

다음과 같이 입력하고 엔터를 치면 비밀번호를 물어봅니다.
이 비밀번호는 아까 cert.p12 만들때 작성하신 패스워드를 넣어주시면 됩니다.
이렇게 하고 나면 해당 경로에 cert.pem 이라는 파일이 생깁니다.

 

 

openssl pkcs12 -nocerts -out key.pem -in key.p12


-------------------------------------------------
Enter Import Password:
Mac verified OK
Enter PEM pass phrase:
Verifying = Enter PEM pass parase:
-------------------------------------------------

위 코드는 key.p12를 key.pem으로 만들어주는 작업입니다.
바로 윗부분에 cert.pem 만드는 작업과 동일하지만 마지막에 패스워드를 생성하는 작업을 해주셔야 합니다.

 

 

openssl rsa -in key.pem -out key.unencrypted.pem


------------------------------------------------
Enter pass phrase for key.pem:
writing RSA key
------------------------------------------------

위에서 만들어진 key.pem을 가지고 key.unencrypted.pem을 만드는 코드입니다.
하단의 출력결과에도 써있다 시피 윗단계에서 새로 생성한 패스워드를 입력하시면 됩니다.
이제 마지막 하나가 남았습니다.

 

 

cat cert.pem key.unencrypted.pem > apns.pem

마지막부분에 apns.pem이라고 적어놓은 부분은 여러분이 만들고 싶은 이름으로 변경해서 작성해주시면 됩니다.
이렇게 마무리 하시면 작업하신 해당 경로에 아래리스트와 같이 6개의 파일들이 생성되어 있을 것이니 꼭 확인해주시기 바랍니다.

  • cert.p12
  • key.p12
  • cert.pem
  • key.pem
  • key.unencrypted.pem
  • apns.pem

 

윗 단계를 하나의 이미지로 보면 다음과 같습니다.

 

 

총 6개의 파일이 생성되며 터미널 안에 보시면 타이핑한 내역이 그대로 있습니다.

 

이제 모든 것이 끝났습니다. 결국 apns.pem 이 파일 하나 만들고자 이짓을 했던 것 입니다.
푸시서버로 구성할 곳으로 apns.pem 파일을 업로드 해주시면 됩니다.
위치는 아무곳에나 하셔도 상관없지만 경로만 알면 다운로드가 가능한 웹루트는 피하시기 바랍니다.

이렇게 만들어진 인증서를 이용해 SSL Connect의 연결을 유지한 상태에서 Push메시지를 발송하면 됩니다.
개발에 관련된 부분은 다음 스텝에서 안내해드리겠습니다.

 

APNS관련 포스팅 보기

 

 

=================================

=================================

=================================

 

 

출처: http://qnibus.com/blog/how-to-develop-ios-application-delegate/

앱단 개발 개요

앱에서 해줘야 할 일은 앱을 실행한 디바이스의 정보를 3rt Party 서버와 통신해 DeviceToken과 UUID 그리고 사용자 정보를 던져주면 됩니다.
해당기기로 접근할 수 있는 키(열쇠)라고 생각하시면 되며, 이외의 모든 일은 iOS와 APNS서버가 알아서 해줍니다.

 

아이폰/아이패드 개발 코드

코드 작성은 크게 4가지 메서드에서 처리하게 됩니다.

  1. 원격 알림서비스를 iOS에 등록해야 합니다. 등록 성공/실패시 2,3번을 각각 실행합니다.
  2. 앱을 실행시 사용자가 알림서비스를 승인하면 서버로 디바이스 토큰을 보내야 합니다.
  3. 앱을 실행시 알림서비스 등록에 실패하는 경우 로직을 구현합니다.
  4. 메시지를 수신했을 때 처리하는 로직을 구현합니다.

이렇게 구현하면 기본적인 구현 절차는 끝나게 됩니다.
앱에 따라서 처리하는 방식이 달라지는 부분은 주로 2번과 4번이 될 것입니다.
예를 들면 메시지 보낼때 참고할 다른 정보를 보내거나, 메시지를 수신하면 앱내의 특정 부분으로 이동해서 해당 내용을 보여주려고 하실텐데 그런 부분들이라고 생각하시면 됩니다.

그럼 작성해야 할 개발코드를 알아보도록 하겠습니다.
위에서도 언급했다시피 EasyAPNS를 이용해 구현하는 것이니 참고하시기 바랍니다.

우선 (BOOL)application:didFinishLaunchingWithOptions: 메서드에서 원격 푸시 서비스를 하겠다고 등록을 해야 합니다.
이것을 작성함으로써 처음 앱 실행시 푸시알림 메시지를 받겠냐고 물어보는 Alert창이 나타나게 되며, 이 절차를 통해 iOS에게 이 앱이 원격 푸시 서비스를 등록하겠다고 알리는 것입니다. 이렇게만 해주시면 APNS서버에서 메시지를 수신하는 처리는 iOS가 알아서 처리해 앱으로 통보해줍니다.

 

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:screenBounds];
    self.window.autoresizesSubviews = YES;
    self.viewController = [[MainViewController alloc] init];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];


    // Add registration for remote notifications
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
    application.applicationIconBadgeNumber = 0;


    return YES;
}

 

사용자가 푸시를 받겠다고 승인하면 (void)application:didRegisterForRemoteNotificationsWithDeviceToken: 메서드가 실행됩니다.
이때, 우리는 일전에 만들었던 서버단으로 UUID와 deviceToken을 보내 정보를 보관하고 있어야 합니다.
deviceToken은 어떤 앱인지와 어떤 디바이스인지의 조합으로 구성되어진 64개의 바이트 조합입니다.
여기서 UUID와 Token은 사용자의 동의를 얻은 후 외부로 보내져야만 하며 이를 어길시 리젝사유가 될 수 있음을 참고하시기 바랍니다.
deviceToken정보는 EasyAPNS로 구현되어진 서버단의 특정 URL로  정보를 담아 전송해주기만 하면 EasyAPNS가 Device정보를 데이터베이스에 저장해줄 것 입니다.

 

 

Notification Format

여기서 DeviceToken만 저장해서 보내주면 되지~ 왜? 오만 잡다한 정보도 저장해야하는지 궁금하지 않으세요?
이는 해당 디바이스에서 어떤 설정을 갖고 있는지 알 수 있는 방법이 없어서 입니다. 현재 앱을 설치한 사용자가 배지설정을 켰는지 껐는지 사운드는 켰는지 껐는지를 보내는 쪽에서도 체크를 해야 합니다. 물론 애플에서 해당 디바이스의 정보를 가져올 수 있는 피드백이라는 기능을 제공하고 있긴 하지만 번거로운 작업인 것은 분명합니다. 만약 정보체크를 소홀히 하신다면 해당 사용자가 알림센터 기능을 끄더라도 메시지는 날아갑니다. 이점 반드시 참고하여주시고, 또하나 메서드를 살펴보면 NSData로 DeviceToken값을 넘겨주는데 이는 퍼포먼스때문에 컴퓨터가 좋아하는 바이너리 포맷으로 전달됩니다. 위의 그림에 다 나와있는 내용이니 참고하세요!
자 그럼, 아래와 같이 작성해서 처리를 완료해주시면 됩니다.

 

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    #if !TARGET_IPHONE_SIMULATOR


    NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
    NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];


    NSUInteger rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];


    NSString *pushBadge = @"disabled";
    NSString *pushAlert = @"disabled";
    NSString *pushSound = @"disabled";


    if (rntypes == UIRemoteNotificationTypeBadge) {
        pushBadge = @"enabled";
    }


    else if (rntypes == UIRemoteNotificationTypeAlert) {
        pushAlert = @"enabled";
    }


    else if (rntypes == UIRemoteNotificationTypeSound) {
        pushSound = @"enabled";
    }


    else if (rntypes == (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert)) {
        pushAlert = @"enabled";
        pushBadge = @"enabled";
    }


    else if (rntypes == (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)) {
        pushBadge = @"enabled";
        pushSound = @"enabled";
    }


    else if (rntypes == (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)) {
        pushAlert = @"enabled";
        pushSound = @"enabled";
    }


    else if (rntypes == (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)) {
        pushBadge = @"enabled";
        pushAlert = @"enabled";
        pushSound = @"enabled";
    }


    UIDevice *dev = [UIDevice currentDevice];
    NSString *deviceUuid = [self uniqueDeviceIdentifier];
    NSString *deviceName = dev.name;
    NSString *deviceModel = dev.model;
    NSString *deviceSystemVersion = dev.systemVersion;


    NSString *devToken = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];


    // Build URL String for Registration
    NSString *host = @"qnibus1.godo.co.kr";
    NSString *urlString = [@"/apns/apns.php?" stringByAppendingString:@"task=register"];
    urlString = [urlString stringByAppendingFormat:@"&appname=%@", appName];
    urlString = [urlString stringByAppendingFormat:@"&appversion=%@", appVersion];
    urlString = [urlString stringByAppendingFormat:@"&deviceuid=%@", deviceUuid];
    urlString = [urlString stringByAppendingFormat:@"&devicetoken=%@", devToken];
    urlString = [urlString stringByAppendingFormat:@"&devicename=%@", deviceName];
    urlString = [urlString stringByAppendingFormat:@"&devicemodel=%@", deviceModel];
    urlString = [urlString stringByAppendingFormat:@"&deviceversion=%@", deviceSystemVersion];
    urlString = [urlString stringByAppendingFormat:@"&pushbadge=%@", pushBadge];
    urlString = [urlString stringByAppendingFormat:@"&pushalert=%@", pushAlert];
    urlString = [urlString stringByAppendingFormat:@"&pushsound=%@", pushSound];


    // Register the Device Data
    NSURL *url = [[NSURL alloc] initWithScheme:@"http" host:host path:urlString];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    NSLog(@"Register URL: %@", url);
    NSLog(@"Return Data: %@", returnData);


    [request release];
    [url release];


    #endif
}

 

아래는 등록하다가 실패하는 경우 사용되는 딜리게이트입니다.
간혹 APNS등록해서 잘 사용하다가 Provisioning, Certification, AppID의 내용을 하나라도 수정하시는 경우 에러가 나타날 수 있습니다.
혹 안되실때 이런부분을 찾아보시기 바랍니다.

 

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    #if !TARGET_IPHONE_SIMULATOR


    NSLog(@"Error in registration. Error: %@", error);


    #endif
}

 

이제 여러분들이 가장 많이 손봐야할 딜리게이트 코드 입니다. 이곳으로 NSDictionary 형태로 메시지정보가 던져지게 됩니다.
그걸 받아서 원하는데로 처리하시면 되겠습니다.
아래는 기본적인 형태로 앱이 실행되지 않을때 경고창을 띄워 알려주는 역할하도록 따로 작성하고 그게 아닌 경우 로그만 찍히게 해봤습니다.
추후 저는 URL을 받아서 해당 URL로 강제이동되게 할 예정입니다.

 

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    #if !TARGET_IPHONE_SIMULATOR


    NSLog(@"Remote Notification: %@", [userInfo description]);
    NSDictionary *apsInfo = [userInfo objectForKey:@"aps"];


    if (application.applicationState == UIApplicationStateActive) {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Did receive a Remote Notification", nil)
                                                            message:[apsInfo objectForKey:@"alert"]
                                                           delegate:self
                                                  cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                                  otherButtonTitles:nil];
        [alertView show];
        [alertView release];
    } else {
        NSString *alert = [apsInfo objectForKey:@"alert"];
        NSLog(@"Received Push Alert: %@", alert);


        NSString *badge = [apsInfo objectForKey:@"badge"];
        NSLog(@"Received Push Badge: %@", badge);


        NSString *sound = [apsInfo objectForKey:@"sound"];
        NSLog(@"Received Push Sound: %@", sound);
        NSLog(@"userinfo: %@", userInfo);
    }
    application.applicationIconBadgeNumber = [[apsInfo objectForKey:@"badge"] integerValue];


    #endif
}

 

자! 지금까지 iOS의 Application Delegate상에 코드 작성하는 것을 보셨는데요~!

여기서는 EasyAPNS를 사용해 프로바이더(서버)를 구성할 계획이므로 이와 같이 참고하시면 되겠습니다.
서버로 던져주는 인자값들은 EasyAPNS에서 정해진 그대로를 사용한 것입니다.
참고적으로 꼭 정해진 인자값만 보내실 필요는 절대 없다는 점 알아두시기 바라며, 가장 중요한 것은 deviceToken값만 있으면 전송에는 문제가 없습니다.

하지만 하이브리드로 작업하시는 경우 Device의 Unique한 값을 찾느라 고민하실 수 있습니다.
이는 키체인을 이용해서 DEVICE UID를 저장해서 쓰시기 바랍니다.
앱을 삭제했다 다시 설치해도 해당 UID값을 계속해서 쓸 수 있습니다.

참고로 안드로이드는 시리얼을 이용하시면 되겠습니다.

다음은 EasyAPNS을 이용해 프로바이더를 구성하는 법을 소개해드리겠습니다.

 

 

=================================

=================================

=================================

 

 

 

출처: http://qnibus.com/blog/how-to-develop-service-provider/

 

 

프로바이더에서의 개발 개요

두번째 시리즈인 프로바이더에서 개발 방법에 대해서 알아보겠습니다.
이미 인증서 만들기에서 제작한 apns.pem 인증서 파일을 FTP 프로그램을 이용해 임의의 경로로 업로드 합니다.
보통 웹루트내에 파일을 올리면 주소나 파일명만 알면 바로 다운로드 받아갈 수 있기때문에 가급적 웹루트가 아닌 곳에 올려주시기 바랍니다.

이 인증서로 APNS 서버와의 통신을 통해 인증을 받고 인증을 받은 상태에서 Payload에 애플이 요구하는 텍스트를 채워 json데이터를 APNS서버로 전송해주기만 하면 됩니다. 이런 부분을 손쉽게 접근할 수 있도록 도와주는 친구들을 아래에서 몇 명 소개시켜드리겠습니다.

그래도 어떤 원리인지는 간단하게 알고 넘어가야겠죠?

 

Payload 작성 양식

다음의 애플문서를 보면 아주아주 자세하게 나와있습니다. 영어가 되시는 분은 애플에서 제공하는 원문을 보시는게 더 좋을 것 같네요!
기본적으로 Payload는 JSON 형식으로 전달되며 형식은 다음과 같습니다.

 

{
    "aps" : { "alert" : {
            "body" : "내용을 적어넣으세요",
            "action-loc-key" : "PLAY",
            "loc-key" : "CONTENT",
            "loc-args" : [ "Jenna", "Frank" ]
        },
    "badge" : 5,
    },
    "acme1" : "bar",
    "acme2" : [ "bang", "whiz" ]
}

여기서 aps 키값은 Dictionary 형태로 만들어지며 각각의 키들은 다음과 같은 역할을 합니다.

  • alert : 스트링 혹은 딕셔너리 형태로 값이 들어갈 수 있으며 스트링으로 할 경우 보내고자하는 메시지를 적어 넣어주시면 됩니다.
    • body : 전달할 내용을 입력합니다.
    • action-loc-key : LocalizedString의 키값을 참조해 AlertView의 View Detail이라는 버튼을 언어에 맞게 변경시켜줍니다.
    • loc-key : LocalizedString의 키값을 참조해 내용을 채워줍니다.
  • badge : 배지의 숫자를 입력합니다.
  • acme1 or acme2 : 커스터마이징 변수값으로 임의의 문자열로 키와 값을 만들어 스트링 혹은 배열의 형태로 전달 할 수 있습니다.

PHP 테스트 코드:

아래는 EasyAPNS가 아닌 막코딩으로 전송테스트를 해보았습니다.
상단에 deviceToken정보만 제대로 입력해주면 해당 디바이스로 메시지가 잘 전송되는 것을 확인했습니다.

 

$deviceToken = 'ca1b359cef4878ccb79b2bb411f026e54880e4a219962bfc3d0d89faeb603904'; // 앱실행후 로그로 찍힌 디바이스 토큰 정보 입력
$message = '토큰값만 있어도 전송이 잘되나요?';
$badge = 1;
$sound = '';


// Payload 작성 (JSON)
$body = array();
$body['aps'] = array('alert' =&gt; $message);
if ($badge)
    $body['aps']['badge'] = $badge;
if ($sound)
    $body['aps']['sound'] = $sound;


// 애플 APNS서버와 인증서를 이용해 소켓 통신
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '/usr/local/apns/apns-dev.pem');
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);


// 실제 배포시 ssl://gateway.push.apple.com:2195 로 변경해야함
if (!$fp) {
    print "Failed to connect $err $errstr\n";
    return;
}
else {
    print "Connection OK\n";
}


// 배열을 json으로 변경
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
print "Sending message :" . $payload . "\n";
fwrite($fp, $msg);
fclose($fp);

 

개발에 사용할 오픈소스

위와 같이 Github 및 검색을 해보시면 아시겠지만 다양한 개발자들이 만들어 놓은 오픈소스가 많이 있습니다.
가장 최근에 등록된 PHP_APN의 경우 API를 기능별로 세분화시켜놓아 관리기능을 별도로 제작할때 좋지 않을까 싶습니다.
하지만 저는 PHP로 개발코자 EasyAPNS를 이용해서 개발을 시작하다가 일이 커져 지금은

별도로 개발해 안드로이드까지 통합발송이 되도록 만들어 사용하고 있습니다.
GCM(Google Cloud Message)에 대한 정보는 다음에 시간날때 게재하도록 하겠습니다.

 

관련 참고 자료:

Why was my device token rejected by Apple?

Local and Push Notification Programming Guide

How to build an Apple Push Notification provider server (tutorial)

 

 

 

=================================

=================================

=================================

 

 

출처: http://theeye.pe.kr/archives/1168

 

 

 


오늘은 애플사에서 제공하는 Apple Push Notification Service 줄여서 APNS를 사용해서 나의 어플리케이션이 메시지를 푸싱하는것에 대해 간단하게 정리해 보겠습니다. 애플에서 제공하는 푸시의 경우 위의 프리젠테이션에서 볼 수 있듯이 써드파티의 서버에서 APNS서버로 메시지를 보내면 APNS에서 해당 디바이스에 직접적으로 메시지를 전달해주는 일을 합니다. 실제로 테스트 해본 결과 단 몇초안에 메시지가 전달이 되더군요. 와우!

1. 인증서 준비

 


위의 화면은 개발자 사이트의 Provisioning Portal – App IDs 에 들어가면 볼 수 있는 화면입니다. 두번째 부분이 Push Notification에 대한 설정인데요. 첫번째 App ID의 경우에는 현재 APNS설정을 할 수 있는 상태입니다. 그리고 두번쨰는 아예 설정이 불가능한 경우입니다. 이 경우는 App ID의 Identifier에 *가 들어간 경우에 해당됩니다. 꼭 어플과 App ID가 1:1매치가 되어야 하겠죠. 3번째는 예상하시다시피 이미 설정이 끝난경우 입니다.

 


Configure를 눌러 들어간 화면에는 위와같은 화면이 나옵니다. Enable을 눌러주시면 아래의 Configure 버튼이 활성화가 됩니다. 보시면 대충 Push를 보내기 위한 관련 인증서를 발급 받는 부분이라는것을 아실수 있으실텐데요 두가지가 있네요. 한가지는 개발용 하나는 실제 서비스용입니다. 우선은 시범적으로 개발용만 발급받아보겠습니다. 실제 서비스때는 밑의 것을 발급받아 사용하시면 됩니다.

 


위와 같은 화면이 나옵니다. 아마 대부분의 개발자분들이 이 화면을 보시면 어떤것이 필요한 것인지 감이 오실것이라 생각되네요. 잘 모르시겠다면 [Apple Program 이용가이드]의 인증서 등록 부분을 참고하시면 될것 같네요. 화면이 요즘과 좀 많이 달라지긴 했지만 참고하시는데에는 무리가 없을것입니다. 저때에 사용하셨던 CSR파일을 그대로 이용하시면 됩니다.

 


잠시후에 자동으로 인증서가 발급됩니다. 위와같이 화면이 뜨게 됩니다.

 


이제 인증서를 다운받을 수 있게 되는데요, 위에 보이는 Download버튼을 눌러 인증서를 다운 받습니다. 그리고 더블클릭하여 시스템의 인증서로 등록하여 줍니다.

aps_developer_identity.cer – 개발용 인증서
aps_production_identity.cer – 실서비스용 인증서

맥의 유틸리티 – 인증서 메뉴에 가보면 위와 같이 Push Service에 대한 인증서가 등록된것을 확인하실 수 있습니다. 이제 해당 인증서와 아래에 선택한것과 같이 CSR 발급에 사용되었던 개인키(Private Key)를 동시에 선택을 한후에 마우스 오른쪽 버튼을 눌러 “외부로 보내기”(기억이 확실치 않네요; 아무튼 Export입니다)를 하여 줍니다. p12라는 확장자의 파일로 저장이 가능합니다. 이때에 암호를 정할 수 있는데 적절한 암호를 입력하여 줍니다.

2. 아이폰 어플리케이션 수정

이제 아이폰 어플리케이션에 Push Service를 등록할 수 있도록 몇가지 추가를 해야 합니다. -AppDelegate.m 파일에 다음의 내용을 추가합니다.

 

- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSMutableString *deviceId = [NSMutableString string];
const unsigned char* ptr = (const unsigned char*) [deviceToken bytes];


for(int i = 0 ; i < 32 ; i++)
{
  [deviceId appendFormat:@"%02x", ptr[i]];
}


NSLog(@"APNS Device Token: %@", deviceId);
}

위의 추가된 코드는 어플리케이션이 최초 실행될때에 어플리케이션이 푸시서비스를 이용함을 알리고 허용할지를 물어보게 하고 사용자의 동의를 얻었을 경우 실행됩니다. APNS에 디바이스 정보를 등록하고 64바이트의 문자열을 받아오게 됩니다. 마지막에 NSLog로 확인을 하게 되는 deviceId를 써드파티 서비스의 서버로 전송하여 관리하시면 됩니다.

 

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSString *string = [NSString stringWithFormat:@"%@", userInfo];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                             message:string delegate:nil
                             cancelButtonTitle:@"OK"
                             otherButtonTitles:nil];
[alert show];
[alert release];
}

위의 새로 추가된 메서드는 어플리케이션이 푸시를 받았을 경우 호출되는 메서드입니다. 그냥 받은 메시지를 그대로 찍도록 해보았습니다. 넘어오는 데이터에 대해서는 직접 한번 디버깅하여 보시길 추천해드립니다.

 

 

 

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *userInfo = [launchOptions objectForKey:
              UIApplicationLaunchOptionsRemoteNotificationKey];


if(userInfo != nil)
{
  [self application:application didFinishLaunchingWithOptions:userInfo];
}


// APNS에 디바이스를 등록한다.
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
  UIRemoteNotificationTypeAlert|
  UIRemoteNotificationTypeBadge|
  UIRemoteNotificationTypeSound];


return YES;
}

이제 하이라이트입니다. 위의 메서드는 어플리케이션이 실행될때에 호출이 됩니다. 요즘은 URI 스키마 혹은 푸시 서비스를 통해 어플리케이션을 실행할 수 있게 되었고 관련 옵션들이 launchOptions인자로 넘어오게 됩니다.

위의 경우에는 어플리케이션이 실행되어있지 않은 경우 푸시를 받아서 어플리케이션이 실행될때 관련 정보가 launchOptions에 담겨오게 되며 그 정보를 가지고 didFinishLaunchingWithOptions를 재호출하는 로직입니다. 그 뒤에 보면 나와있는 APNS등록 부분이 사용자에게 푸시를 허용할지 물어보는 부분이 되겠습니다.

이때에 알아두실 점은 위의 메서드가 구현된 시점부터 원래 사용되던 다음의 메서드는 사용되지 않습니다.

 

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// didFinishLaunchingWithOptions를 구현할 경우 사용되지 않는다.
}

3. 푸시 서버 준비

이 블로깅에서는 푸시 서버를 Java기반에서 개발하는것으로 설명합니다. 검색해 보면 PHP, Ruby, Python등 매우 다양한 언어로 이미 포팅되어 잘 만들어진 라이브러리도 많이 있더군요. 저도 역시나 잘 만들어진(실제로 여러 포럼에서 추천받는) javapns를 사용하도록 하겠습니다.

필요한 JAR 파일은 다음과 같습니다.

javapns
log4j
bcprov-ext
commons-io
commons-lang

 

이제 해당 홈페이지에 나와있는 간단한 예제를 실행해 보도록 하겠습니다. 다음 소스에서 사용되는iPhoneId의 경우 didRegisterForRemoteNotificationsWithDeviceToken 메서드에서 언급되었던 deviceId값이며 certificate의 경우 인증서 준비단계에서 만들었던 p12확장자 파일을 뜻합니다.passwd는 p12를 만들당시에 물어보았던 비밀번호입니다.

 

public class Push {
     // APNs Server Host & port
     private static final String HOST = "gateway.sandbox.push.apple.com";
     private static final int PORT = 2195;


     // Badge
     private static final int BADGE = 66;


     // iPhone's UDID (64-char device token)
     private static String iPhoneId = "2ed202ac08ea9...cf8d55910df290567037dcc4";
     private static String certificate = "/absolute/path/to/my/certificate";
     private static String passwd = "mycertificatepassword";


     public static void main( String[] args ) throws Exception {


        System.out.println( "Setting up Push notification" );


        try {
             // Setup up a simple message
             PayLoad aPayload = new PayLoad();
             aPayload.addBadge( BADGE );
             aPayload.addAlert("Hello World!");
             aPayload.addSound("default");
             System.out.println( "Payload setup successfull." );


             System.out.println ( aPayload );


             // Get PushNotification Instance
             PushNotificationManager pushManager =
                PushNotificationManager.getInstance();


             // Link iPhone's UDID (64-char device token) to a stringName
             pushManager.addDevice("iPhone", iPhoneId);
             System.out.println( "iPhone UDID taken." );


             System.out.println( "Token: " +
                pushManager.getDevice( "iPhone" ).getToken() );


             // Get iPhone client
             Device client = pushManager.getDevice( "iPhone" );
             System.out.println( "Client setup successfull." );


             // Initialize connection
             pushManager.initializeConnection( HOST, PORT, certificate, passwd,
                SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);
             System.out.println( "Connection initialized..." );


             // Send message
             pushManager.sendNotification( client, aPayload );
             System.out.println( "Message sent!" );


             System.out.println( "# of attempts: " +
                pushManager.getRetryAttempts() );
             pushManager.stopConnection();


             System.out.println( "done" );


         } catch (Exception e) {
             e.printStackTrace();
         }
     }
}

예제코드에 이어 바로 결과 화면을 붙여보았습니다. 위와같이 메시지를 전송하면 잠자고 있던 아이팟이 알람을 울리게 됩니다.

여기서 한가지 더 알아두셔야 할 점으로 Feedback이라는 것이 있습니다. 위와 같은 Push의 경우에는 해당 사용자에게 제대로 메시지가 전달 되었는지 여부를 확인할 길이 없습니다. 만약에 10만명의 푸시 사용자가 가입을 했고 5만명이 해당 어플의 푸시를 받지 않겠다고 설정을 바꾸었거나 삭제를 했다면 서비스는 일일이 받지 않는 사용자에게 푸시를 날리느라 시간을 낭비하게 되겠죠. 그래서 이 Feedback을 이용하여 사용하지 않는 사용자의 리스트를 받아오게 됩니다.

 

public class Feedback {
     // APNs Server Host & port
     private static final String HOST = "feedback.push.apple.com";
     private static final int PORT = 2196;


     private static String certificate = "/absolute/path/to/certificate";
     private static String passwd = "certificatePassword";


     public static void main( String[] args ) throws Exception {


        try {
             // Get FeedbackServiceManager Instance
             FeedbackServiceManager feedbackManager =
                FeedbackServiceManager.getInstance();


             // Initialize connection
             LinkedList<Device> devices = feedbackManager.getDevices( HOST, PORT,
                certificate, passwd, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12 );
             System.out.println( "Connection initialized..." );


             System.out.println( "Devices returned: " + devices.size() );


             ListIterator<Device> itr = devices.listIterator();
             while ( itr.hasNext() ) {
                 Device device = itr.next();
                 System.out.println( "Device: id=[" + device.getId() +
                " token=[" + device.getToken() + "]" );
             }


             System.out.println( "done" );


         } catch (Exception e) {
             e.printStackTrace();
         }
     }
}

테스트 몇번 해보았을 뿐이라 사용하지 않는 사용자는 존재하지 않는군요. 위와같은 명령을 하루 한번정도의 텀으로 주기적으로 실행하여 발송 목록에서 제거하는 등의 처리를 하시면 되겠습니다. 더 많은 관련 문서는 javapns의 WIKI 페이지에 잘 나와있습니다^^

 

 

=================================

=================================

=================================

 

 

출처:http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1

 

Update 4/12/2013: Fully updated for iOS 6 (original post by Matthijs Hollemans, update by Ali Hafizji).

In iOS, apps can’t do a lot in the background. Apps are only allowed to do limited set of activities so battery life is conserved.

But what if something interesting happens and you wish to let the user know about this, even if they’re not currently using your app?

For example, maybe the user received a new tweet, their favorite team won the game, or their dinner is ready. Since the app isn’t currently running, it cannot check for these events.

Luckily, Apple has provided a solution to this. Instead of your app continuously checking for events or doing work in the background, you can write a server-side component to do this instead.

And when an event of interest occurs, the server-side component can send the app a push notification! There are three things a push notification can do:

  • Display a short text message
  • Play a brief sound
  • Set a number in a badge on the app’s icon

You can combine these however you see fit; for example, play a sound and set the badge but not display a message.

In this 2-part tutorial series, you’ll get to try this out for yourself by making a simple app that uses APNS (Apple push notification service)!

In this first part, you’ll learn how to configure your app to receive push notifications and receive a test message.

This tutorial is for intermediate or advanced iOS developers. If you are still a beginner to iOS, you should check out some of the other tutorials on this site first. Also, it’s highly recommended that you review these two tutorials first (or have equivalent knowledge):

Without further ado, let’s push through this!

 

Getting Started: Brief Overview

Getting push to work for your app takes quite a bit of effort. This is a puzzle with many pieces. Here is an overview:

 

 

  1. An app enables push notifications. The user has to confirm that he wishes to receive these notifications.
  2. The app receives a “device token”. You can think of the device token as the address that push notifications will be sent to.
  3. The app sends the device token to your server.
  4. When something of interest to your app happens, the server sends a push notification to the Apple Push Notification Service, or APNS for short.
  5. APNS sends the push notification to the user’s device.

When the user’s device receives the push notification, it shows an alert, plays a sound and/or updates the app’s icon. The user can launch the app from the alert. The app is given the contents of the push notification and can handle it as it sees fit.

Are push notifications still worth it now that we have local notifications and multitasking? You bet!

Local notifications are limited to scheduling timed events and unlimited background processing is only available to apps that do VOIP, navigation or background audio. If you want to notify the users of your app about external events while the app is closed, you still need push notifications.

In this tutorial, I will explain in detail how the push notification system works and how to build push into your own app. There is a lot to explain, so take your time to let it all sink in.

What You Need for Push Notifications

To add push notifications to your app, you need:

An iPhone or iPad. Push notifications do not work in the simulator, so you will need to test on the device.

An iOS Developer Program membership. You need to make a new App ID and provisioning profile for each app that uses push, as well as an SSL certificate for the server. You do this at the iOS Provisioning Portal.

If you want to follow along with the examples in this tutorial, you will need to create your own provisioning profile and SSL certificate; you cannot use mine. Because it’s important to get the certificate right, I’ll explain the detailed steps on how to obtain one.

A server that is connected to the internet. Push notifications are always sent by a server. For development you can use your Mac as the server (which you’ll do in this tutorial) but for production use, you need at least something like a VPS (Virtual Private Server).

A cheap shared hosting account is not good enough. You need to be able to run a background process on the server, install an SSL certificate, and be able to make outgoing TLS connections on certain ports.

Most shared hosting providers do not let you do this, although they might if you ask. However, I really recommend using a VPS host such as Linode.

Anatomy of a Push Notification

Your server is responsible for creating the push notification messages, so it’s useful to know what they look like.

A push notification is a short message that consists of the device token, a payload, and a few other bits and bytes. The payload is what you are interested in, as that contains the actual data you will be sending around.

Your server should provide the payload as a JSON dictionary. The payload for a simple push message looks like this:

{
    "aps": {
        "alert": "Hello, world!",
        "sound": "default"
    }
}

For the JSON uninitiated, a block delimited by curly { } brackets contains a dictionary that consists of key/value pairs (just like an NSDictionary).

The payload is a dictionary that contains at least one item, “aps”, which itself is also a dictionary. In our example, “aps” contains the fields “alert” and “sound”. When this push notification is received, it shows an alert view with the text “Hello, world!” and plays the standard sound effect.

There are other items you can add to the “aps” section to configure the notification. For example:

{
    "aps": {
        "alert": {
            "action-loc-key": "Open",
            "body": "Hello, world!"
        },
        "badge": 2
    }
}

 

Now “alert” is a dictionary of its own. The “action-loc-key” provides an alternative text for the “View” button. The “badge” field contains the number that will be shown on the application icon. This notification will not play a sound.

There are many more ways to configure the JSON payload. You can change the sound that is played, you can provide localized text, and you can add fields of your own. For more information, check out the official Local and Push Notification Programming Guide.

Push notifications are intended to be small; the payload size can be no more than 256 bytes. That leaves you about as much room as fits in an SMS message or a tweet. A smart push server won’t waste space on newlines and whitespace and generates something that looks like:

{
    "aps": {
        "alert": "Hello, world!",
        "sound": "default"
    }
}

 

It’s less easy to read for us humans, but it saves enough bytes to make it worth it. Push notifications whose payload exceeds 256 bytes will not be accepted by APNS.

Push Notification Gotchas

 

 

Push Notifications Are Unreliable!

They are not reliable! There is no guarantee that push notifications will actually be delivered, even if the APNS server accepted them.

As far as your server is concerned, push notifications are fire-and-forget; there is no way to find out what the status of a notification is after you’ve sent it to APNS. The delivery time may also vary, from seconds up to half an hour.

Also, the user’s iPhone may not be able to receive push notifications all the time. They could be on a WiFi network that does not allow connections to be made to APNS because the required ports are blocked. Or the phone could be turned off.

APNS will try to deliver the last notification it received for that device when it comes back online, but it will only try for a limited time. Once it times out, the push notification will be lost forever!

 

 

After looking at the APNS Server Bill

They can be expensive! Adding push functionality to your app is fairly easy and inexpensive if you own the data, but can be expensive if you have a lot of users or data you need to poll.

For example, it’s no problem if you want to notify your users when the contents of your own RSS feed change. Because you control that RSS feed and know when it changes — when you update the content on your web site — your server can send out the notifications at the right moment.

But what if your app is an RSS feed reader that allows users to put in their own URLs? In that case you need to come up with some mechanism to detect updates to those feeds.

In practice this means your server will need to continuously poll those feeds for changes. If you have a lot of users, you may have to install a bunch of new servers to handle all that processing and bandwidth. For apps such as these, push can become quite expensive and may not be worth it.

OK, enough theory. It’s time to learn how to do this push thing. Before we can get to the good stuff – programming! – there is some boring set-up work to be done on the iOS Provisioning Portal, so let’s get that over with as quickly as possible.

Provisioning Profiles and Certificates, Oh My!

 

 

APNS needs a certificate!

To enable push notifications in your app, it needs to be signed with a provisioning profile that is configured for push. In addition, your server needs to sign its communications to APNS with an SSL certificate.

The provisioning profile and SSL certificate are closely tied together and are only valid for a single App ID. This is a protection that ensures only your server can send push notifications to instances of your app, and no one else.

As you know, apps use different provisioning profiles for development and distribution. There are also two types of push server certificates:

  • Development. If your app is running in Debug mode and is signed with the Development provisioning profile (Code Signing Identity is “iPhone Developer”), then your server must be using the Development certificate.
  • Production. Apps that are distributed as Ad Hoc or on the App Store (when Code Signing Identify is “iPhone Distribution”) must talk to a server that uses the Production certificate. If there is a mismatch between these, push notifications cannot be delivered to your app.

In this tutorial, you won’t bother with the distribution profiles and certificates and just use the ones for development.

Generating the Certificate Signing Request (CSR)

Remember how you had to go to the iOS Provisioning Portal and make a Development Certificate after you signed up for the iOS Developer Program? If so, then these next steps should be familiar. Still, I advise you to follow them exactly. Most of the problems people have with getting push notifications to work are due to problems with the certificates.

Digital certificates are based on public-private key cryptography. You don’t need to know anything about cryptography to use certificates, but you do need to be aware that a certificate always works in combination with a private key.

The certificate is the public part of this key pair. It is safe to give it to others, which is exactly what happens when you communicate over SSL. The private key, however, should be kept… private. It’s a secret. Your private key is nobody’s business but your own. It’s important to know that you can’t use the certificate if you don’t have the private key.

Whenever you apply for a digital certificate, you need to provide a Certificate Signing Request, or CSR for short. When you create the CSR, a new private key is made that is put into your keychain. You then send the CSR to a certificate authority (in this case that is the iOS Developer Portal), which will generate the SSL certificate for you based on the information in the CSR.

Open Keychain Access on your Mac (it is in Applications/Utilities) and choose the menu option Request a Certificate from a Certificate Authority….

 

 

If you do not have this menu option or it says “Request a Certificate from a Certificate Authority with key”, then download and install the WWDR Intermediate Certificate first. Also make sure no private key is selected in the main Keychain Access window.

You should now see the following window:

 

 

Enter your email address here. I’ve heard people recommended you use the same email address that you used to sign up for the iOS Developer Program, but it seems to accept any email address just fine.

Enter “PushChat” for Common Name. You can type anything you want here, but choose something descriptive. This allows us to easily find the private key later.

Check Saved to disk and click Continue. Save the file as “PushChat.certSigningRequest”.

If you go to the Keys section of Keychain Access, you will see that a new private key has appeared in your keychain. Right click it and choose Export.

 

 

Save the private key as PushChatKey.p12 and enter a passphrase.

For the convenience of this tutorial, I used the passphrase “pushchat” to protect the p12 file but you should really choose something that is less easy to guess. The private key needs to be a secret, remember? Do choose a passphrase that you can recall, or you won’t be able to use the private key later.

Making the App ID and SSL Certificate

Log in to the iOS Dev Center and “Select the Certificates, Identifiers and Profiles” from the right panel.

 

 

You will be presented with the following screen (Doesn’t the new dev center UI look sleek :))

 

 

Since you’re making an iOS app select Certificates in the iOS Apps section.

Now, you are going to make a new App ID. Each push app needs its own unique ID because push notifications are sent to a specific application. (You cannot use a wildcard ID.)

Go to App IDs in the sidebar and click the + button.

 

 

Fill the following details:

  • App ID Description: PushChat
  • App Services Check the Push Notifications Checkbox
  • Explicit App ID: com.hollance.PushChat

It is probably best if you choose your own Bundle Identifier here – com.yoursite.PushChat – instead of using mine. You will need to set this same bundle ID in your Xcode project. After you’re done filling all the details press the Continue button. You will be asked to verify the details of the app id, if everything seems okay click Submit

Hurray! You have successfully registered a new App ID.

 


In a few moments, you will generate the SSL certificate that your push server uses to make a secure connection to APNS. This certificate is linked with your App ID. Your server can only send push notifications to that particular app, not to any other apps.

After you have made the App ID, it shows up like this in the list:

 

 

Select the PushChat app ID from the list. This will open up an accordion as shown below:

 

 

Notice in the “Push Notification” row, there are two orange lights that say “Configurable” in the Development and Distribution column. This means your App ID can be used with push, but you still need to set this up. Click on theSetting button to configure these settings.

 

 

Scroll down to the Push Notifications section and select the Create Certificate button in the Development SSL Certificate section.

 

 

The “Add iOS Certificate” wizard comes up:

 

 

The first thing it asks you is to generate a Certificate Signing Request. You already did that, so click Continue. In the next step you upload the CSR. Choose the CSR file that you generated earlier and click Generate.

 

 

It takes a few seconds to generate the SSL certificate. Click Continue when it’s done.

Now click Download to get the certificate – it is named “aps_development.cer”.

 

 

As you can see, you have a valid certificate and push is now available for development. You can download the certificate again here if necessary. The development certificate is only valid for 3 months.

When you are ready to release your app, repeat this process for the production certificate. The steps are the same.

Note: The production certificate remains valid for a year, but you want to renew it before the year is over to ensure there is no downtime for your app.

You don’t have to add the certificate to your Keychain, although you could if you wanted to by double-clicking the downloaded aps_development.cer file. If you do, you’ll see that it is now associated with the private key.

Making a PEM File

So now you have three files:

  • The CSR
  • The private key as a p12 file (PushChatKey.p12)
  • The SSL certificate, aps_development.cer

Store these three files in a safe place. You could throw away the CSR but in my opinion it is easier to keep it. When your certificate expires, you can use the same CSR to generate a new one. If you were to generate a new CSR, you would also get a new private key. By re-using the CSR you can keep using your existing private key and only the .cer file will change.

You have to convert the certificate and private key into a format that is more usable. Because the push part of our server will be written in PHP, you will combine the certificate and the private key into a single file that uses the PEM format.

The specifics of what PEM is doesn’t really matter (in fact, I have no idea) but it makes it easier for PHP to use the certificate. If you write your push server in another language, these following steps may not apply to you.

You’re going to use the command-line OpenSSL tools for this. Open a Terminal and execute the following steps.

Go to the folder where you downloaded the files, in my case the Desktop:

$ cd ~/Desktop/

Convert the .cer file into a .pem file:

$ openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem

Convert the private key’s .p12 file into a .pem file:

$ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12 Enter Import Password: MAC verified OK Enter PEM pass phrase: Verifying - Enter PEM pass phrase:

You first need to enter the passphrase for the .p12 file so that openssl can read it. Then you need to enter a new passphrase that will be used to encrypt the PEM file. Again for this tutorial I used “pushchat” as the PEM passphrase. You should choose something more secure.

Note: if you don’t enter a PEM passphrase, openssl will not give an error message but the generated .pem file will not have the private key in it.

Finally, combine the certificate and key into a single .pem file:

$ cat PushChatCert.pem PushChatKey.pem > ck.pem

At this point it’s a good idea to test whether the certificate works. Execute the following command:

$ telnet gateway.sandbox.push.apple.com 2195 Trying 17.172.232.226... Connected to gateway.sandbox.push-apple.com.akadns.net. Escape character is '^]'.

This tries to make a regular, unencrypted, connection to the APNS server. If you see the above response, then your Mac can reach APNS. Press Ctrl+C to close the connection. If you get an error message, then make sure your firewall allows outgoing connections on port 2195.

Let’s try connecting again, this time using our SSL certificate and private key to set up a secure connection:

$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem Enter pass phrase for PushChatKey.pem:

You should see a whole bunch of output, which is openssl letting you know what is going on under the hood.

If the connection is successful, you should be able to type a few characters. When you press enter, the server should disconnect. If there was a problem establishing the connection, openssl will give you an error message but you may have to scroll up through the output to find it.

Note: There are two different APNS servers: the “sandbox” server that you can use for testing, and the live server that you use in production mode. Above, we used the sandbox server because our certificate is intended for development, not production use.

Making the Provisioning Profile

You’re not yet done with the iOS Dev Center. Click the Provisioning Profiles button in the sidebar and click the+ button.

 

 

This will open up the iOS provisioning profile wizard.

Step 1: Select Type

Select the “iOS App development” option button in the first step of the wizard and press Continue.

 

 

Step 2: Configure

Select the PushChat app id that you created in the previous section. This will ensure that this provisioning profile is explicitly tied to the PushChat app.

 

 

Step 3: Generate

In this step you select the certificates you want to include in this provisioning profile. This step should be quite routine by now.

 

 

Step 4: Select devices

Select the devices you want to include in this provisioning profile. Since you’re creating the development profile you would typically select the devices you use for development here.

 

 

Step 5: Name this profile

Set the provisioning profile name as “PushChat Development” as shown below.

 

 

You’re almost done! Finally press the Download button, this will download the newly created Development provisioning profile.

Add the provisioning profile to Xcode by double-clicking it or dragging it onto the Xcode icon.

If you’re ready to release your app to the public, you will have to repeat this process to make an Ad Hoc or App Store distribution profile.

A Very Basic App

So far things haven’t been really exciting, but those preliminaries are necessary. I wanted to show you in detail how to generate the certificate because it’s not something you do every day and push won’t work without it.

You’ve already established that your certificate is valid by connecting to the sandbox server. Let’s test if you can actually send some push notifications!

Fire up Xcode and choose File, New Project. In the assistant, pick Single View Application and continue to the next step.

 

 

I filled in these fields as follows:

  • Product Name: PushChat
  • Organization Name: Ray Wenderlich
  • Company Identifier: com.hollance
  • Device Family: iPhone

The Product Name and Company Identifier together form the Bundle ID. In my case that is “com.hollance.PushChat”. You should choose a Product Name and Company Identifier that correspond to the App ID that you made earlier in the Provisioning Portal (com.yourname.PushChat). Make sure that the “Use Storyboards” and “Use Automatic reference counting” checkboxes are checked.

Finish the assistant and open AppDelegate.m. Change the application:didFinishLaunchingWithOptions: method to look like this:

 

The new call to registerForRemoteNotificationTypes: tells the OS that this app wants to receive push notifications.

Build & Run the app. You need to do this on your device because the simulator does not support push notifications. Xcode should automatically have selected the new provisioning profile. If you get a code sign error, then make sure the proper profile is selected in the Code Signing build settings.

When the app starts and registers for push notifications, it shows a message to inform the user that this app wishes to send push notifications.

 

 

The app asks this only once. If the user picks “OK”, then we should be all set. However, if they choose “Don’t Allow” then our app will never receive push notifications. The user can reverse their decision in the phone’s Settings.

 

 

The name of your app will be added to the phone’s Settings, under Notifications. The user can enable or disable the notifications for your app here, including individual settings for badges, sounds, and alerts.

 

 

Your app can find out which types of push notifications are enabled through:

 

There is one more thing you need to add in order to be able to receive push notifications. Add the following toAppDelegate.m:

When your app registers for remote (push) notifications, it tries to obtain a “device token”. This is a 32-byte number that uniquely identifies your device. Think of the device token as the address that a push notification will be delivered to.

 

Run the app again and you should see something like this in Xcode’s console window:

My token is: <740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad>

The token is an opaque binary data structure, stuffed into an NSData object. Apple doesn’t want you to mess around with its internals. For our purposes it is enough to know that it is currently 32 bytes long. As you can see above, the token can be represented by 64 hexadecimal characters. You will be using it in that format, although you still strip off the brackets and leave out the spaces.

If you run the app in the simulator, the application:didFailToRegisterForRemoteNotificationsWithError: method will be called as push notifications are not supported in the simulator.

That’s it for the app. There is one more thing to do and then you can finally see some push notifications in action!

Sending Your First Push Notification

As I’ve mentioned a few times before, you need to set up a server that sends the push notifications to your app. For this first test, you’re not going to set up a server just yet. Instead, I’ll give you a very simple PHP script that sets up a connection to APNS and sends a push notification to a device token that you specify. You can run this straight from your Mac.

Download the SimplePush code and unzip it. You need to make some changes to simplepush.php.

 

You should copy the device token from the app into the $deviceToken variable. Be sure to leave out the spaces and brackets; it should just be 64 hexadecimal characters. Put your private key’s passphrase into $passphrase, and the text you wish to send in $message.

Copy your ck.pem file into the SimplePush folder. Remember, the ck.pem file contains both your certificate and the private key.

Then open a Terminal and type:

$ php simplepush.php

If all goes well, the script should say:

Connected to APNS Message successfully delivered

And within a few seconds you should receive your first push notification:

 

 

Note that you won’t see anything when the app is open. The push message is delivered but you did not build anything in the app to handle it yet. Close the app and try again.

If the simplepush.php script exits with an error message, then check that you have made the PEM file correctly, and that you can connect to the sandbox server without problems (see above).

For now, it is not important what the script exactly does. I will explain more about this in the second part of the series when we build a true push server.

Troubleshooting

This section has a few tips to keep track of if you’re facing problems to get push notifications to work.

Some notifications received but not all: If you’re sending multiple push notifications simultaneously and you receive only a few, fear not that is intended behaviour. APNS maintains a QoS (Quality of Service) queue for each device with a push app. The size of this queue is 1 so if you send multiple notifications then the last notification is overridden.

Problem connecting to Push Notification Service:

  1. One possibility here could be that there is a firewall blocking the ports used by APNs. Make sure youunblock these ports.
  2. Another possibility here could be that you’ve gotten the private key and CSR file wrong. While updating this tutorial I myself made this mistake and fixed it using the correct private key. Remember that each app id has a unique CSR and private key combination.
  3. Unable to get local issuer certificate. This error means that the certificate from the server could not be verified. To fix this you need to download the Entrust CA root certificate. This can be done from the Terminal using the command:curl -O https://www.entrust.net/downloads/binary/entrust_2048_ca.cerYou then also need to addstream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');to the simplepush.php file after line 16.

Where To Go From Here?

At this point, you’ve successfully set up an app to receive push notifications, and delivered your first push notification through custom PHP code!

Next check out part 2 of the tutorial series, where I’ll cover how to make a simple direct messaging app called PushChat that uses push notifications to deliver the messages.

There you’ll learn how to make a complete server side API that delivers push notifications continuously in the background!

In the meantime, if you have any questions, comments, or suggestions, feel free to join in the forum discussion below!

 

 

=================================

=================================

=================================

 

 

 

출처: http://blog.kondratev.pro/2015/03/sending-ios-push-notifications-from-java.html

 

Sending iOS push notifications from JAVA

 

This example presumes that you've already registered your device for Push Notifications and retrieved the device token from the Apple Push Notification Service (APNS) on the iOS side.
The code below uses the https://github.com/notnoop/java-apns Java library to send notifications to the APNS server.

The code

Suggested files structure:
src/main/java/PushNotifications.java - simple java class to send notifications
src/main/resources/dev_cert.p12 - development certificate
src/main/resources/prod_cert.p12 - production certificate
pom.xml - maven config

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>PushNotifications</groupId>
    <artifactId>PushNotifications</artifactId>
    <version>1.0</version>

    <dependencies>
        <dependency>
            <groupId>com.notnoop.apns</groupId>
            <artifactId>apns</artifactId>
            <version>1.0.0.Beta6</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.7</version>
        </dependency>
    </dependencies>

</project>

 


PushNotifications.java:

import com.notnoop.apns.APNS;
import com.notnoop.apns.ApnsService;
import com.notnoop.apns.ApnsServiceBuilder;

public class PushNotifications {

    public static void main(String [] args) {
        System.out.println("Sending an iOS push notification...");

        String token = "";
        String type = "dev";
        String message = "the test push notification message";

        try {
            token = args[0];
        } catch (Exception e) {
            System.out.println("Usage: PushNotifications devicetoken [message] [type prod|dev]");
            System.out.println("example: PushNotifications 1testdevicetoken3eb414627e78991ac5a615b4a2a95454c6ba5d18930ac137 'hi there!' prod");
            return;
        }
        try {
            message = args[1];
        } catch (Exception e) {
            System.out.println("no message defined, using '"+message+"'");
        }
        try {
            type = args[2];
        } catch (Exception e) {
            System.out.println("no API type defined, using "+type);
        }

        System.out.println("The target token is "+token);

        ApnsServiceBuilder serviceBuilder = APNS.newService();

        if (type.equals("prod")) {
            System.out.println("using prod API");
            String certPath = PushNotifications.class.getResource("prod_cert.p12").getPath();
            serviceBuilder.withCert(certPath, "password")
                    .withProductionDestination();
        } else if (type.equals("dev")) {
            System.out.println("using dev API");
            String certPath = PushNotifications.class.getResource("dev_cert.p12").getPath();
            serviceBuilder.withCert(certPath, "password")
                    .withSandboxDestination();
        } else {
            System.out.println("unknown API type "+type);
            return;
        }

        ApnsService service = serviceBuilder.build();


        //Payload with custom fields
        String payload = APNS.newPayload()
                .alertBody(message)
                .alertTitle("test alert title")
                .sound("default")
                .customField("custom", "custom value").build();

        ////Payload with custom fields
        //String payload = APNS.newPayload()
        //        .alertBody(message).build();

        ////String payload example:
        //String payload = "{\"aps\":{\"alert\":{\"title\":\"My Title 1\",\"body\":\"My message 1\",\"category\":\"Personal\"}}}";


        System.out.println("payload: "+payload);
        service.push(token, payload);

        System.out.println("The message has been hopefully sent...");
    }
}

 

Compile the code:
>mvn compile

Run the code:
>mvn exec:java -Dexec.mainClass="PushNotifications" -Dexec.args="1testdevicetoken3eb414627e789ba5d18930ac137 'My test iOS push notification!' dev" 

Getting certificates from the Apple
Certificates are quite tricky and confusing part of iOS push notifications.

1. First of all we need to create a SigningRequest
Keychain Access -> Certificate Assistant -> Request a certificate from a Certificate Authority
Enter the apple dev email into the Certificate Assistant form, select the "Save to disk" radio
Save the Signing Request onto the disk (don't forget to give it some reasonable name);

2. Go to the developer.apple.com/account/ios/
Certificates, Identifiers & Profiles > App IDs > [the app id] > Edit > Push notifications >
Production SSL Certificate > Create / Create Additional,
ensure that Push Notifications enabled for your app,
Generate a new certificate using the SigningRequest from 1.
Download the generated certificate;

3. (on the Mac) Double click the certificate to import into the keychain;

4. Locate the Certificate in the Keychain Access, expand it to see the Key inside,
right click and Export "keyname"...
Save the p12 file;

5. Notice that after regenerating your certificates you need to refresh your provisioning profiles and use them to deploy your app. In xCode go to Preferences -> Accounts, selet your dev apple-id, click "View Details", do refresh. It is not going to be a problem, if you generated the certeficates before getting to code.

Links
If you use the PHP on you server, you may find the link below useful:
http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1

 

 

=================================

=================================

=================================

 

 

 

**** 관련링크들 ****

 

Node.js의 생산성은 감탄을 금할 길이 없도다.

Node.js와 Google Play Service를 이용하여 안드로이드 푸시서비스 구현하기(GCM)

http://blog.saltfactory.net/node/implementing-push-notification-service-for-android-using-google-play-service.html

http://blog.saltfactory.net/android/implement-push-service-via-gcm.html

 

Node.js를 이용하여 iOS 푸시서비스 구현하기

http://blog.saltfactory.net/node/implementing-push-notification-service-for-ios.html

 

 

=================================

=================================

=================================

 

 

 

node.js 를 이용한 서버 (자바구현)

 

http://www.java2s.com/Open-Source/Java_Free_Code/Server/Download_push_notification_apn_gcm_Free_Java_Code.htm

 

push-notification-apn-gcm-master.zip
0.72MB

 

 

=================================

=================================

=================================

 

 

 

 

 

출처링크: http://blog.naver.com/rs1017/90150373326

 

APNS( IOS Push Server )

 

1. 대략적인 설명

http://sunsideup.tistory.com/66

http://sunsideup.tistory.com/75

 

 

2. 디테일한 설명

http://artyst.egloos.com/2652130

 

 

3. 디테일한 설명 #2

http://j07051.tistory.com/entry/Push-Notification%ED%91%B8%EC%8B%9C-%EC%95%8C%EB%A6%BC-%EC%84%9C%EB%B9%84%EC%8A%A4-for-JSP

 

 

 

이하 내용 출처 : 

http://blog.naver.com/PostView.nhn?blogId=biboi99&logNo=90145323945&categoryNo=0&parentCategoryNo=0&viewDate=¤tPage=1&postListTopCurrentPage=1&userTopListOpen=true&userTopListCount=20&userTopListManageOpen=false&userTopListCurrentPage=1

 

APNS 관련 싸이트

 

이지 APNS 오픈 소스 라이브러리

http://www.easyapns.com/

구글 코드 APNS 오픈 소스

http://code.google.com/p/apns-php/

서버 튜토리얼

http://blog.serverdensity.com/how-to-build-an-apple-push-notification-provider-server-tutorial/

 

 

http://cafe.naver.com/mcbugi/203687 ( asp.net )

https://github.com/thegeekbird/Apns4r ( ruby )

https://github.com/samuraisam/pyapns ( physon )

http://blog.toshsoft.de/index.php?/archives/3-Sending-Apple-Push-Notifications-APN-in-C-Updated-Using-CA-Cert.html ( c )

http://code.google.com/p/javapns/ ( java )

http://code.google.com/p/apns-php/ ( php )

[출처] APNS( IOS Push Server )|작성자 닌자거북이

 

 

=================================

=================================

=================================

 

 

반응형


관련글 더보기

댓글 영역