Posts Tagged ‘asynchronous networking’

A little while ago we were talking about clients and servers. And I’ve finally got back to the interesting thing which is playing with emacs networking.

The comment from Gabriel nicely pointed out emacs’ low-level socket facilities.

(open-network-stream) which is synchronous and
(make-network-process) which can be asynchronous if the parameter is set. I haven’t been able to make the last one work, but check it out yourself.

So, er, I’m not 100% sure how to simulate a crappy internet connection. Maybe making my server pause 2 seconds between line reads will be sufficient.

sub process_request
    while () {
        print STDERR "Received [$_]\n";
        last if /quit/i;
        sleep 2;

I’m also interested (or perhaps curious?) in when the connection is made and broken.

sub post_accept_hook
    print STDERR "Accepted new connection\n";

sub post_process_request_hook
    print STDERR "Client disconnected\n";

Did I mention I think Perl’s bad press is a little unfair?

So what I need now is a basic API for connecting, disconnecting and sending messages. make-network-process makes a process as you can probably infer from the name and for my purposes a single client is enough so I make a wrapper to get the current process. Layering in this way means I can add any error handling at the appropriate point later on. And also it should be fairly easy to generalise client-process to choose between different connections.

(defconst client-process-name "*client*")
(defsubst client-process () (get-process client-process-name))

For maximum asynchronicity we add a sentinel function that will be called back when the connection has completed, either successfully or unsuccessfully.

It turns out that a perl-style chomp function is really handy!

(defun chomp (str)
  (if (and (stringp str) (string-match "\r?\n$" str))
      (replace-match "" t nil str)

(defun client-notify-connect (&rest args)
  (message (format "Connection message [%s]" (mapcar #'chomp args))))

I added a fairly hacky (sit-for 1) after opening the client as otherwise I could send messages before it was ready due to the sentinel and nowait. Future versions will probably get the sentinel to set an "opened" variable which will determine whether the send message and close functions work.

(defun client-open (host port)
  (make-network-process :name client-process-name
                        :host host
                        :service port
                        :nowait t
                        :sentinel #'client-notify-connect)
  (sit-for 1))

Closing the connection is a simple matter of deleting the process. I confirmed this against my test perl server.

(defun client-close ()
  (delete-process (client-process)))
(defun client-send-string (str)
  (process-send-string (client-process) (concat str "\r\n")))

My test program simply sends 10 hello messages to the server at the same time as inserting 10 hellos in the buffer. The fact that the buffer insertions completed almost instantaneously whereas the server took around 20 seconds to display the messages indicates there is some degree of separation.

  (client-open 'local 8080)
  (dotimes (var 10)
    (client-send-string (format "Hello %s" var))
    (insert (format "Hello %s\n" var)))

Next time I’ll look at setting up a filter to receive messages that the server sends back. Any questions or comments let me know.

If you liked this post, why not subscribe to my RSS feed.


Read Full Post »