Networking API for Tcl

 < Day Day Up > 



Tcl includes a very simple Sockets layer that provides support for TCP stream sockets. We’ll intermix the Tcl core Sockets API with the Tcl-DP API, but the Tcl-DP API will be apparent by the dp_ prefix that precedes all Tcl-DP commands.

The Tcl Sockets API is essentially one function, socket, but this function returns a channel identifier that can then be used for I/O operations. We introduce these as socket commands for discussion, but it should be noted that they are not actually part of Tcl’s Sockets API. The list of commands to be covered for Tcl are shown in Figure 14.1.

start figure

CommandDescription
socket [options] host portCreate a client socket and connect to the peer or create a server socket and await connections.
fconfigure channel name valueConfigure an option on a given channel.
read channel [numChars]Read from a channel.
gets channel [varname]Read from a channel.
puts channel [varname]Write to a channel.
close channelClose a channel.
flush channelFlash output for a channel.
fileevent channel [mode] scriptPerform an action when an event has occurred.
select rd [wr] [ex] [timeout]Perform a select operation on a list of socket descriptors with an optional timeout.

end figure

Figure 14.1: Tcl core socket commands.

The Tcl-DP package includes a number of functions that extend the capabilities of the Tcl core Sockets API. This includes multicast and datagram support in addition to other commands. The Tcl-DP package also includes support for RPC, which is not covered here. The list of commands that are covered for the Tcl-DP API are shown in Figure 14.2.

start figure

CommandDescription
dp_connect channel argsCreate a TCP, UPD or multicast client or server socket (channel).
dp_accept channelAccept a new TCP connection from a TCP channel.
dp_recv channelRead from a channel.
dp_send channel stringWrite to a channel.
dp_netinfo option argsGet address or service info.

end figure

Figure 14.2: Tcl-DP sockets commands.

This is obviously different from the standard BSD API, but as we’ll soon see, Tcl provides a complete interface that allows the construction of very complex Sockets applications.

Sockets API Discussion

Let’s now walk through the Tcl command and the Tcl-DP package and illustrate how the commands are used.

In Tcl, communication takes place over a channel, so you’ll see the word socket and channel used here interchangeably. Tcl provides a number of commands that operate on channels, so if you have socket, the command can be used on it as a channel.

Creating, Destroying, and Connecting Sockets

We’ll begin by investigating how Tcl creates and destroys local sockets. Recall that Tcl provides not only socket creation but also connection within the command API. Therefore, in this section, we cover not only socket creation and destruction, but also connection. We’ll first look at the Tcl socket core command and then the Tcl-DP dp_connect command.

The socket command within Tcl provides for socket creation and connection, depending upon the type of socket being created (client or server). Let’s look at the formal arguments for the socket command and then dissect it using a number of examples.

The Tcl socket command for clients has the following format:

 socket [options] host port

In this basic case, the client socket command connects to the host and port and then returns a channel identifier. An example of this is:

set clisock [ socket "192.168.1.2" 52000 ]

which creates a client socket, and then connects it to the host identified by IP address “192.168.1.2” and port 52000. The resulting socket (channel identifier) is then stored into variable clisock. Note that the brackets shown ([]) mean that this command should be invoked, with the result being a string (the only data type available in Tcl is the string).

The client version of the socket command also supports a number of options, which are shown in the following code in italics:

 socket –myaddr addr –myport port –async host port

The first option ‘-myaddr addr’ allows us to specify the host or IP address of the local socket. This option is rarely used for client sockets. The second option ‘-myport port’ allows us to specify the port number that we want to bind to for the local socket. Again, this option is rarely used (typically, only servers perform this). Finally, the ‘-async’ option allows us to perform the peer connect asynchronously. This means that the socket command will return immediately with the client socket channel identifier, but the socket may not have yet connected to the peer. When the client application attempts to use the socket for a read or write operation (gets or puts), an error may occur identifying that the connect operation has not yet completed.

Let’s now look at a full example of the client connect, using the options available to us:

set clisock [ socket –myaddr 10.0.0.1                    –myport 25000 –async 10.0.0.2 25001 ]

In this example, we create a client socket and bind it locally to the interface identified by IP address “10.0.0.1” and port 25000. We also specify that the connect operation should occur asynchronously by the –async option. The client socket should connect to the host identified by IP address “10.0.0.2” and port 25001. Because –async was specified, the call will complete immediately, but the socket will not be usable immediately for gets and puts commands.

Recall that binding an address to a client socket restricts outgoing connections to that interface. Therefore, in the example previously shown, client connections will be restricted to use only the interface identified by IP address “10.0.0.1”.

