GNU Smalltalk NetClients-based Echo Server

Or: how I stopped thinking "Not Invented Here" and learned to love the library system

My SimpleChat example shows some basics towards creating a TCP server library of your own. This is perhaps useful and interesting, but it actually happens that GNU Smalltalk includes a "NetClients" package which, among other things, includes a TCP server library. I haven't been able to track down any real documentation for the library, but, thanks to Debian and the power of sudo apt-get install gnu-smalltalk the sources are there (in /usr/share/gnu-smalltalk/net) for your anti-confusion needs.

You find that NetServer is a subclass of NetThread, itself directly inherting from Object, and looks a bit like our SimpleTCP.Server class from our "SimpleChat" article. Infinite loop? Check. Abstract "do this when I get another connection"? Check. What next? No check. Well, at least we're not the first people to be confused as to "what next"?

But there's hope.

It turns out that not only does GNU Smalltalk give us this NetClients.NetServer base class, but it gives us an entire httpd implementation based upon it named, appropriately enough, WebServer. (Quick hint: grep "NetServer subclass" *.st FTW.) It still doesn't give us very specific information, but we do get enough hints (such as "You need NetSession objects to go along with your NetServer you fool!")

What the hell. Let's give this a whirl. It turns out that all you "have" to do is:

  1. Create a NetServer subclass that implements newSession (create a session when clients are ready to be accepted) and respondTo (when a session wants a request handled)
  2. Create a NetSession subclass that implements next to create the next request for the accepted socket

That's it. In our first simple case of the "echo server", we're not going to bother creating a special 'request object', instead we'll just use a Dictionary to pass the socket and input message to the server's respondTo method. Without further adieu, here is a simple echo server based on the NetClients package:

PackageLoader fileInPackage: 'NetClients'!

NetClients.NetSession subclass: #EchoSession
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: ''!

NetClients.NetServer subclass: #EchoServer
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: ''!

EchoServer methodsFor: 'session creation'!

  'New session!' printNl.
  ^EchoSession new

EchoServer methodsFor: 'request handling'!

respondTo: aRequest
  | session sessionSocket msg |
  session := aRequest at: 'session'.
  sessionSocket := session socket.
  msg := aRequest at: 'message'.
  msg displayOn: sessionSocket.
  (String with: (Character value: 10)) displayOn: sessionSocket.
  sessionSocket flush

EchoSession methodsFor: 'request creation'!

  | d s msg |
  msg := (self socket) nextLine.
  d := Dictionary new.
  d at: 'session' put: self.
  d at: 'message' put: msg.

| echoServer s |
  echoServer := EchoServer at: 8000.
  echoServer start.
  s := Semaphore new.
  s wait!

Unfortunately there is a good bit of ugly there at the end, as if I just call echoServer start the program quickly ends. So I wait forever on a semaphore that will never be signaled. Ah well. Another thing to figure out someday, I suppose.

Anyway, from here you can get more and more interesting behaviours by expanding on the creation and handling of requests in the above example. It shouldn't be too surprising an endeavor to re-create our chat server example (Hint: maybe the server would keep track of sessions using an instance variable, and maybe the session wouldn't return its first request to the server until it has logged in a new user), and after that, you might be able to dive in and understand what is going on in the WebServer library.

Lastly, as a helpful hint, mostly to myself, this listing was prepared for Markdown format using the command:

sed "s/^/    /"
changed October 2, 2007