Computer Graphics Workshop '96 Lab Notes | 1/25/96 |
Documentation for SocketMan is located in
Example code for some multicasting programs is located in
These programs' listings are also shown below.
The SocketMan classes (SocketMan and SocketClient) work by sending data structures known as SocketBufs amongst themselves. A SocketBuf is a very simple C++ class, of which we will be using only two parts.
The first part is the setBufFromString method. This method takes a Scheme string as argument and sets the internal buffer buf of the SocketBuf to be this string. For example, to create a new SocketBuf which contains the string "Hello world", we could do the following:
> (define my-sbuf (new-SocketBuf)) > (-> my-sbuf 'setBufFromString "Hello world")The next part is the buf field. Note that this is not an Inventor-style field; when you send the buf message to a SocketBuf, it returns the string which the object contains. For example, to extract the string from the above object, we could do:
> (define my-string (-> my-sbuf 'buf)) > my-string "Hello world"By using some built-in Scheme functions, and some provided by the Scheme Library, we can send Scheme objects over the network in the form of strings. To enable this functionality, put the following two lines in any Scheme code which needs network support:
(require 'object->string) (require 'string-port)The object->string procedure takes a Scheme object and prints it into a string, returning this string as its value. This function will not have much meaning for Inventor objects, since they print their memory locations as their values; in order to send information between Scheme interpreters, you must use object->string on a native Scheme data type such as a list, vector, or number.
As an example, to put the string representing the Scheme vector of #(1.0 4.0 9.0) into a SocketBuf, we would do the following:
(define my-vec '#(1.0 4.0 9.0)) (define my-string (object->string my-vec)) (define my-sbuf (new-SocketBuf)) (-> my-sbuf 'setBufFromString my-string)We can then send this SocketBuf using the methods described below.
On the receiving end, we would like to convert the data in a SocketBuf back into a Scheme object. We can do this using the standard Scheme read command, combined with the Scheme Library's string-port extension:
(define the-new-string (-> my-sbuf 'buf)) (define the-new-object (call-with-input-string the-new-string read))the-new-object would, in our above example, now be the vector from the other end. We can now access its values in the standard way:
> (vector-ref the-new-object 1) 4.0The two primary networking functions which we will be using (and which are present in both SocketMan and SocketClient) are readWithPollAndTimeout and writeWithTimeout. The former takes no arguments, and attempts to read in a SocketBuf from the network; the latter takes a SocketBuf as argument, and attempts to write it to the network. It returns a status code, which, at least for now, can be ignored.
The SocketMan class handles connections to multiple clients transparently. The acceptConnectionWithPoll method takes no arguments and receives a connection from a client, if one is attempting to connect. Connections are automatically closed when the client disconnects. The rewindSockets and nextSocket methods allow iteration over all connected clients. See the code in
for an example of iterating over sockets. client.scm and server.scm in this directory implement a client-server pair. The connect-and-send function connects a SocketClient to a server and sends a message. Both the server and message are strings; for example:
(connect-and-send "m4-034-18" "Hi there!")
Examples of multicasting code are in
The cone-sender program opens an examiner viewer with a cone inside a handle box manipulator. Dragging the cone around the scene causes the cone in any cone-receiver's window to follow the motion of the sender's cone. This program demonstrates sending Scheme vectors over the network.
The mclooper and mcsender programs demonstrate the ability to send differently typed data over the network, encapsulated within strings. The send-string function inside mcsender sends a string over the network; the send-SbVec3f function sends the contents of an SbVec3f. The mclooper program defines a loop-infinite procedure which, when run, simply waits for data sent by the mcsender functions and displays any data read in. Note that this is implemented poorly; the "right" way of performing this task is inside an idle callback, as is shown in the cone programs.
(define client (new-SocketClient 10732)) (define sbuf (new-SocketBuf)) (define (connect-and-send server message) (-> client 'closeConnection) ;; if necessary, ;; close current connection (-> client 'connectToServer server) ;; open new connection (-> sbuf 'setBufFromString message) ;; set message (-> client 'writeWithTimeout sbuf) ;; send it )
(define server (new-SocketMan 10732)) (define (sub-loop) (if (= 1 (-> server 'nextSocket)) (begin (let ((sbuf (-> server 'readWithPollAndTimeout))) (if (not (equal? sbuf (SocketBuf-cast (void-null)))) (begin (display (-> sbuf 'buf)) (newline)))) (sub-loop)))) (define (loop-once) (-> server 'acceptConnectionWithPoll) (-> server 'rewindSockets) (sub-loop)) (define (loop-semiinfinite times) (if (> times 0) (begin (loop-once) (loop-semiinfinite (- times 1))))) (define (loop-infinite) (loop-once) (loop-infinite))
(require 'string-port) (define sc (new-SocketClient 10666)) (-> sc 'setUsingMulticast 1) (-> sc 'connectToServer "224.6.6.6") (define (loop-infinite) (let ((sbuf (-> sc 'readWithPollAndTimeout))) (if (not (equal? sbuf (SocketBuf-cast (void-null)))) (let ((obj (call-with-input-string (-> sbuf 'buf) read))) (if (vector? obj) (begin (display "Vector values: ") (for-each (lambda (i) (display i) (display " ")) (vector->list obj)) (newline)) (begin (display (-> sbuf 'buf)) (newline)))))) (loop-infinite))
(require 'string-port) (require 'object->string) (define sc (new-SocketClient 10666)) (-> sc 'setUsingMulticast 1) (-> sc 'connectToServer "224.6.6.6") (define send-sbvec3f (let ((sbuf (new-SocketBuf))) (lambda (the-sbvec) (let* ((the-vec (-> the-sbvec 'getValue)) (the-string (object->string the-vec))) (-> sbuf 'setBufFromString the-string) (-> sc 'writeWithTimeout sbuf))))) (define send-string (let ((sbuf (new-SocketBuf))) (lambda (the-string) (-> sbuf 'setBufFromString the-string) (-> sc 'writeWithTimeout sbuf))))
(require 'object->string) (load "cone-network") (define viewer (new-SoXtExaminerViewer)) (-> viewer 'show) (define root (new-SoSeparator)) (-> root 'ref) (define mat (new-SoMaterial)) (-> (-> mat 'diffuseColor) 'setValue 0.2 0.8 0.2) (-> root 'addChild mat) (define manip (new-SoHandleBoxManip)) (-> root 'addChild manip) (-> root 'addChild (new-SoCone)) (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue "field-changed-cb") (define field-sensor (new-SoFieldSensor (get-scheme-sensor-cb) (void-cast cb-info))) (-> field-sensor 'attach (-> manip 'translation)) (define field-changed-cb (let ((sbuf (new-SocketBuf))) (lambda (user-data sensor) (let* ((vec (-> (-> (-> manip 'translation) 'getValue) 'getValue)) (the-string (object->string vec))) (-> sbuf 'setBufFromString the-string) (-> sc 'writeWithTimeout sbuf))))) (-> viewer 'setSceneGraph root)
(require 'object->string) (require 'string-port) (load "cone-network") (define viewer (new-SoXtExaminerViewer)) (-> viewer 'show) (define root (new-SoSeparator)) (-> root 'ref) (define mat (new-SoMaterial)) (-> (-> mat 'diffuseColor) 'setValue 0.2 0.8 0.2) (-> root 'addChild mat) (define xform (new-SoTransform)) (-> root 'addChild xform) (-> root 'addChild (new-SoCone)) (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue "idle-cb") (define idle-sensor (new-SoIdleSensor (get-scheme-sensor-cb) (void-cast cb-info))) (-> idle-sensor 'schedule) (define (idle-cb user-data sensor) (-> sensor 'schedule) (let ((sbuf (-> sc 'readWithPollAndTimeout))) (if (not (equal? sbuf (SocketBuf-cast (void-null)))) (let ((the-vec (call-with-input-string (-> sbuf 'buf) read))) (-> (-> xform 'translation) 'setValue the-vec))))) (-> viewer 'setSceneGraph root)
(define *cone-sender-port* 13742) (define *cone-sender-address* "224.4.8.16") (define sc (new-SocketClient *cone-sender-port*)) (-> sc 'setUsingMulticast 1) (-> sc 'connectToServer *cone-sender-address*)
$Id: index.html,v 1.4 1996/01/29 00:03:31 kbrussel Exp $