Let’s now look at the server-side of the socket command. The Tcl socket command for servers has the following format:

  • socket –server command [options] port

  • The basic socket server command has the following format:

  • set servsock [ socket –server myaccept 25001 ]

This creates a server socket (note the –server option) with the procedure argument that will be called once an incoming connection is accepted for the socket. We bind to INADDR_ANY here (the wildcard) and port 25001. This means that we’ll accept connections on any available interface but port 25001.

The server version of the socket command supports a single option, which is shown in the following code in italics:

socket –server command –myaddr addr port

The default configuration for the server socket command is that a port must be specified to which the server will locally bind. If the –myaddr option is not specified, then we default to the wildcard and allow incoming connections on any available interface. If the –myaddr option is specified, then we restrict incoming connections from the given interface. A complete example of the socket server command is illustrated as:

set servsock [ socket –server myaccept                    –myaddr "10.0.0.2" 25001 ]

In this example, the server socket will call the myaccept procedure when an incoming client is connected. We restrict incoming connections to the interface represented by IP address “10.0.0.2” and port 25001.

The Tcl-DP package provides a similar command to Tcl’s core socket command, but with much more functionality to create a variety of different types of sockets. Let’s look at a few examples of the Tcl-DP package for socket creation and connection.

The dp_connect command can be used to create client and server sockets for a number of different protocols. The result of dp_connect is a new socket (channel identifier). We can create a TCP client socket as:

set clisock [ dp_connect tcp –host 192.168.1.1 –port 25001 ]

In this case, we use dp_connect with the TCP protocol to connect to host 192.168.1.1 and port 25001. To create the server end of this connection, we would do the following:

set srvsock [ dp_connect tcp –server true –myport 25001 ]

For the srvsock, we use dp_connect again specifying our desired protocol of TCP. The –server option specifies that we’re creating a server socket, which takes a Boolean. Finally, the –myport option specifies the port to which we’ll bind. For dp_connect, there is no callback for incoming client connections. This is because the Tcl-DP API includes an accept command to service incoming connections.

Creating a UDP protocol socket is similar to the TCP dp_connect command:

set srvsock [ dp_connect udp –host "10.0.0.1"                   –port 1999 –myport 2999 ]

We specify the UDP protocol identifier after the dp_connect command and the host and port to which we’ll direct outgoing packets (using the –host and –port options). We also specify the port number to which we’ll bind locally (here, 2999). Creating a peer socket for the previous UDP socket definition would be done as:

set srvsock [ dp_connect udp –host "10.0.0.2"                   –port 2999 –myport 1999 ]

Note that in the first case, we’re directing packets to host 10.0.0.1 port 1999, whereas in the peer case we’re directing packets to host 10.0.0.2 port 2199 (and each have bound these ports conversely).

We can also create a multicast socket using the ipm protocol identifier. In this case, we specify the group address that we want to join and the port. An example for multicast is:

set msock [ dp_connect ipm –host "239.0.0.2" –myport 48000 ]

This allows us to both send and receive multicast datagrams through msock, because all engage in the same communication. We could also set the maximum time-to-live for all datagrams sent using the –ttl option:

set msock [ dp_connect ipm –host "239.0.0.2" –myport 48000 –ttl 1 ]

When we’re finished with a socket, we must close it. To close a previously created socket, clisock, we use the close command.

 close clisock

After the close command is called, no further communication is possible with the socket. Any data queued for transmission will be given some amount of time to be sent before the connection physically closes.

Socket Addresses

Tcl has no external concept of an Internet or socket address. All address information is specified within the socket and dp_connect calls. This presents one of the problems with network programming in Tcl. Because addresses are specified within the calls that initiate communication, there is no way to identify the source address of a UDP datagram. For this reason, there’s no way to write the datagram server code pattern (in either pure Tcl or Tcl-DP). One caveat is the peer address information returned from the dp_accept command, which we see in the next section.

Socket Primitives

In this section, we look at a number of other important socket control commands, relating to the Tcl-DP package. Note that bind, listen, and connect are all implied within the socket or dp_connect commands and, therefore, no equivalent exists for Tcl. The only primitive that operates in a similar fashion to the BSD API is the accept command (dp_accept).

dp_accept

The dp_accept command is the command used by TCP servers for accepting incoming client connections. The dp_accept call blocks until a connection is received, and returns a list identifying the socket for the new client connection and address information about the peer that connected.

Let’s look at an example of dp_accept in a TCP server (acctest.tcl).

