cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
6979
Views
5
Helpful
10
Replies

TCL socket

ivanov.arseniy
Level 1
Level 1

Hello friends,

not so long time ago i found out about tcl scripts that can be executed on cisco devices and found this very exciting. So i got me some books with tcl basics and had some good time studying it, now i want to explore some more advanced topics, such as let's say client-server communication. I found examples and even some explanations, but i am just not getting it. Is there a nice fella here on this forum who could chew it over for me? Let's say for example i want to create a socket between server on one router and client on another and i want server and client to communicate via messages bidirectionaly (client sends a string then server receives it and prints out to stdout and vise versa).

So server side is something like:

proc OnConnectProcedure {channel clientaddr clientport} {

    puts "$clientaddr connected"

    puts $channel "Hello, Client"

    close $channel

}

socket -server OnConnectProcedure 9990

vwait forever

and this server will wait for connection on port 9990, print out "x.x.x.x connected" and send hello client message to the client. Right?

What is on the other side?

set server x.x.x.x

set sckt [socket $server 9990]

gets $sock str

puts "Server said: $str"

close $sockChan

But that about when a lot of questions come to my mind... How should messages go the other way - from client to server, how and when do i use fileevent (readable, writable), fconfigure, sync option, when do i need to flush the channel? How and when should i wait for the EOF in channel?

I greatly appreciate any help and thank you in advance. 

Best regards, Arseniy S. Ivanov
1 Accepted Solution

Accepted Solutions

Joe Clarke
Cisco Employee
Cisco Employee

You can actually use straight examples of Tcl client/server communication on IOS with a few minor mods.  Take a look at

http://www.tcl.tk/about/netserver.html .  What you would need to do is wrap these scripts in EEM syntax.  For example, on the server side:

::cisco::eem::event_register_none

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*

array set arr_einfo [event_reqinfo]

# Echo_Server --
#     Open the server listening socket
#     and enter the Tcl event loop
#
# Arguments:
#     port     The server's port number

proc Echo_Server {port} {
    set s [socket -server EchoAccept $port]
    vwait forever
}

# Echo_Accept --
#     Accept a connection from a new client.
#     This is called after a new socket connection
#     has been created by Tcl.
#
# Arguments:
#     sock     The new socket connection to the client
#     addr     The client's IP address
#     port     The client's port number
     
proc EchoAccept {sock addr port} {
    global echo

    # Record the client's information

    puts "Accept $sock from $addr port $port"
    set echo(addr,$sock) [list $addr $port]

    # Ensure that each "puts" by the server
    # results in a network transmission

    fconfigure $sock -buffering line

    # Set up a callback for when the client sends data

    fileevent $sock readable [list Echo $sock]
}

# Echo --
#     This procedure is called when the server
#     can read data from the client
#
# Arguments:
#     sock     The socket connection to the client

proc Echo {sock} {
    global echo

    # Check end of file or abnormal connection drop,
    # then echo data back to the client.

    if {[eof $sock] || [catch {gets $sock line}]} {
     close $sock
     puts "Close $echo(addr,$sock)"
     unset echo(addr,$sock)
    } else {
     puts $sock $line
    }
}

Echo_Server $arr_einfo(arg1)

Register this as an EEM Tcl policy (assuming this script lives in flash:/policies as no_echo_server.tcl on the device):

event manager directory user policy flash:/policies

event manager policy no_echo_server.tcl

Then run it from EXEC mode:

event manager run no_echo_server.tcl 3333

That will start a listening socket on tcp/3333 on this device.

On the client device, create a script called no_echo_client.tcl with:

::cisco::eem::event_register_none

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*

array set arr_einfo [event_reqinfo]

#
# A client of the echo service.
#

proc Echo_Client {host port} {
    set s [socket $host $port]
    fconfigure $s -buffering line
    return $s
}

set sock [Echo_Client $arr_einfo(arg1) $arr_einfo(arg2)]
puts $sock "Hello!"
gets $sock response
puts "Response is $response"

Again, register this policy, then run it from EXEC mode with:

event manager run no_echo_client.tcl 10.1.1.1 3333

Where 10.1.1.1 is the IP address of the first (i.e., server) device.

That's it.  That's a simple client/server example on IOS with EEM.

View solution in original post

10 Replies 10

Joe Clarke
Cisco Employee
Cisco Employee

You can actually use straight examples of Tcl client/server communication on IOS with a few minor mods.  Take a look at

http://www.tcl.tk/about/netserver.html .  What you would need to do is wrap these scripts in EEM syntax.  For example, on the server side:

::cisco::eem::event_register_none

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*

array set arr_einfo [event_reqinfo]

# Echo_Server --
#     Open the server listening socket
#     and enter the Tcl event loop
#
# Arguments:
#     port     The server's port number

proc Echo_Server {port} {
    set s [socket -server EchoAccept $port]
    vwait forever
}

# Echo_Accept --
#     Accept a connection from a new client.
#     This is called after a new socket connection
#     has been created by Tcl.
#
# Arguments:
#     sock     The new socket connection to the client
#     addr     The client's IP address
#     port     The client's port number
     
proc EchoAccept {sock addr port} {
    global echo

    # Record the client's information

    puts "Accept $sock from $addr port $port"
    set echo(addr,$sock) [list $addr $port]

    # Ensure that each "puts" by the server
    # results in a network transmission

    fconfigure $sock -buffering line

    # Set up a callback for when the client sends data

    fileevent $sock readable [list Echo $sock]
}

# Echo --
#     This procedure is called when the server
#     can read data from the client
#
# Arguments:
#     sock     The socket connection to the client

