gcm server 내용 정리

GCM 서비스를 신청하는 방법은 아래의 블로그에 자세히 나와있다.
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에게 메시지를 전송하는 절차:
  1. 3rd-party app server는 GCM server에게 메시지를 전송한다.
  2. Device가 offline이면, GCM server는 그 메시지를 큐에 넣고 저장한다.
  3. Device가 online이면, GCM server는 그 메시지를 device에게 전달한다.
  4. Device에서는, 지정된 Android application에게 system이 그 메시지를 Intent broadcast with proper permissions 를 통해서 broadcast하기 때문에 지정된 Android application만이 그 메시지를 수신한다. 이때 지정된 Android application은 깨어난다. 메시지를 수신하기 위해서 Android client app이 사전에 실행될 필요는 없다.
  5. 지정된 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이 메시지를 수신하는 절차:
  1. system은 메시지를 수신하고, 메시지에 payload가 있다면 거기서 raw key/value pair들을 추출한다.
  2. System은 key/value pair들을 com.google.android.c2dm.intent.RECEIVE Intent의 a set of extras 에 담아서 targeted Android application에게 전달한다.
  3. 그 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_KEY
  • Content-Type: application/json for JSON;
    application/x-www-form-urlencoded;charset=UTF-8 for plain text.
For example:
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&registration_id=42
 
Note: Firewall에서 열어야 할 port가 있다: 5228, 5229, and 5230.


Response format

메시지를 전송요청은 두 가지 결과가 가능하다.:
  • 요청이 성공적으로 처리되었다.
  • GCM server가 요청을 거절한다.


요청이 성공적으로 처리된 경우, HTTP response는 200 status를 갖고 body는 메시지의 상태에 관한 더 자세한 내용을 포함한다.
요청이 거절된 경우, 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들을 갖는다:
  • message_id: 성공적으로 처리된 메시지를 나타내는 식별자.
  • registration_id: 메시지는 잘 전송했으나, 그 사용자의 registration ID를 canonical registration ID로 갱신시켜야 한다.
  • error: String describing an error that occurred while processing the message for that recipient. 가능한 값들은 상기 표에 나타난 것들에, "Unavailable" (GCM server에 부하가 심해서 특정 수신자의 메시지를 처리하지 못했고, 나중에 그 메시지를 재전송해야 한다)이 하나 추가된다.


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를 해석하기를 참조하라.


plain-text request 가 성공한 경우 (HTTP status code 200), response body는 key/value pair 형식의 한 줄 또는 두 줄이다. 첫 번째 줄은 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 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 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 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 MessageTooBig.

Invalid Data Key
The payload data contains a key (such as 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.

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 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 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 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.


만약 위의 메시지들이 plain-text형식으로 하나씩 전송되었더라면 응답은 다음과 같았을 것이다. 세 번째 메시지:
Error=InvalidRegistration
다섯 번째 메시지:
id=1:2342
registration_id=32