[AMG]: It's not too hard. In this example there are two computers, client and server. The client's IP address is, let's say, 10.0.0.1. It will connect to the server (IP: 10.0.0.2) on TCP port 12345. Here's the code each side runs: '''Client''' (IP: 10.0.0.1) set chan [socket 10.0.0.2 12345] ;# Open the connection puts $chan hello ;# Send a string flush $chan ;# Flush the output buffer puts "10.0.0.2:12345 says [gets $chan]" ;# Receive a string close $chan ;# Close the socket '''Server''' (IP: 10.0.0.2) proc accept {chan addr port} { ;# Make a proc to accept connections puts "$addr:$port says [gets $chan]" ;# Receive a string puts $chan goodbye ;# Send a string close $chan ;# Close the socket (automatically flushes) } ;# socket -server accept 12345 ;# Create a server socket vwait forever ;# Enter the event loop Of course the server should already be up and running before the client starts. What does it look like? '''Client''' (IP: 10.0.0.1) 10.0.0.2:12345 says goodbye '''Server''' (IP: 10.0.0.2) 10.0.0.1:49100 says hello Discuss. ---- [Chad] observes that on the server side, doing something like set S [socket -server accept 12345] won't result in being able to puts $S "anything" later. This makes a lot of sense, I'm sure, but seems a bit counter-intuitive to us less-immersed types. While the example above certainly works and is the simplest possible, something more will need to be done if you want to have an ongoing conversation between client and server once the connection is made. At this point the only handle we have on the readable/writable socket (which is NOT returned by the `socket -server` call) is `$chan`. `$chan`, which points to the socket channel, is local to the proc `accept` (and dies with it), but the channel itself is not. So, we can proc accept {chan addr port} { ;# Make a proc to accept connections puts "$addr:$port says [gets $chan]" ;# Receive a string puts $chan "let's talk"; flush $chan ;# Send a string set ::realS $chan ;# don't forget name of socket when proc finishes } ;# ...and then, from anywhere else... gets $::realS Since we're not relying on `close $chan` to [flush] the channel, we have to do it explicitly. If we're going to be communicating with newline-terminated strings only, we can use [fconfigure] to automate the flushing. proc accept {chan addr port} { ;# Make a proc to accept connections fconfigure $chan -buffering line ;# automate flushing puts "$addr:$port says [gets $chan]" ;# Receive a string puts $chan "let's talk" ;# Send a string set realS $chan ;# don't forget name of socket when proc finishes } ;# (At the risk of straying too far from the intent of this page as declared in its title) if the conversation that will then ensue will not follow a rigid script, blocking issues will arise. This means that if the client has not sent anything, then gets $::realS will hang your application until the client ''does'' send something. To avoid this, we should use [fileevent] to manage pulling data off the socket when available. proc handleComm S {puts [gets $S]} ;# Make a proc to receive communications proc accept {chan addr port} { ;# Make a proc to accept connections fconfigure $chan -buffering line ;# automate flushing puts "$addr:$port says [gets $chan]" ;# Receive a string puts $chan "let's talk" ;# Send a string fileevent $chan readable \ [list handleComm $chan] ;# set up to handle incoming data when necessary } ;# `handleComm` will be called every time the client side flushes data to the socket. In the meantime, the name of the socket channel is held in the event registered by fileevent. Of course, fconfigure and fileevent are identically applicable on the client side. We could then perhaps call this the simplest possible ''useful'' socket demonstration. ---- See also: * [Einfach Tcl]: '''Beispiel: Ein einfacher Zeitserver''' and subsequent examples * [A little client-server example] * [socket] ---- [[ [Category Tutorial] | [Category Networking] ]]