package require dp 4.0 set srvsock [ dp_connect tcp –server true –myport 13 ] set rcvlist [ dp_accept $srvsock ] set clisock [ lindex $rcvlist 0 ] set addr [lindex $rcvlist 1 ] puts "Got a connection from $addr" close $clisock close $srvsock

In this example, we create our TCP server socket using dp_connect and bind it locally to port 13. We then call dp_accept on our server socket and return the list result to rcvlist. The first element of this list is the new client socket (channel identifier), and the second is the address from which the peer connected. The lindex command is used to extract these elements from the list. We use the puts command at the end to emit who connected to us and then close both sockets with the close command.

Sockets I/O

Tcl provides a minimal set of commands to read or write data to a socket. The methods are identical for stream sockets as they are for datagram sockets, so we’ll look at examples of the Tcl core API and the Tcl-DP API.

The functions for communicating through a socket are read, gets, and puts, dp_recv, and dp_send. We look at examples of each of these commands in the following sections.

The Tcl core API utilizes the read, gets, and puts socket methods. Let’s now look at some sample code that illustrates the stream-specific methods. The first example illustrates a simple echo server built using stream sockets (acctest2.tcl):

proc myaccept { sock addr port }     fconfigure $sock –buffering line     while { true } {         puts $sock [ gets $sock ]     } } proc main{ port } {     set srvsock [ socket –server myaccept $port ]     vwait forever } main 45000

In this example, we create a socket server in our main function and then simply enter Tcl’s event loop to await incoming connections (via vwait). After a new client connection arrives, Tcl calls the myaccept command with the client socket information. We configure line buffer immediately using fconfigure and then enter an infinite loop. Within this loop, we read all data from the socket anonymously using gets and then pass this immediately to puts. Given this implementation, we could open as many client connections to this server as are allowable by the host machine, each having its own myaccept to process its input.

Now, let’s look at the TCP client socket that will connect to the previously defined server, but this time we’ll use the Tcl-DP package (testcli.tcl):

package require dp 4.0 set clisock [ dp_connect tcp –host localhost –port 45000 ] fconfigure $clisock –buffering line dp_send $clisock "Hello\n" set recv_str [ dp_recv $clisock ] puts $recv_str close $clisock

After we create our TCP client socket using the dp_connect command, we configure it for line buffering using the fconfigure command. We issue a dp_send command to send our test string and then await a response using dp_recv. The received string is then emitted to standard-out using puts and the client socket is closed using the close command.

To build datagram applications within Tcl, the Tcl-DP package must be used. Even with Tcl-DP, it’s not possible to identify the source of a given datagram (the dp_recv or gets commands are the only ones available to return data from a socket). Therefore, we can build datagram applications that communicate with one another, but they must have predefined addresses and ports so that each end of the connection can be consistent.

Building a simple datagram application that sends datagrams to a remote peer is simpler than building a stream application because there is no virtual connection between the two endpoints. In the following example, the datagram application simply sends a datagram to the peer and awaits the response (dgramcli.tcl):

package require dp 4.0 # Create a datagram socket and point to 127.0.0.1 / 33000 set dsock [ dp_connect udp –host 127.0.0.1 –port 33000 ] # Configure for line buffering fconfigure $dsock –buffering line # Send some data to the remote peer dp_send $dsock "Some string data..." # Await the response from the peer set line [ dp_recv $dsock ] # emit the response puts $line # Close the socket close $dsock

The datagram client is a fire-and-forget application in that we have no idea if the datagram arrived at the peer. In this particular application, we can know because we expect a response back, but in most cases, we won’t know if the datagram was received and no reply was made, or if the datagram was simply lost. The dp_recv and dp_send commands, as shown in the example, are used in the same way that send and recv are used in traditional Sockets applications. The connect must have been performed before communication may take place so that the Sockets layer can cache the addressing information about the peer. As there is no way to specify the destination information with the packet itself (as is done with Java), Tcl (and Tcl-DP) provide minimal datagram support.

Socket Options

Socket options permit an application to change some of the modifiable behaviors of a socket and change the behavior of the methods that manipulate them. For example, an application can modify the sizes of the send or receive socket buffers or whether input or output commands are blocking or nonblocking.

Tcl provides a minimal set of options that can be manipulated, though they’re much easier to deal with than in most other languages. Tcl also provides standard defaults for most options, though the send and receive socket buffers are currently defaulted to 4 KB. Additionally, the SO_REUSEADDR socket option is set by default for server sockets.

Let’s look at a simple scalar example first. Let’s say that we want to identify the size of the receive buffer for a given socket. This can be done with the following code segment:

set srvsock [ socket –server accept_proc 45000 ] set size [ fconfigure $srvsock –buffersize ] puts "The SND/RCV buffer sizes are $size"

