I've got a ruby script that opens a connection to Apple's push server and sends all the pending notifications. I can't see any reason why, but I get broken pipe errors when Apple disconnects my script. I've written my script to accomodate this happening, but I would rather just find out why it's happening so I can avoid it in the first place.
It doesn't consistently disconnect on a specific notification. It doesn't disconnect at a certain byte transfer size. Everything appears to be sporadic. Are there certain limitations to the data transfer or payload count you can send on a single connection? Seeing people's solutions that hold one connection open all the time, I would assume that isn't the issue. I've seen the connection drop after 3 notifications, and I've seen it drop after 14 notifications. I've never seen it make it past 14.
Has anyone else experienced this type of problem? How can this be handled?
The problem was caused by sending an invalid device token to the APNS server. In this specific case it was a development token. When an invalid device token is sent to APNS, it disconnects the socket. This can cause some headaches, and has been addressed by Apple as being something they are going to address in future updates.
I had the same issue for a bit and did two things to tackle it:
Here's my current connection code, using EventMachine:
module Apns
  module SocketHandler
    def initialize(wrapper)
      @wrapper = wrapper
    end
    def post_init
      start_tls(:cert_chain_file => @wrapper.pem_path,
                :private_key_file => @wrapper.rsa_path,
                :verify_peer => false)
    end
    def receive_data(data)
      @wrapper.read_data!(data)
    end
    def unbind
      @wrapper.connection_closed!
    end
    def write(data)
      begin
        send_data(data)
      rescue => exc
        @wrapper.connection_error!(exc)
      end
    end
    def close!
      close_connection
    end
  end
  class Connection
    attr_reader :pem_path, :rsa_path
    def initialize(host, port, credentials_path, monitoring, read_data_handler)
      setup_credentials(credentials_path)
      @monitoring = monitoring
      @host = host
      @port = port
      @read_data_handler = read_data_handler
      open_connection!
    end
    def write(data)
      @connection.write(data)
    end
    def open?
      @status == :open
    end
    def connection_closed!
      @status = :closed
    end
    def connection_error!(exception)
      @monitoring.inform_exception!(exception, self)
      @status = :error
    end
    def close!
      @connection.close!
    end
    def read_data!(data)
      @read_data_handler.call(data)
    end
    private
    def setup_credentials(credentials_path)
      @pem_path = "#{credentials_path}.pem"
      @rsa_path = "#{credentials_path}.rsa"
      raise ArgumentError.new("#{credentials_path}.pem and #{credentials_path}.rsa must exist!") unless (File.exists?(@pem_path) and File.exists?(@rsa_path))
    end
    def open_connection!
      @connection = EventMachine.connect(@host, @port, SocketHandler, self)
      @status = :open
    end
  end
end
end end
It separates writes and reads in the connection, using the ID field in the notification to correlated notifications I send with feedback I receive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With