(If you do not see the frame version of this page with a table of contents in the left frame, click here.)

The farVIEW Communications Library

Introduction

The multi-threaded Comm library included in the farVIEW engine provides a flexible TCP/IP interface for a farSlang module. You can use the library to access web pages on the internet. You can also use it to implement a web server, a Remote-Procedure-Call (RPC) server, or any other kind of server you want. The Comm library supports messages using the HTTP protocol. I'll provide a short description of that later in this document, and provide links to the primary document site. The thread management aspects of the library are transparent to the farSlang module.
 

The Comm Library

The library contains four main classes: Of these classes, you use only four functions in the CComm interface class to control the whole library.

There are several support classes, mostly acting as wrappers to simplify the communication between the farSlang module thread and the session threads. One special support class is CCommResult. Objects of this class are passed by the farSlang module with a communication request, and are used by the CSession thread to synchronize and communicate results back to the farSlang caller. I will discuss this further later in this document. The other support classes are not used by farSlang.
 

The Comm Interface

You do not interact directly with CSession, CListen, or CSocket. Instead, you use the CComm class. The CComm class provides six simple interface functions to control the Comm library: To properly control the Comm package, you open a session, send and receive messages as needed, then you close the session. When you open a session, you associate a unique identifier with that session. (Use the uniqueID library function to obtain a unique identifier.) Use the session identifier in all subsequent commands to that CSession object to identify it to the CComm interface.
 

OpenComm?(result: CCommResult)

Since this command should never be used by a farSlang module, I will comment no further.
 

CloseComm?(result: CCommResult)

As above.
 