First, we create a new stream server socket using the socket command. To get the value of the send and receive buffer size socket option (SO_SNDBUF / SO_RCVBUF), we use the fconfigure command. We specify the –buffersize option to the fconfigure command, and without specifying any argument, the command returns the value of the option. The disadvantage in Tcl is that there is no way to set the receive buffer size differently than the send buffer size; both must be changed identically.

We can set the buffer sizes for a given socket as the following code snippet demonstrates:

set srvsock [ socket –server accept_proc 45000 ] fconfigure $srvsock –buffersize 8192 set size [ fconfigure $srvsock –buffersize ] puts "The SND/RCV buffer sizes are $size"

The support for manipulating socket options within Tcl is lacking and further degrades its applicability to any network application development other than stream sockets.

Other Miscellaneous Functions

Let’s now look at a few miscellaneous commands available within Tcl and the capabilities that they provide. The first method that we discuss provides information about the current host.

Command info with the hostname option returns the string name of the current host:

set theHost [ info hostname ] puts theHost

The DNS resolver permits us to resolve a host name to an IP address, or vice versa. Command dp_netinfo of the Tcl-DP package provides domain name resolution capabilities (as well as other services). To convert a domain name into an IP address, the dp_netinfo command can be used with the –address option:

set ipAddr [ dp_netinfo –address www.microsoft.com ] puts ipAddr

where the return string ipAddr represents the IP address of the FQDN. What if we wanted to go in the opposite direction, providing an IP address and receiving the FQDN? To achieve this, we use the dp_netinfo command, again with the -address option:

set hostAddr [ dp_netinfo –address 207.46.249.190 ] puts hostAddr

The domain name of the specified address is contained within the string hostAddr.

Now, let’s consider the problem of identifying the port number for a given service. To specifically retrieve the port number associated with a service, we use the dp_netinfo command of the Tcl-DP package. Using the –service option, we can specify one of two arguments. We specify either the service name string or the service port number. Consider the following two examples:

set line [ dp_netinfo –service http ] set official_name [ lindex $line 0 ] set official_port [ lindex $line 1 ]

The dp_netinfo command with the service option returns a list. The first element of the list is the official name of the service (for http, this is www). The second element of the list is the official port number on which a service can be found. Therefore, upon using the -service option of the dp_netinfo command, the lindex command is then used to extract the individual elements.

The dp_netinfo command with the –service option can also be used to search for a service based upon the port number. For example:

set line [ dp_netinfo –service 80 ] set official_name [ lindex $line 0 ] set official_port [ lindex $line 1 ]

is synonymous with the previous example, searching for service string http.

Notification

Let’s now look at the concept of event notification for sockets. This capability is commonly provided by the select primitive. In Tcl, the select method is available as part of Extended Tcl (not natively), but Tcl also provides a different capability for event notification that is worthy of investigation.

The fileevent command within Tcl provides for event notification on a channel identifier. The fileevent requires a channel identifier, mode (readable or writable), and finally a script that will be invoked when the event occurs. Let’s now look at an example of the fileevent command being used to identify when a socket has data available for read (seltest.tcl).

proc main {} {     set servsock [ socket –server myaccept 45000 ]     vwait forever } proc myaccept { sock addr port } {     fconfigure $sock –buffering line     fileevent $sock readable [ list myread $sock ] } proc myread { sock } {     # Test the socket for eof (peer disconnect)     if { [ eof $sock ] } {         puts "Peer disconnected"         close $sock     } else {         # Loopback the data read         set line [ gets $sock ]         puts $sock $line     } } # Invoke main... main

We’ve seen the main procedure before; it’s simply setting up a server socket with the callback of myaccept for new incoming connections. When an incoming connection arrives, myaccept is called. The first task is to configure the new client socket for line buffering using fconfigure. The fileevent command then configures the socket for a callback whenever data is available for read. The script to be called is constructed at the end of fileevent. The list command is used to construct a list of the callback function and the channel identifier. This is required by Tcl for a well-formed callback command. When data finally arrives for the new client socket, the myread procedure is called with the channel identifier. We first test the channel for eof to see if the peer has disconnected. If so, we close the socket using the close command and end the callback. Otherwise, we read the data from the socket using gets and then emit this back through the socket using puts.



 < Day Day Up > 



BSD Sockets Programming from a Multi-Language Perspective
Network Programming for Microsoft Windows , Second Edition (Microsoft Programming Series)
ISBN: 1584502681
EAN: 2147483647
Year: 2003
Pages: 225
Authors: Jim Ohlund

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net