I'm trying to implement support for Apple's enhanced Push Notification message format in my Rails app, and am having some frustrating problems. I clearly don't understand sockets as much as I thought I did.
My main problem is that if I send all messages correctly, my code hangs, because socket.read will block until I receive a message. Apple doesn't return anything if your messages looked OK, so my program locks up.
Here is some pseudocode for how I have this working:
cert = File.read(options[:cert])
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = OpenSSL::PKey::RSA.new(cert, options[:passphrase])
ctx.cert = OpenSSL::X509::Certificate.new(cert)
sock = TCPSocket.new(options[:host], options[:port])
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.sync = true
ssl.connect
messages.each do |message|
ssl.write(message.to_apn)
end
if read_buffer = ssl.read(6)
process_error_response(read_buffer)
end
Obviously, there are a number of problems with this:
If I'm sending messages to a large number of devices, and the failure message is sent half way through processing, then I'm not going to actually see the error until I've already tried to send to all devices.
As mentioned earlier, if all messages were acceptable to Apple, my app will hang on the socket read call.
One way I've tried to solve this is by to reading from the socket in a separate thread:
Thread.new() {
while data = ssl.read(6)
process_error_response(data)
end
}
messages.each do |message|
ssl.write(message.to_apn)
end
ssl.close
sock.close
This doesn't seem to work. Data never seems to be read from the socket. This is probably a misunderstanding I have about how sockets are supposed to work.
The other solution I have thought of is having a non-blocking read call... but it doesn't seem like Ruby has a non blocking read call on SSLSocket until 1.9... which I unfortunately cannot use right now.
Could someone with a better understanding of socket programming please point me in the right direction?
cam is correct: the traditional way to handle this situation is with IO.select
if IO.select([ssl], nil, nil, 5)
read_buffer = ssl.read(6)
process_error_response(read_buffer)
end
This will check ssl for "readability" for 5 seconds and return ssl if it's readable or nil otherwise.
Can you use IO.select? It lets you specify a timeout, so you could at limit the amount of time you block. See the spec for details: http://github.com/rubyspec/rubyspec/blob/master/core/io/select_spec.rb
I'm interested in this too, this is another approach, unfortunately with it's own flaws.
messages.each do |message|
begin
// Write message to APNS
ssl.write(message.to_apn)
rescue
// Write failed (disconnected), read response
response = ssl.read(6)
// Unpack the binary response and print it out
command, errorCode, identifier = response.unpack('CCN');
puts "Command: #{command} Code: #{errorCode} Identifier: #{identifier}"
// Before reconnecting, the problem (assuming incorrect token) must be solved
break
end
end
This seems to work, and since I'm keeping a persistent connection, I can without problems reconnect in the rescue code and start over again.
There are some issues though. The main problem I'm looking to solve is disconnects caused by sending in incorrect device tokens (for example from development builds). If I have 100 device tokens that I send a message to, and somewhere in the middle there is an incorrect token, my code lets me know which one it was (assuming I supplied good identifiers). I can then remove the faulty token, and send the message to all devices that appeared after the faulty one (since the message didn't get sent to them). But if the incorrect token is somewhere in the end of the 100, the rescue doesn't happen until the next time I send messages.
The problem seams to be that the code isn't really in real time. If I were to send in, say, 10 messages to 10 incorrect tokens with this code, everything would be just fine, the loop will go through and no problems will be reported. It seems that write() doesn't wait for everything to clear up, and the loops runs through before the connection is terminated. The next time the loop will be run, the write() command fails (since we've actually been disconnected since the last time) and we would get the error.
If there is an alternative way to respond to the failed connection, this could solve the problem.
There is a simple way. After you write your messages, try reading in nonblocking mode:
ssl.connect
ssl.sync = true # then ssl.write() flushes immediately
ssl.write(your_packed_frame)
sleep(0.5) # so APN have time to answer
begin
error_packet = ssl.read_nonblock(6) # Read one packet: 6 bytes
# If we are here, there IS an error_packet which we need to process
rescue IO::WaitReadable
# There is no (yet) 6 bytes from APN, probably everything is fine
end
I use it with MRI 2.1 but it should work with earlier versions too.
Related
I am using Quickfixj 2.3 for initiator. Vendor party is acceptor .
I have implemented SessionStateListener with the methods onConnectException and OnDisconnect.
I have resetOnLogon =Y in configuration file .
How can I catch specific exception like EndOfStream occurred ,due to wrong session data or due to acceptor allows only one session at a time or due to invalid Msg seq ?
Now, when the resetOnLogOn=Y,until the msgSeq satisfies, it keeps internally the disconnecting and initiating. I would like to logout manually in all other disconnects except this situation where it auto matches the seq number .
Thank you .
You actually cannot tell the scenarios listed in point 1 apart most of the time.
E.g. the counterparty normally will not tell you if you have wrong session data (I assume you mean wrong SenderCompID or TargetCompID) because that would disclose information about their system. Same goes for the duplicate session.
Only in the case of a "sequence number too low" event the counterparty normally will send this information in the 58/Text field of the Logout message.
I’m trying to put together some Lua code that will send a command and then capture all the broken up responses that come back from a udp device.
Here is my current code
local udp = socket.udp()
udp:settimeout(0)
udp:setpeername("172.16.0.23", 65432)
local cmd = "$RS232 test message\r"
udp:send(cmd)
repeat
local data, msg = udp:receive()
if data then
print("received:", data)
end
elseif msg ~= 'timeout' then
error("Network error: "..tostring(msg))
end
until not data
But it keeps giving me the following error..
Code error: Line 12: 'until' expected (to close 'repeat' at line 7) near 'elseif'
Any ideas what I’m missing ?
A quick bit of background on the udp (serial rs232) device I’m connecting to, the data is sent as a lot of two or threee byte packets. This is because of the RS232 data rate and the device uses interupts to process the data. Basically the udp device receives a few bytes, interrupt fires, it processes those then receives a few more interrupt fires again, etc etc.
So the above repeat loop is to ensure I have captured everything it’s got for me?
Your if statement is ended prematurely.
if data then
print("received:", data)
end -- remove this end!
elseif msg ~= 'timeout' then
error("Network error: "..tostring(msg))
end
That repeat statement doesn't make too much sense imho. Why don't you use a timeout instead?
If you removed that end you will receive once with timeout 0. As your peer never had a chance to respond data will be nil and your repeat loop will terminate.
Maybe give the documentation another read.
Also I don't understand how you relate udp to rs232. That's two completely different things.
After logon, i would like to send a chat message to the Guild channel.
I'm currently listening for events:
PLAYER_ENTERING_WORLD
GUILD_ROSTER_UPDATE
Once those have fired (in order), i'd like to send a chat message. However, it never sends.
Code:
print("Should_send")
SendChatMessage(msgToSend, "GUILD");
It's also worth noting that if i then trigger this manually, it works.
I do see the "Should_send" print statement appearing in the default chat window each time - as expected. I've also checked that "msgToSend" contains content - and is less than 255 characters.
So, when can i call SendChatMessage?
Ok, in order to be able to send a chat message to guild, you need to wait for the event "CLUB_STREAM_SUBSCRIBED" to fire.
This is due to the Guild channel becoming a "community" channel of sorts - previously, it seems this wasn't required.
So, adding an event listener:
frame:RegisterEvent("CLUB_STREAM_SUBSCRIBED");
Resolves the issue.
You will likely need to set a flag for the event, then print later on another event.
You can send chat messages any time after you see the welcome message or after the welcome message was posted. Which is pretty soon after you able to receive events from your frames.
Here is what I would do to complete a similar mission:
Just put your send code in a macro to test it first. Don't worry about timing the message until you see it work in a macro.
You can make your own print to send generic messages to the chat window which should always work similar to:
function MyPrint( msg, r, g, b, frame, id)
(frame or DEFAULT_CHAT_FRAME):AddMessage(msg, r or 1, g or 1, b or 0, id or 0)
end
-- put these in your event handlers
MyPrint("event PLAYER_ENTERING_WORLD")
MyPrint("event GUILD_ROSTER_UPDATE")
And use that for debugging instead.
You need to divide and conquer the problem, because there are so many things that could be wrong causing your issue, no one here can really have a definitive answer.
I know for sure that if you try to write to chat before the welcome message with print it at least used to not work. I remember spooling messages in the past until a certain event had fired then printing them.
I'm facing problem with TCpindy connection.readln method , I had no control in the other side sending data , when using Readln method in server side application hang (because receiving data don't contain carrige return ) , i'm trying readstring method but without success
Is there any suggestion to encouter this problem , me be looking for other component rather than indy ,
I need to get data from other client (tcp connection ) without any information about size of receiving data and without carriage return at the end of each frame.
You have to know how the data is being sent in order to read it properly. TCP is a byte stream, the sender needs to somehow indicate where one message ends and the next begins, either by:
prefixing each message with its
length
putting unique delimiters in between
each message
pausing in time between each message
Indy can handle all of these possibilities, but you need to identify which one is actually being used first.
Worse case scenerio, use the CurrentReadBuffer() method, which returns a String of whatever raw bytes are available at that moment.
FATAL ERROR: CALL_AND_RETRY_2 Allocation Failed - process out of memory
I'm seeing this error and not quite sure where it's coming from. The project I'm working on has this basic workflow:
Receive XML post from another source
Parse the XML using xml2js
Extract the required information from the newly created JSON object and create a new object.
Send that object to connected clients (using socket.io)
Node Modules in use are:
xml2js
socket.io
choreographer
mysql
When I receive an XML packet the first thing I do is write it to a log.txt file in the event that something needs to be reviewed later. I first fs.readFile to get the current contents, then write the new contents + the old. The log.txt file was probably around 2400KB around last crash, but upon restarting the server it's working fine again so I don't believe this to be the issue.
I don't see a packet in the log right before the crash happened, so I'm not sure what's causing the crash... No new clients connected, no messages were being sent... nothing was being parsed.
Edit
Seeing as node is running constantly should I be using delete <object> after every object I'm using serves its purpose, such as var now = new Date() which I use to compare to things that happen in the past. Or, result object from step 3 after I've passed it to the callback?
Edit 2
I am keeping a master object in the event that a new client connects, they need to see past messages, objects are deleted though, they don't stay for the life of the server, just until their completed on client side. Currently, I'm doing something like this
function parsingFunction(callback) {
//Construct Object
callback(theConstructedObject);
}
parsingFunction(function (data) {
masterObject[someIdentifier] = data;
});
Edit 3
As another step for troubleshooting I dumped the process.memoryUsage().heapUsed right before the parser starts at the parser.on('end', function() {..}); and parsed several xml packets. The highest heap used was around 10-12 MB throughout the test, although during normal conditions the program rests at about 4-5 MB. I don't think this is particularly a deal breaker, but may help in finding the issue.
Perhaps you are accidentally closing on objects recursively. A crazy example:
function f() {
var shouldBeDeleted = function(x) { return x }
return function g() { return shouldBeDeleted(shouldBeDeleted) }
}
To find what is happening fire up node-inspector and set a break point just before the suspected out of memory error. Then click on "Closure" (below Scope Variables near the right border). Perhaps if you click around something will click and you realize what happens.