http://blog.daum.net/mailss/29
다음은 구글의 GCM 문서의 일부분이다.
참조: https://developer.android.com/google/gcm/index.html
GCM Server 구현하기
Google Cloud Messaging (GCM)의 서버는 2개의 요소로 이루어진다.:
- Google이 제공하는 GCM 연결 server:
3rd-party app server로부터 메시지를 받아서 GCM-enabled Android application (the "client app") running on a device에게 전달한다. - 당신이 구현할 3rd-party app server
3rd-party app server를 구현하려면,
GCM server helper library와 demo app을 이용한다.
GCM HTTP 연결 server
- Downstream만 가능하다: cloud-to-device.
- 3rd-party app server들은 HTTP POST request로 메시지 전송요청을 보낸후 응답이 오기를 기다려야만 한다. 이 메커니즘은 synchronous하기 때문에 송신자를 block시키므로, app server는 그 사이에 다른 메시지를 보낼 수 없다.
- JSON 객체인 message들이 HTTP POST를 통해서 전송된다.
3rd-party app server의 역할
GCM feature를 사용하는 Android client app을 만들려면, 다음의
기준을 만족시키는 3rd party app server를 먼저 만들어야 한다:
- Android client app과 통신할 것
- 올바른 형식의 request를 GCM server로 보낼 것
- 필요한 경우에는 exponential back-off를 이용해서 request를 재전송할 것
- API key와 client registration ID를 저장할 것. Message를 전송하는 POST request의 헤더에 API key를 포함시킬 것.
- App server가 전송하는 각 메시지는 유일한 message ID를 생성할 것 (?)
Message 보내기
3rd-party app server가 client Android application에게 메시지를 전송하는 절차:
- 3rd-party app server는 GCM server에게 메시지를 전송한다.
- Device가 offline이면, GCM server는 그 메시지를 큐에 넣고 저장한다.
- Device가 online이면, GCM server는 그 메시지를 device에게 전달한다.
- Device에서는, 지정된 Android application에게 system이 그 메시지를 Intent broadcast with proper permissions 를 통해서 broadcast하기 때문에 지정된 Android application만이 그 메시지를 수신한다. 이때 지정된 Android application은 깨어난다. 메시지를 수신하기 위해서 Android client app이 사전에 실행될 필요는 없다.
- 지정된 Android application은 그 메시지를 처리한다.
뒤따르는 섹션들은 메시지 전송과 관련한 기본 요구사항을 설명한다.
Target
필수사항.
GCM HTTP인 경우 다음 둘 중의 하나로 target을 명시해야 한다:
- registration_ids: 1000개 미만의 device에게 메시지를 전송한다. 메시지를 복수개의 registration id들에게 보내는 것을 multicast message라고 말한다.
- notification_key: 한 사용자 소유의 여러 device에게 메시지를 전송한다.
Payload
선택사항.
파라미터 data 를 이용해서 payload를
넣는다.
Message parameters
뒤따르는 표는 3rd-party app server가 연결 server에 전송하는 JSON 메시지에 포함시켜야 하는 파라미터들을
열거한다.
Table 1. JSON 메시지 파라미터
파라미터
|
설명
|
registration_ids
|
string array containing the list of devices.
최소 1개, 최대 1000개의 registration ID.
multicast message를 보내려면, JSON을 사용해야 한다.
하나의 device에 하나의 메시지만 보낸다면, JSON객체
대신에 plain text를 사용해도 된다. (아래를
참조하세요)
하나의 Request는 하나의 recipient를
가져야 한다 (그런데 이 recipient는 하나의 registration ID이거나 여러 registration ID들의
배열일 수 있다. 또는 하나의 notification_key일 수 있다).
필수사항.
|
notification_key
|
String.
한 사용자가 소유한 여러대의 deivce들에 설치되어있는 해당 app instance들.
This allows a 3rd-party server to send a single message
to multiple app instances (typically on multiple devices) owned by a single
user.
A 3rd-party server can use notification_key as the target
for a message instead of an individual registration ID (or array of
registration IDs).
최대 20개의 notification_key.
선택사항.
|
collapse_key
|
This parameter specifies an arbitrary string (such as
"Updates Available") that is used to collapse a group of like
messages when the device is offline, so that only the last message gets sent
to the client.
유사한 메시지별로 동일한 collapse_key를 주면,
너무 많은 메시지를 발생시키는 것을 피할 수 있다.
collapse key를 갖는 메시지를 send-to-sync
messages 라고도 부른다.
GCM 서버는 각
device별로 최대 4개의 collapse key를
제공한다. 이 제약을 넘어서면 서버는 자기 마음대로 4개의 collapse key를 선택한다.
선택사항.
|
data
|
4KB 미만의 JSON
object whose fields represents the key-value pairs of the message's payload
data.
If present, the payload data will be included in the
Intent as application data, with the key being the extra's name.
For instance, "data":{"score":"3x1"} would result in an intent extra named score whose value is
the string 3x1.
There is no limit on the number of key/value pairs,
though there is a limit on the total size of the message (4kb).
Key/value에서 value는 무엇이건 가능하지만, GCM server가 value를 string으로 변환시키기 때문에, value의 데이터타입은 string인 것이 가장 좋다.
Integer나 boolean을 value로 사용하고싶다면, 그것들을 string으로 변환하는 것은 당신이 스스로 해야한다.
예약된 단어를 key로 사용할 수 없다. (예: from, to, 또는 google로 시작하는 단어)
선택사항.
|
delay_while_idle
|
이 값이 JSON boolean값으로 true이면 device가 잠들어있는 상황에서는 메시지를 곧바로 전달하지 않고,
device가 active가 될 때까지 기다린다.
디폴트 값은 false이다.
선택사항.
|
time_to_live
|
JSON number.
Device가 offline일
때 메시지를 GCM 저장소에 보관하고 있어야 할 초.
디폴트는 4 weeks.
선택사항.
|
restricted_package_name
|
Client Android application의 package name을 나타내는 string.
이 값이 명시된 경우, 메시지는 그 package
name과 일치하는 registration ID들에게만 전달된다.
선택사항.
|
dry_run
|
실제로 메시지를 device로 전송하지 않은채로
request를 테스트한다.
선택사항.
디폴트는 JSON boolean 값으로 false.
|
메시지를 JSON대신
plain text로 보내는 경우에는 메시지 파라미터는 뒤따르는 표에서 설명한 것처럼 되어야 한다.
Table 2. Message Parameters Plain Text (HTTP only).
파라미터
|
설명
|
registration_id
|
|
collapse_key
|
|
data.<key>
|
data. 뒤에 key 이름.
For instance, a parameter of data.score=3x1 would result in
an intent extra named score whose value is the string 3x1.
|
delay_while_idle
|
A value of 1 or true indicates true,
and anything else indicates false.
|
time_to_live
|
|
restricted_package_name
|
|
dry_run
|
Message 수신하기
Client Android application이 메시지를 수신하는 절차:
- system은 메시지를 수신하고, 메시지에 payload가 있다면 거기서 raw key/value pair들을 추출한다.
- System은 key/value pair들을 com.google.android.c2dm.intent.RECEIVE Intent의 a set of extras 에 담아서 targeted Android application에게 전달한다.
- 그 client Android application은 com.google.android.c2dm.intent.RECEIVE Intent로부터 key를 통해서 raw data를 추출하고 그 data를 처리한다.
GCM HTTP 연결 server
연결 server들은 3rd-party app server로부터 메시지를 받아서 그것을 device에게 보내는 Google이 제공하는 server들이다.인증하기
3rd-party app server는 메시지를 보낼 때https://android.googleapis.com/gcm/send
로 POST request를 보낸다.Request는 두 부분으로 구성된다: HTTP header와 HTTP body.
HTTP header는 다음 헤더들을 반드시 포함해야 한다:
Authorization
: key=YOUR_API_KEYContent-Type
:application/json
… for JSON;
application/x-www-form-urlencoded;charset=UTF-8
… for plain text.
Content-Type:application/json Authorization:key=AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA { "registration_ids" : ["APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx..."], "data" : { ... }, }Note: Plain text인 경우
Content-Type를 생략할
수 있다
.Request Format
다음은 JSON을 이용한 가장 작은 request이다:{ "registration_ids": [ "42" ] }
다음은 plain text를 이용한 동일한 메시지이다:registration_id=42
다음은 payload와 6개의 수신자를 갖는 메시지이다:{ "data": {
"score": "5x1",
"time": "15:10"
},
"registration_ids": ["4", "8", "15", "16", "23", "42"]
}
다음은 모든 optional field들과 6개의 수신자를
갖는 메시지이다:{ "collapse_key": "score_update", "time_to_live": 108, "delay_while_idle": true, "data": { "score": "4x8", "time": "15:16.2342" }, "registration_ids":["4", "8", "15", "16", "23", "42"] }다음은 동일한 메시지인데 수신자가 1개인 plain-text format이다:
collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.score=4x8&data.time=15:16.2342®istration_id=42
Note: Firewall에서 열어야 할 port가 있다: 5228, 5229, and 5230.
Response format
메시지를 전송요청은 두 가지 결과가 가능하다.:- 요청이 성공적으로 처리되었다.
- GCM server가 요청을 거절한다.
요청이 거절된 경우, HTTP response는 non-200 status code (such as 400, 401, or 503)를 갖는다.
뒤따르는 표는 HTTP response header에 포함될 수 있는 status들을 요약하고 있다.
Status code
|
설명
|
200
|
요청이 성공적으로 처리되었다.
응답의 format에 대해서 자세히 알려면 Interpreting
a success response를 읽어봐라.
|
400
|
JSON request인 경우에만 해당함.
Request가 JSON으로 해석될 수 없음, 또는 invalid field를 포함하고 있음 (예를 들면, 숫자가 있어야할 곳에 문자열이있다).
|
401
|
Sender 계정에 대한 인증에 실패했다.
|
5xx
|
Request에는 문제가 없었다.
나중에 재시도를 하면 된다. Response에 포함된
Retry-After header를 참고해서 재시도하라. App server에는 exponential back-off를 구현해 놓아야 한다. |
요청의 성공적인 처리에 관한 response를 해석하기
JSON request가 성공적으로 처리되었을 경우 (HTTP status code 200), response body에는 다음과 같은 field를 갖는 JSON object가 들어있다:
Field
|
설명
|
multicast_id |
multicast message 식별자
|
success |
에러 없이 잘 처리된 메시지의 개수
|
failure |
처리과정에서 에러가 발생한 메시지의 개수
|
canonical_ids |
canonical registration ID를 포함하는 result의 개수.
사용한 registration ID에 대해서 canonical registration ID를 받으면, 서버에
저장해놓은 그 사용자의 registration ID를
canonical registration ID로 바꿔야 한다.
|
results |
처리한 메시지의 상태를 나타내는 객체들의 배열.
객체들은 request에 나열된 순서를 유지한 채로 나열되고 (즉, for each registration ID in the
request, its result is listed in the same index in the response), 다음과 같은 field들을 갖는다:
|
Failure와
canonical_ids의
값이
0이면, response를 해석할 필요가
없다.그 값들이 0이 아니라면, results field를 일일이 검토하여 다음과 같은 처리를 해야 한다:
만약 message_id
가 있으면,registration_id
를 조사해라
:만약 registration_id가 있으면
, DB에서 원래의 ID를 canonical ID로 변경시켜라. 원래의 ID는 result에서 발견할 수 없으므로, request를 조사해서 알아내야 한다 (using the same index).- Message_id가 없으면,
error
값을 조사해라
: - 만약 그 값이
Unavailable
이라면
, 다른 request를 만들어 보내서 재전송하라. - 만약 그 값이
NotRegistered
이라면
, DB에서 registration ID를 삭제해라. (Client Android app이 device에서 삭제되었거나 메시지를 수신할 수 없게 되었다). - 만약 그 외의 경우라면, registration ID에 어떤 문제가 있다; 이 문제는 해결할 수 없기 때문에 DB에서 registration ID를 삭제해야 한다. 모든 error value들에 대해서 알고싶으면 아래의 error response를 해석하기를 참조하라.
id=
ID of sent message 이거나 Error=
GCM error code이다. 혹시 두 번째 줄이 존재한다면, registration_id=
canonical ID 이다. 두 번째 줄은 첫 번째 줄이 error가 아닌 경우에만 존재한다. Plain-text response도 JSON response처럼 처리한다:- 만약 첫 번째 줄이
id
라는 글자로 시작하면
, 두 번째 줄을 조사해라: - 만약 두 번째 줄이
registration_id
라는 글자로 시작하면
, 그 값으로 DB에 저장되어있는 registration ID 값을 갱신시켜라. - 첫 번째 줄이 id라는 글자로 시작하지 않는다면,
Error
의 값을 조사해라
: - 만약 그 값이
NotRegistered
이라면
, DB에서 registration ID를 삭제하라. - (Plain-text request는
Unavailable
을 리턴하지 않는다)
- 만약 그 값이
NotRegistered
이외의 것이라면, 이 문제는 해결할 수 없기 때문에 DB에서 registration ID를 삭제하라.
error response를 해석하기
Here are the recommendations for handling the different types of error that might occur when trying to send a message to a device:
Missing Registration ID
Check that the
request contains a registration ID (either in the
Happens when error code is
registration_id
parameter in a plain text message, or in the registration_ids
field in JSON). Happens when error code is
MissingRegistration
.
Invalid Registration ID
Check the
formatting of the registration ID that you pass to the server. Make sure it
matches the registration ID the phone receives in the
Happens when error code is
com.google.android.c2dm.intent.REGISTRATION
intent and that you're not truncating it or adding additional
characters. Happens when error code is
InvalidRegistration
.
Mismatched Sender
A registration
ID is tied to a certain group of senders. When an application registers for GCM
usage, it must specify which senders are allowed to send messages. Make sure
you're using one of those when trying to send messages to the device. If you
switch to a different sender, the existing registration IDs won't work. Happens
when error code is
MismatchSenderId
.
Unregistered Device
An existing
registration ID may cease to be valid in a number of scenarios, including:
·
If the application manually
unregisters by issuing a
com.google.android.c2dm.intent.UNREGISTER
intent.
·
If the application is
automatically unregistered, which can happen (but is not guaranteed) if the
user uninstalls the application.
·
If the registration ID expires.
Google might decide to refresh registration IDs.
·
If the application is updated
but the new version does not have a broadcast receiver configured to receive
com.google.android.c2dm.intent.RECEIVE
intents.
For all these
cases, you should remove this registration ID from the 3rd-party server and
stop using it to send messages.
Happens when error code is
Happens when error code is
NotRegistered
.
Message Too Big
The total size
of the payload data that is included in a message can't exceed 4096 bytes. Note
that this includes both the size of the keys as well as the values.
Happens when error code is
Happens when error code is
MessageTooBig
.
Invalid Data Key
The payload data
contains a key (such as
Happens when the error code is
from
or any value prefixed by google.
) that is used
internally by GCM in the com.google.android.c2dm.intent.RECEIVE
Intent and cannot be used. Note that some words (such as collapse_key
)
are also used by GCM but are allowed in the payload, in which case the payload
value will be overridden by the GCM value. Happens when the error code is
InvalidDataKey
.
Invalid Time To Live
The value for
the Time to Live field must be an integer representing a duration in seconds
between 0 and 2,419,200 (4 weeks). Happens when error code is
InvalidTtl
.
Authentication Error
The sender
account that you're trying to use to send a message couldn't be authenticated.
Possible causes are:
·
Authorization header missing or
with invalid syntax.
·
Invalid project number sent as
key.
·
Key valid but with GCM service
disabled.
·
Request originated from a
server not whitelisted in the Server Key IPs.
Check that the
token you're sending inside the
Authorization
header is the correct API
key associated with your project. You can check the validity of your API key by
running the following command:# api_key=YOUR_API_KEY # curl --header "Authorization: key=$api_key" --header Content-Type:"application/json" https://android.googleapis.com/gcm/send -d "{\"registration_ids\":[\"ABC\"]}"
If you receive a
401 HTTP status code, your API key is not valid. Otherwise you should see
something like this:
{"multicast_id":6782339717028231855,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
If you want to
confirm the validity of a registration ID, you can do so by replacing
"ABC" with the registration ID.
Happens when the HTTP status code is 401.
Happens when the HTTP status code is 401.
Timeout
The server
couldn't process the request in time. You should retry the same request, but
you MUST obey the following requirements:
·
Honor the
Retry-After
header if it's included in the response from the GCM server.
·
Implement exponential back-off
in your retry mechanism. This means an exponentially increasing delay after
each failed retry (e.g. if you waited one second before the first retry, wait
at least two second before the next one, then 4 seconds and so on). If you're
sending multiple messages, delay each one independently by an additional random
amount to avoid issuing a new request for all messages at the same time.
Senders that
cause problems risk being blacklisted.
Happens when the HTTP status code is between 501 and 599, or when the
Happens when the HTTP status code is between 501 and 599, or when the
error
field
of a JSON object in the results array is Unavailable
.
Internal Server Error
The server
encountered an error while trying to process the request. You could retry the
same request (obeying the requirements listed in the Timeout
section), but if the error persists, please report the problem in the android-gcm
group.
Happens when the HTTP status code is 500, or when the
Happens when the HTTP status code is 500, or when the
error
field
of a JSON object in the results array is InternalServerError
.
Invalid Package Name
A message was
addressed to a registration ID whose package name did not match the value
passed in the request. Happens when error code is
InvalidPackageName
.
Device Message Rate Exceeded
The rate of
messages to a particular device is too high. You should reduce the number of
messages sent to this device and should not retry sending to this device
immediately.
Happens when error code is
Happens when error code is
DeviceMessageRateExceeded
.Response의 예제
여기서는 response의 여러가지 예를 보여준다.다음은 성공적으로 처리된 가장 간단한 경우의 JSON 형식의 응답이다:
{ "multicast_id": 108, "success": 1, "failure": 0, "canonical_ids": 0, "results": [ { "message_id": "1:08" } ] }위와 동일한 응답의 plain-text 형식은 다음과 같다:
id=1:08
다음은 수신자가 6개 (ID는 각각 4, 8, 15, 16, 23, and 42) 이고, 성공적으로 처리된 메시지가 3개,
canonical registration ID를 받은 메시지가 1개, error가 발생한 메시지가 3개인 경우의 JSON 형식의 응답이다:{ "multicast_id": 216, "success": 3, "failure": 3, "canonical_ids": 1, "results": [ { "message_id": "1:0408" }, { "error": "Unavailable" }, --> 재전송작업 필요 { "error": "InvalidRegistration" }, --> DB삭제작업 필요 { "message_id": "1:1516" }, { "message_id": "1:2342", "registration_id": "32" }, --> DB갱신작업 필요 (23 -> 32) { "error": "NotRegistered"} --> DB삭제작업 필요 (device에서 app이 삭제되었음) ] }In this example:
- First message: success, not required.
- Second message: should be resent (to registration ID 8).
- Third message: had an unrecoverable error (maybe the value got corrupted in the database).
- Fourth message: success, nothing required.
- Fifth message: success, but the registration ID should be updated in the server database (from 23 to 32).
- Sixth message: registration ID (42) should be removed from the server database because the application was uninstalled from the device.
Error=InvalidRegistration
다섯 번째 메시지:id=1:2342
registration_id=32