proc Echo {sock} {
    global echo

    # Check end of file or abnormal connection drop,
    # then echo data back to the client.

    if {[eof $sock] || [catch {gets $sock line}]} {
     close $sock
     puts "Close $echo(addr,$sock)"
     unset echo(addr,$sock)
    } else {
     puts $sock $line
    }
}

Echo_Server $arr_einfo(arg1)

Register this as an EEM Tcl policy (assuming this script lives in flash:/policies as no_echo_server.tcl on the device):

event manager directory user policy flash:/policies

event manager policy no_echo_server.tcl

Then run it from EXEC mode:

event manager run no_echo_server.tcl 3333

That will start a listening socket on tcp/3333 on this device.

On the client device, create a script called no_echo_client.tcl with:

::cisco::eem::event_register_none

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*

array set arr_einfo [event_reqinfo]

#
# A client of the echo service.
#

proc Echo_Client {host port} {
    set s [socket $host $port]
    fconfigure $s -buffering line
    return $s
}

set sock [Echo_Client $arr_einfo(arg1) $arr_einfo(arg2)]
puts $sock "Hello!"
gets $sock response
puts "Response is $response"

Again, register this policy, then run it from EXEC mode with:

event manager run no_echo_client.tcl 10.1.1.1 3333

Where 10.1.1.1 is the IP address of the first (i.e., server) device.

That's it.  That's a simple client/server example on IOS with EEM.

Thank you, Joseph. I really appreciate your help. I guess i've edited my initial message right at the same time you wrote your answer. Would you be so kind to reread it, please? I've got more questions. Now let's talk about the example you have provided. I registered these policies and ran scripts after that. Something isn't working as i assume it should.

I'm starting server part:

R1#event manager run server.tcl 19999

I'm checking if router is listening on specified port:

R1#show control-plane host open-ports

Active internet connections (servers and established)

Prot               Local Address             Foreign Address                  Service    State

tcp                        *:23                         *:0                   Telnet   LISTEN

tcp                     *:19999                         *:0             EEM TCL Proc   LISTEN

It does.

Now I'm starting client part and output is not quite right:

R2#event manager run client.tcl 10.10.10.1 19999

Response is

R2#


What do you think could be the reason of this partial output?

Best regards, Arseniy S. Ivanov

Likely the problem is on the server side.  You need to increase the maxrun time.  If you modify the server script so the event line looks like:

::cisco::eem::event_register_none maxrun 100000

That should do it.

As for your other questions, the article I mentioned has some good insight explaining what the flow of a client/server system is.  In Tcl, fileevent is used to start an async handler for a connected client.  You use fileevent to register a callback that will be executed whenever a client connects to the server.  I'm not sure you'd ever use fileevent writeable on a server socket.  You'd want to first make sure the socket is readable meaning the client is going to initiate some communication.  I think the example above makes it clear how to check for EOF (you check just like you would on any file descriptor).  You'd typically check for EOF whenever you read from the socket.

Joseph, thank you again.

I'm implementing server-client communication right now and facing a little problem here. When client connects to the server i want it to send a string of text, which server should store in the file on flash. The problem is this file actually - it's 0 size and also in few seconds after running the script ubsflash: if removed by system:

Jun 29 11:06:50.431: %USBFLASH-5-CHANGE: usbflash0 has been removed!

do you know what could be the reason? When i send the output to stdout - there is no problem at all, but storing into the file fails. I tried to close file descriptor and not to close file descriptor, i tried a lot of thing and now i'm asking for tour help again.

here is server code:

::cisco::eem::event_register_none maxrun 1000

namespace import ::cisco::eem::*

namespace import ::cisco::lib::*

array set arr_einfo [event_reqinfo]

proc START {port} {

    set s [socket -server ACCEPT $port]

    vwait forever

}

proc ACCEPT {sock addr port} {

    global adr

    set adr $addr

    fconfigure $sock -buffering line

    fileevent $sock readable [list WR $sock]

}

proc WR {sock} {

    global adr

    if {[eof $sock] || [catch {gets $sock line}]} {

     close $sock

     unset adr

    } else {

     puts "$line"; #to stdout

     puts "$adr";  #to stdout

     set fd [open flash:tchk.inf w]; #open file on flash for writing

     puts $fd "host: $line - ip: $adr"; #writing to file

     close $fd; #closing file by descriptor

     close $sock; #closing socket to client

     unset adr

    }

}

START 19876

Best regards, Arseniy S. Ivanov

It sounds like a bug, but I have used a number of usbflash devices (e.g., the 1900s), and I have never seen this problem before.  Maybe you have bad flash.  What version of code are you using?  A quick test of 15.2(2)T shows this to be working.

It does sound like a bug, since i do not really see where this problem would come from. I'm 2 x using 1921 with "c1900-universalk9-mz.SPA.150-1.M6.bin" image on it. Tomorrow i'll try to swap server and client sides to see if this is actually hardware related problem (although these routers are from the same shipment and are identical), anyway we will see what happens.

Best regards, Arseniy S. Ivanov

Could be a code issue, too.  Like I said, I was unable to reproduce on 15.2(2)T.  Does the same problem happen for you in tclsh:

tclsh

set fd [open "flash:tchk.inf" "w"]

puts $fd "This is a test"

close $fd

I tried this piece of code earlier today too - works just fine.

Best regards, Arseniy S. Ivanov

Yep, you were right - not a script  problem - just ran it with no problems on other platform. Tomorrow i'll  try another image with 1921 (which i use for all my experiments) to see  if this problem is hardware related or image related.

Best regards,
Arseniy S. Ivanov

Best regards, Arseniy S. Ivanov

Ok, solved. For the record - the problem was with image

"c1900-universalk9-mz.SPA.150-1.M6.bin"

Best regards,
Arseniy S. Ivanov

Best regards, Arseniy S. Ivanov
Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: