[翻译]Go语言开发苹果推送通知

原文在此:http://bravenewmethod.wordpress.com/2011/02/25/apple-push-notifications-with-go-language/

前两天正巧看到 APNS 没有 Go 的实现,还在琢磨怎么实现一个试试,这下我又省心了。文章本身并不怎么出色,代码倒是有些用途。翻译这篇东西纯粹是为了给自己后面的工作留个资料。大家有用则用,无用就无视吧。

————–翻译分割线————–

Go语言开发苹果推送通知

我开始尝试学习并熟悉 Go 语言,并且做了一些普通的常识,例如,发送苹果推送通知(Apple Push Notifications)。这是我个人对一些开发环境的性能测试。迄今为止,已经有:

第一步 准备

获取并编译 Go。这里的例子是在 Ubuntu 10.04 LTS x64 上,基于 Go 上手指南(Go getting started guide) 里的介绍进行安装。

  • 关于苹果推送和取得应用沙盒私钥的 .pem 文件的介绍在这里
  • 当然,你必须从 iOS 应用获取 32 位的推送令牌(push token)。

第二部 代码

这里是完整的代码,复制并保存为文件 apn.go。

注意修改证书文件(cert.pem 和 key-noenc.pem)为你自己的证书文件。同时,替换推送令牌为你自己的推送令牌, 在这个例子里为了更加清楚,使用十六进制字符串书写。

package main

import (
   "crypto/tls"
   "fmt"
   "net"
   "json"
   "os"
   "time"
   "bytes"
   "encoding/hex"
   "encoding/binary"
)

func main() {

   // 加载证书和配置文件
   cert, err := tls.LoadX509KeyPair("cert.pem", "key-noenc.pem")
   if err != nil {
       fmt.Printf("error: %s\n", err.String())
       os.Exit(1)
   }
   conf := &tls.Config {
        Certificates: []tls.Certificate{cert},
   }

   // 连接到 APNS 并且用 tls 客户端加密 socket
   conn, err := net.Dial("tcp", "", "gateway.sandbox.push.apple.com:2195")
   if err != nil {
      fmt.Printf("tcp error: %s\n", err.String())
      os.Exit(1)
   }
   tlsconn := tls.Client(conn, conf)

   // 强制握手,以验证身份握手被处理,否则会在第一次读写的时候进行尝试
   err = tlsconn.Handshake()
   if err != nil {
      fmt.Printf("tls error: %s\n", err.String())
      os.Exit(1)
   }
   // 调试信息
   state := tlsconn.ConnectionState()
   fmt.Printf("conn state %v\n", state)

   // 从 JSON 结构构造二进制的 payload
   payload := make(map[string]interface{})
   payload["aps"] = map[string]string{"alert": "Hello Push"}
   bpayload, err := json.Marshal(payload)

   // 将十六进制的设备令牌转为二进制的字节数组
   btoken, _ := hex.DecodeString("6b4628de9317c80edd1c791640b58fdfc46d21d0d2d1351687239c44d8e30ab1")

   // 构造 pdu
   buffer := bytes.NewBuffer([]byte{})
   // 命令
   binary.Write(buffer, binary.BigEndian, uint8(1))

   // 传输 id,optional
   binary.Write(buffer, binary.BigEndian, uint32(1))

   // 过期时间,1小时
   binary.Write(buffer, binary.BigEndian, uint32(time.Seconds() + 60*60))

   // 推送设备令牌
   binary.Write(buffer, binary.BigEndian, uint16(len(btoken)))
   binary.Write(buffer, binary.BigEndian, btoken)

   // 推送 payload
   binary.Write(buffer, binary.BigEndian, uint16(len(bpayload)))
   binary.Write(buffer, binary.BigEndian, bpayload)
   pdu := buffer.Bytes()

   // 写入 pdu
   _, err = tlsconn.Write(pdu)
   if err != nil {
      fmt.Printf("write error: %s\n", err.String())
      os.Exit(1)
   }

   // 等待 5 秒,以便 pdu 从 socket 返回错误
   tlsconn.SetReadTimeout(5*1E9)

   readb := [6]byte{}
   n, err := tlsconn.Read(readb[:])
   if n > 0 {
     fmt.Printf("received: %s\n", hex.EncodeToString(readb[:n]))
   }

   tlsconn.Close()
}

第三步 编译和运行

简单

$ 6g apn.go
$ 6l apn.6
$ ./6.out
conn state {true 47}
$

如果所有都没有问题,程序会在几秒钟后退出,你会在你的 iPhone 上看到推送通知。

Join the Conversation

3 Comments

  1. 对你相当无言 快点看看哪些分布式比较好用组件分享出来吧

  2. When I originally commented I clicked the “Notify me when new comments are added” checkbox and
    now each time a comment is added I get several e-mails with the same comment.
    Is there any way you can remove me from that service? Cheers!

    Take a look at my web-site … Hung

Leave a comment

Your email address will not be published. Required fields are marked *