Thursday, February 16, 2012

Serving Sockets... The Soup

Alright, first thing first. Announced during yesterday's Episode 2 of Dartisans Hangout, Dartium binaries are being relased. Initially for Mac OS X and Linux with Windows binaries to follow soon. I won't go on about this much as it has already been mentioned again and again, and again.

One thing to note however, is that currently the Linux Build is a 32-bit version so if you're running a 64-bit OS you will need to ensure you have the proper 32-bit libraries downloaded and installed. Or alternatively, wait for the 64-Bit builds to arrive.

Continuing with my small series on Sockets in the Dart:IO library, I thought well I have an EchoServer written, albeit extremely primitive, my next 'logical' step is to make a client which connects to the server, sends a string, receives the echo response and disconnects. This should be pretty straight forward, since it was so easy to make the server... Oh boy was I ever wrong about that. Not that Sockets are overly complex or anything, they just have many more ways of accomplishing the same thing, but they can't be used in conjunction as I found out with the following:

#import("dart:io");

void main() {
  // Create a new socket connecting to localhost and port 5700
  // the same port as our echo server we wrote is running on.
  Socket conn = new Socket("127.0.0.1", 5700);
  StringInputStream inputStr;
  OutputStream outputStream;
  
  // method is called when connection is established.
  conn.connectHandler = () {
    print("Now Connected");
    inputStr = new StringInputStream(conn.inputStream);
    outputStream = conn.outputStream;
    
    String test = "This is a simple test\n";
    outputStream.write(test.charCodes());
    print("Sending: $test");
    
    // We wrapped the input stream for this easier reading
    // using the lineHandler as opposed to bulk data.
    inputStr.lineHandler = () {
      String input = inputStr.readLine();
      print("Recieved: $input\n");
    };
  };
  
  // Called when the last byte of data has been read from the socket.
  // Socket potentially still open for writing.
  conn.closeHandler = () {
    print("Connection closed. Last byte of data has been read from stream");
    conn.close();
  };
  
}
Now Connected
Unhandled exception:
StreamException: Cannot get input stream when socket handlers are used
 0. Function: '_Socket@14117cc4.get:inputStream' url: 'dart:io' line:4231 col:9
 1. Function: '::function' url: 'src/dart/echo/EchoClient.dart' line:13 col:54
 2. Function: '_Socket@14117cc4.firstWriteHandler' url: 'dart:io' line:4274 col:64
 3. Function: '_SocketBase@14117cc4._multiplex@14117cc4' url: 'dart:io' line:3898 col:23
 4. Function: '_SocketBase@14117cc4.function' url: 'dart:io' line:3996 col:59
 5. Function: 'ReceivePortImpl._handleMessage@924b4b8' url: 'bootstrap_impl' line:1734 col:22

So as we can see, this didn't work quite as cleanly as we'd have liked. Apparently the issue above is with my mixing the Socket handlers, such as connectHandler and closeHandler, while also pulling the socket's input and output streams themselves. In the case of the InputStream wrapping it in a StringInputStream handler to make dealing with input a little easier since we only expect text.

Initially I thought this error was telling me that I cannot use any socket handlers, if I wanted to pull the IO streams directly from the Socket. However I only just now stumbled upon an easier solution. The error isn't with the use of connectHandler and closeHandler on the socket. Apparently only the closeHandler is triggering the error. So I can move that method to be called on the StringInputStream instead. So a quick re-write gives me the following:

#import("dart:io");

void main() {
  // Create a new socket connecting to localhost and port 5700
  // the same port as our echo server we wrote is running on.
  Socket conn = new Socket("127.0.0.1", 5700);
  StringInputStream inputStr;
  OutputStream outputStream;
  
  // method is called when connection is established.
  conn.connectHandler = () {
    print("Now Connected");
    inputStr = new StringInputStream(conn.inputStream);
    outputStream = conn.outputStream;
    
    String test = "This is a simple test\n";
    outputStream.write(test.charCodes());
    print("Sending: $test");
    
    // We wrapped the input stream for this easier reading
    // using the lineHandler as opposed to bulk data.
    inputStr.lineHandler = () {
      String input = inputStr.readLine();
      print("Recieved: $input\n");
    };

    // Called when the last byte of data has been read from the socket.
    // Socket potentially still open for writing.
    inputStr.closeHandler = () {
      print("Connection closed. Last byte of data has been read from stream");
      conn.close();
    };
  };
  
}
Now Connected
Sending: This is a simple test

Recieved: THIS IS A SIMPLE TEST

Connection closed. Last byte of data has been read from stream

Well then, that's working so much better. And in fact I could leave it here.. and will for the time being. However later, perhaps tomorrow, I will write a continuation of this post on some of the other methods of using and communicating with sockets. They are plentiful.

No comments:

Post a Comment