Intel Edisonにプッシュ通知する

# プッシュ通知とは
　コンピュータをインターネットに接続して情報を取得する場合，その多くは，クライアント側からインターネット上のサーバーにアクセスし，必要な情報をもらってきます．これとは真逆の経路で，サーバー側から情報をクライアント側に通知しに来るというのが，”プッシュ通知”と言われるアクセスになります．身近なところでは，電話のコールを受けたり，Skype/LINE等でメッセージを受け取る場合などがあります．サーバー発の情報をクライアントで受け取る場合，この他にも”ポーリング”と言って，一定時間ごとにクライアントがサーバーに対して新しい情報がないかを問い合わせるという手段もあります．
　実は，このプッシュ通知，自分で実装しようと思うとすごくタイヘン．それについて，次章で説明します．

# 技術的困難さ
　世の中の多くのコンピュータは，インターネット（www）と接続する際に，ルーター（ブロードバンドルーター）が間に挟まって，ローカルネットワーク（LAN）とwwwを分離しています．ルーターは，LAN内のコンピュータに対し，LAN内のみで固有のIPアドレス（プライベートアドレス）を各コンピュータに割当ててネットワークを統治しています．そして，LAN内のコンピュータがwwwのコンピュータと通信を行う際には，LAN内のコンピュータのプライベートアドレスを，wwwで通用するグローバルIPアドレスに変換して通信できるようにする（NAT/IPマスカレード）という役割もこなしています．
![いんたぁねっとの図](https://pbs.twimg.com/media/B4s3A-ZCEAA19F7.png "いんたぁねっとの図")
　この構成ですと，LAN内のコンピュータからwwwにアクセスする際には問題ないのですが，逆にwww側のコンピュータからLAN内のコンピュータにアクセスしようと思った際に，LAN内のコンピュータをwww内で識別するグローバルIPアドレスが存在しないため，アクセスできません．

# 回避方策
　[NAT越え](http://ja.wikipedia.org/wiki/NAT_traversal)は，まだ確立された技術ではないため，決定的な方策は存在しません．今回は，とりあえず2種類を紹介します．
　 **以下で説明するSTUN/TURNについて，[voluntas](http://qiita.com/voluntas)さんからコメントをいただきました．** コメントを参照ください．

## サーバーを介す
　互いに通信を行おうとする端末同士が同一のサーバーと接続し，そのサーバーが両者の通信をリレーする方式です．特に，標準化された方法に[TURN](http://ja.wikipedia.org/wiki/TURN)があります．この方式は，通信量とサーバーの負荷が相当かかりますが，
実装が簡単で，
ほとんど全てのNATを越えられます．

## STUN
　[STUN](http://ja.wikipedia.org/wiki/STUN)は，通信を行おうとする端末同士が，
どのようなグローバルIPアドレスを所持して
何番のポート番号でwwwと接続しているかを，実際にwww上にいるサーバーに教えてもらい，その情報を使って端末同士がP2Pで通信を行う方式です．この方式ですと，サーバーは最初の一回だけ端末にプロファイルを通知するだけなので，サーバーのスペックに依らずに端末同士で高速な通信が可能です．ただし，STUNでは越えられるNATの種類に制限があります．

# 実験
　そんな困難なNAT越えですが，外部のサービスを使ってお手軽に実現してみましょう．このようなプッシュ通知をサポートするサービスとしては，[PubNub](http://www.pubnub.com/)が有名です．他にも，[Google Cloud PlatformのChannel API](https://cloud.google.com/appengine/docs/python/channel/)を利用するという手もあります．
　しかし今回は，みんな大好き，僕も大好きTwitterの，[Streaming API](https://dev.twitter.com/streaming/overview)を利用します．
　Twitterにアプリケーションからアクセスするためには，自作アプリを登録して，コンシューマーキーを発行し，さらにアクセスするアカウントのアクセストークンを取得する必要があります．[このへん参照](http://webgaku.hateblo.jp/entry/2014/04/11/200523)．

```python:push.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from twitter import *
import mraa, time

gpio = mraa.Gpio(20)
gpio.dir(mraa.DIR_OUT)

def blink():
  for i in range(20):
    gpio.write(0 if i%2==0 else 1)
    time.sleep(0.3)

auth = OAuth(
    consumer_key = "iruafhiiUEHWIiuHFWIUli",
    consumer_secret = "Eiuhf4fFW0fwwfehi2fWEiWooifwQEIUUfh2efFui",
    token = "428639810-dwufhiEhi04FWIi3Lluh3fwiuhIWhi2fi8fwuFWf",
    token_secret = "fo9f3fwufwiGiuHF57Fui1ohifut7kfwuFKwufhwuF"
    )

allow_users = ['k_yone']

twitter_userstream = TwitterStream(auth=auth, domain='userstream.twitter.com    ')
for msg in twitter_userstream.user():
  if 'user' in msg:
    user = msg['user']
    if 'screen_name' in user:
      print("user: %s " % user['screen_name'])
      if user['screen_name'] not in allow_users:
        continue

      print msg['text']
      if msg['text'] == u"Lチカ":
        blink()
        print "success"
```

プログラムはこんなかんじになります．
　TwitterのStreaming APIは，クライアントとサーバー間で通信経路を確立したまま保持し続けるので，サーバーにアクションがあった場合にその結果が即時にクライアントに送信されます．この機能を利用したNAT越えになります．送れるデータが140文字以内のテキストデータとしょぼいですが，デバイスによっては，これで十分な使い方もあるでしょう．また，Twitterの持つ認証機構をそのまま利用できるのも魅力です．

# 結果
　動画は[こちら](https://twitter.com/k_yone/status/543582690226995200)．

# まとめ
+ NAT越えはタイヘン
+ サポートしてくれる外部サービスがいくつかある
+ TwitterのStreaming APIは使い出がある