17. Sockets

Introduction

//----------------------------------------------------------------------------------
myClient = new Socket("Machine name", portNumber)
myAddress = myClient.inetAddress
myAddress.hostAddress  // string representation of host address
myAddress.hostName     // host name
myAddress.address      // IP address as array of bytes
//----------------------------------------------------------------------------------

Writing a TCP Client

//----------------------------------------------------------------------------------
s = new Socket("localhost", 5000);
s << "Why don't you call me anymore?\n"
s.close()
//----------------------------------------------------------------------------------

Writing a TCP Server

//----------------------------------------------------------------------------------
// commandline socket server echoing input back to originator
groovy -l 5000 -e "println line"

// commandline socket server eching input to stderr
groovy -l 5000 -e "System.err.println line"

// a web server as a script (extension to cookbook)
 server = new ServerSocket(5000)
 while(true) {
     server.accept() { socket ->
         socket.withStreams { input, output ->
             // ignore input and just serve dummy content
             output.withWriter { writer ->
                 writer << "HTTP/1.1 200 OK\n"
                 writer << "Content-Type: text/html\n\n"
                 writer << "<html><body>Hello World! It's ${new Date()}</body></html>\n"
             }
         }
     }
 }
//----------------------------------------------------------------------------------

Communicating over TCP

//----------------------------------------------------------------------------------
server = new ServerSocket(5000)
while(true) {
    server.accept() { socket ->
        socket.withStreams { input, output ->
            w = new PrintWriter(output)
            w << "What is your name? "
            w.flush()
            r = input.readLine()
            System.err.println "User responded with $r"
            w.close()
        }
    }
}
//----------------------------------------------------------------------------------

Setting Up a UDP Client

//----------------------------------------------------------------------------------
// UDP client
data = "Message".getBytes("ASCII")
addr = InetAddress.getByName("localhost")
port = 5000
packet = new DatagramPacket(data, data.length, addr, port)
socket = new DatagramSocket()
socket.send(packet)
//----------------------------------------------------------------------------------

Setting Up a UDP Server

//----------------------------------------------------------------------------------
// UDP server
socket = new DatagramSocket(5000)
buffer = (' ' * 4096) as byte[]
while(true) {
    incoming = new DatagramPacket(buffer, buffer.length)
    socket.receive(incoming)
    s = new String(incoming.data, 0, incoming.length)
    String reply = "Client said: '$s'"
    outgoing = new DatagramPacket(reply.bytes, reply.size(),
            incoming.address, incoming.port);
    socket.send(outgoing)
}

// UDP client
data = "Original Message".getBytes("ASCII")
addr = InetAddress.getByName("localhost")
port = 5000
packet = new DatagramPacket(data, data.length, addr, port)
socket = new DatagramSocket()
socket.send(packet)
socket.setSoTimeout(30000) // block for no more than 30 seconds
buffer = (' ' * 4096) as byte[]
response = new DatagramPacket(buffer, buffer.length)
socket.receive(response)
s = new String(response.data, 0, response.length)
println "Server said: '$s'"
// => Server said: 'Client said: 'Original Message''
//----------------------------------------------------------------------------------

Using UNIX Domain Sockets

//----------------------------------------------------------------------------------
// DOMAIN sockets not available in cross platform form.
// On Linux, use jbuds:
// http://www.graphixprose.com/jbuds/
//----------------------------------------------------------------------------------

Identifying the Other End of a Socket

//----------------------------------------------------------------------------------
// TCP socket
socketAddress = tcpSocket.remoteSocketAddress
println "$socketAddress.address, $socketAddress.hostName, $socketAddress.port"
// UDP packet
println "$udpPacket.address, $udpPacket.port"
//----------------------------------------------------------------------------------

Finding Your Own Name and Address

//----------------------------------------------------------------------------------
// Print the fully qualified domain name for this IP address
println InetAddress.localHost.canonicalHostName
//----------------------------------------------------------------------------------

Closing a Socket After Forking

//----------------------------------------------------------------------------------
socket.shutdownInput()
socket.shutdownOutput()
//----------------------------------------------------------------------------------

Writing Bidirectional Clients

//----------------------------------------------------------------------------------
// Spawn off a thread to handle each direction
//----------------------------------------------------------------------------------

Forking Servers

//----------------------------------------------------------------------------------
// Spawn off a thread to handle each request.
// This is done automatically by the Groovy accept() method on ServerSocket.
// See 17.3 for an example.
//----------------------------------------------------------------------------------

Pre-Forking Servers

//----------------------------------------------------------------------------------
// Use a thread pool
//----------------------------------------------------------------------------------

Non-Forking Servers

//----------------------------------------------------------------------------------
// Consider using Selector and/or SocketChannel, ServerSocketChannel and DatagramChannel
//----------------------------------------------------------------------------------

Writing a Multi-Homed Server

//----------------------------------------------------------------------------------
// When creating a socket on a multihomed machine, use the socket constructor with
// 4 params to select a specific address from those available:
socket = new Socket(remoteAddr, remotePort, localAddr, localPort)

// When creating a server on a multihomed machine supply the optional bindAddr param:
new ServerSocket(port, queueLength, bindAddr)
//----------------------------------------------------------------------------------

Making a Daemon Server

//----------------------------------------------------------------------------------
// Fork off a thread for your server and call setDaemon(true) on the thread.
//----------------------------------------------------------------------------------

Restarting a Server on Demand

//----------------------------------------------------------------------------------
// Consider using special packages designed to provide robust startup/shutdown
// capability, e.g.: http://jakarta.apache.org/commons/daemon/
//----------------------------------------------------------------------------------

Program: backsniff

//----------------------------------------------------------------------------------
// Alternative to cookbook as proposed inetd solution is not cross platform.
host = 'localhost'
for (port in 1..1024) {
    try {
        s = new Socket(host, port)
        println("There is a server on port $port of $host")
    }
    catch (Exception ex) {}
}
// You could open a ServerSocket() on each unused port and monitor those.
//----------------------------------------------------------------------------------

Program: fwdport

//----------------------------------------------------------------------------------
// It's not too hard to write a TCP Proxy in Groovy but numerous Java packages
// already exist, so we might as well use one of those:
// http://ws.apache.org/axis/java/user-guide.html#AppendixUsingTheAxisTCPMonitorTcpmon
//----------------------------------------------------------------------------------