3

I've got a conceptual question (which is probably better posted here than on StackOverflow?).

I want to develop a client application that maintains a persistent connection to a server, and exchanges text data back and forth. An IRC client is a great example. So, these are givens:

  1. There is a fairly steady stream of incoming text that needs to be interpreted and dealt with in some way by the client.
  2. The client will send commands and it expects specifically formatted responses back from the server.

There are probably quite a few approaches, but I'm looking for (obviously) the most efficient. Should I just manage all incoming text through the "on data received" event?

(pseudo-code)

on(data)
  if data matches /###DAT action1/
    handleAction1
  else if data matches /###DAT action2/
    handleAction2
  else if data matches /###DAT PRIVMSG/
    handlePrivateMessage
  else if data matches /###DAT LOGIN_OK/
    handleConfirmedLogin

So every possible bit of text that I'd want to deal with, is scanned here, whether it's a private message from someone, or a response to a login command I just sent.

Is there a better way to deal with this? If it matters, I'm using JavaScript.

1

It's likely the most efficient approach is to model your message handler as a state machine. Scan character by character and move to an appropriate state. If there's no state transition defined for a character, then the message is invalid. If the new state is final (a complete message), then execute the prescribed action. Until then, keep processing character by character. Basically, it's a message lexer with only one token to identify. (You can always add more, parameters or whatnot, down the road. Lexing will remain fast.) The advantage of this approach is that you only ever touch each character in the message once. In your pseudo-code, if the message is LOGIN_OK, you touch the first character four times before it's resolved.

Let's look at an example. Say our valid messages are:

ACTION_1
ACTION_2
PRIVMSG
LOGIN_OK

Start scanning characters from left to right. If I see an 'L', I immediately know the message must be LOGIN_OK or invalid. There's no need to consider the other cases. Keep scanning to confirm.

If the first character I see is 'A', I know the message must be an action. There's no need to consider PRIVMSG or LOGIN_OK. I need to consume 'A', 'C', 'T', 'I', 'O', 'N', and '_' before I get to a character that resolves the ambiguity. The nice thing is that once I get there, I'm done. Either I know the answer or the message is invalid.

Consider the message ACTION_INVALID in your if/else approach. You have to scan the whole message to check for ACTION_1, then scan the whole message again to check for ACTION_2. Even then, you have to consider the possibility of PRIVMSG or LOGIC_OK. With a state machine/lexer, the first character immediately eliminates two possibilities. To fully resolve the message, you only have to look at eight characters versus scanning the string again and again.

If you decide to go this route, tools like Ragel are useful for generating the state machine versus writing it by hand. There's even a port that generates JavaScript.

  • Thanks for the explanation! Though my question was less about how to parse and more about the general structure of the client app - whether it's acceptable to just lump all the parsing in a single "on data-received" event callback, or if there's some other clever way. It just feels weird dealing with every possible bit of incoming server data in a single event callback. – ffxsam Jan 11 '16 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.