OpenSession?(sessionID$, hostIP$, hostPort#, userName$, result: CCommResult)

TBD

   System.Comm.OpenSession("mySession", "www.mozilla.org", 80, "", result)
 

CloseSession?(sessionID$, result: CCommResult)

TBD

   System.Comm.CloseSession("mySession", result)
 

SendMessage?(sessionID$, mssgType$, command$, argument$, bodyType$, body: CAtom, life: disposition, result: CCommResult)

TBD

   System.Comm.SendMessage("mySession", "HTTP", "GET", "", "", nil, keepAlive, result)
 

ReceiveMessage?(sessionID$, result: CCommResult)

TBD

   System.Comm.ReceiveMessage("mySession", result)
 

Basic Operation

The basic actions taken when you call a CComm function are as follows:
  1. Your code creates a CCommResult object, specifying synchronous or asynchronous behavior, and passes it, along with specific request parameters to the appropriate CComm function.
  2. The CComm function wraps the request parameters within a container object and enqueues that object into the specified CSession object’s activity request port. The CComm function then waits on the CCommResult object. When it regains control, it will return to your code. If you secified asynchronous behavior, the wait step will not wait.
  3. The specified CSession object dequeues the request from its activity request port, processes the request, then releases the CCommResult object.
If you specify synchronous behavior, the CComm function will wait until the requested action is completed before returning control to you. This is recommended. If you specify asynchronous behavior, the CComm function will return control to you immediately, where it becomes your responsibility to determine when the action has completed. This is not recommended because you are tying up the machine while you are in the test loop. When you  create a CCommResult object, the default is for synchronous behavior.

Let's examine a simple example. For this example, we will request a web page from www.wdj.com (Windows Developers Journal). That  is about the simplest thing to do. The result will be an HTML document that includes links to obtain the images. We won't resolve the links, since we would have to discuss HTML parsing, which is well beyond the scope of this article. We will connect to www.wdj.com on port 80, which is the normal HTTP server port, send a GET request to the server, receive the server's response, and disconnect from the server.
 

Connecting to a Host

Obtain a unique identifier, then open the session. Retain the sessionID for subsequent calls.
 
-- The CComm object is in the System global
  glb System: CSystem

  -- Get a session identifier
  var sessionID := UniqueID

  -- Create a CCommResult object
  var result := CCommResult -- defaults to wait

  -- Open a session
  System.Comm.OpenSession(sessionID, 
                   "http://www.wdj.com", 80, "", result)

Note that error checking is omitted from the examples for clarity. In normal coding, you should include error checking, and we will discuss it later in this document.
 

Sending a Message to a Host

Use the sessionID created above along with the content of the message to send in this call.
 
   -- Send a request to an HTTP server
   var body: CAtom -- no body for a "GET"
   System.Comm.SendMessage(sessionID, 
                    "HTTP",
                    "GET",
                    "",
                    "",
                    body,
                    keepAlive,
                    result)

The SendMessage command has the most parameters.
 

Receiving a Message from a Host

Continue to use the same sessionID to request to receive a message
 
   -- Receive the server's response
   System.Comm.ReceiveMessage(sessionID, result)

   -- The content of the server's response
   -- is in the result object.

   -- Obtain the header list and
   -- write it to a file
   result.GetHead.WriteFile("head.txt")

   -- Obtain the body (content) and
   -- write it to a file
   result.GetBody:CStrList.WriteFile("body.txt")

Disconnecting from a Host

Disconnect the session from the host
 
   -- Disconnect from the server
   System.Comm.CloseSession(sessionID, result)

Acknowledging  a Guest

Of course, there is more to acknowledging the guest's request than just the ACK-message, but let's just send the text of the guest's message back to her for now.
 
-- Send the body-part of the guest's
-- message back to the guest.
glb System: CSystem
System.Comm.SendMessage(System.SessionID,
                        "ACK",
                        "",
                        "200 OK",
                        "",
                        System.CommBody,
                        keepAlive,
                        result);

Notes

When implementing a connection between two farVIEW computers, you need to know either the URL of the host machine or its IP address. You can find its IP address by opening a Windows Command Window (MSDOS), and typing ipconfig at the prompt. You will see something like the following.
 
C:\WINDOWS>ipconfig

Windows 98 IP Configuration

0 Ethernet adapter :

        IP Address. . . . . . . . . : 192.168.0.1
        Subnet Mask . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . :

1 Ethernet adapter :

        IP Address. . . . . . . . . : 204.255.212.233
        Subnet Mask . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . : 204.255.212.233

2 Ethernet adapter :

        IP Address. . . . . . . . . : 0.0.0.0
        Subnet Mask . . . . . . . . : 0.0.0.0
        Default Gateway . . . . . . :

C:\WINDOWS>

192.x.x.x is a local IP address, which is what you should use if you are working on a LAN. If you are setting up for Internet access, however, that one won't work. In my case, the Internet IP address is 204.255.212.233. However, because, my ISP assigns a new IP address to my machine everytime I log on, that one won't work generally either. In fact, unless you have a DSL or Cable connection, you may not have a permanent IP address assignment for your machine. That pretty much eliminates that machine as a farVIEW host, unfortunately, except for just the immediate session.
 

Building an HTTP Server

TBD
 

About the HTTP Protocol

TBD
 

Implementing Remote Procedure Calls

The farVIEW engine can be used as a portal for implementing remote procedure calls fairly easily. For this discussion, I will show how to write a simple farSlang server and a farSlang client to do something trivial.

Let's ask a server to double a value provided by a client. The client, for its part, will show a dialog to obtain the value, send the value to the server, then display the result when it is returned.
 

The Server

-- Sample RPC server module in farSlang to double the value in the body
-- of the message and return it.
--
-- Note that the server code must reside within a proc, in this case,
-- proc double. Also note that the SendMessage and CloseSession
-- commands used in this code are not immediately carried out but are
-- enqueued into the inPort of the same thread in which this module
-- executes, and cannot execute until this module completes. Therefore,
-- the module code must not attempt to wait until the commands are
-- completed, since if it does, they will never even be started.

module RPCServer
   glb System: CSystem
   var result := CCommResult(false)        -- don't wait for completion

   proc double
      -- Get the value, double it, and replace the old value
      System.CommBody:CStrList.GotoHead
      var val# := integer(System.CommBody:CStrList.StrItem(true))
      System.CommBody:CStrList.StrReplace(string(2 * val, 10))

      -- Return the result to the RPC client and shut down
      System.Comm.SendMessage(System.SessionID,
                              "ACK",
                              "200 OK",
                              "",
                              "text/plain",
                              System.CommBody,
                              closeConnect,
                              result)
      System.Comm.CloseSession(System.SessionID, result)
      endProc double

   endModule RPCServer

The Client

For the purposes of this discussion, you can test on a single machine by using the standard 127.0.0.1 IP address. That way, your farVIEW will act as both the client and the server.
 
-- Sample RPC client module in farSlang to get a value from the user,
-- send it to the sample server to double the value then display the
-- value to the user.

module RPCClient

   proc getValueFromServer$(hostURL$, server$, value$)

      glb System: CSystem
      var body := CStrList

      body.StrInsert(value, atEnd)

      var result    := CCommResult
      var sessionID := UniqueID

      -- Open a session with the specified host
      System.Comm.OpenSession(sessionID, hostURL, 8000, "", result)

      -- Send the request
      System.Comm.SendMessage(sessionID,
                              "FAR",
                              "FP",
                              server,
                              "text/plain",
                              body,
                              keepAlive,
                              result)

      -- Receive the result
      System.Comm.ReceiveMessage(sessionID, result)

      -- Shut the session down
      System.Comm.CloseSession(sessionID, result)

      -- Extract the result and return it
      body := result.GetBody:CStrList
      body.GotoHead
      return body.StrItem(true)
      endProc getValueFromServer

   -- Display a Accept dialog to get a number
   var accept := CAcceptDialog(nil)
   accept.SetEntry("Enter Number:", "", "A", 64)
   accept.Accept("RPC Sample Client")
   var value := accept.GetAcceptValue("Enter", true)

   -- Send it to the server and get the result
   value := getValueFromServer("127.0.0.1", "RPCServer.double", value)

   -- Display the result to the user
   CMessageBox("The host said...", value).Run
   endModule RPCClient

Notes

The sample code does not check for errors.

Note that the server parameter value in the SendMessage command in the client identifies the farSlang module (RPCServer.double) to execute as the server.

You can easily execute a Windows program remotely. For example, to execute Word remotely, add the following lines to the server module.

   var launch := CLaunch
   launch.LaunchChild("Word")
   repeat until launch.IsChildDone

You will find this code in RPCLaunch.far.

The modules shown in this document are in the farSlang library in your release. Look for RPC*.far.
 

Accessing a Remote farBook

TBD
 

Handling Errors

TBD