Kurose’s book exercises (labs) — UDP Pinger (Server and Client)
The exercise description is:
In this lab, you will study a simple Internet ping server written in the Java language, and implement a corresponding client. The functionality provided by these programs are similar to the standard ping programs available in modern operating systems, except that they use UDP rather than Internet Control Message Protocol (ICMP) to communicate with each other. (Java does not provide a straightforward means to interact with ICMP.)
The ping protocol allows a client machine to send a packet of data to a remote machine, and have the remote machine return the data back to the client unchanged (an action referred to as echoing). Among other uses, the ping protocol allows hosts to determine round-trip times to other machines.
ICMP Message
Pinger Message Class:
class PingMessage(addr: InetAddress, port: Int, message: String) {
def getAddress = addr
def getMessage = message
def getPort = port
}
UDP Pinger Protocol
class UDPPinger {
protected val socket = new DatagramSocket()
def sendPing(ping: PingMessage) {
val host = ping.getAddress
val port = ping.getPort
val message = ping.getMessage
val msgBytes = message.getBytes
val msglen = message.length
try {
val packet = new DatagramPacket(msgBytes, msglen, host, port)
socket.send(packet)
println("Sent message to " + host + ":" + port)
} catch {
case e: IOException =>
println("Error sending packet: " + e)
}
}
def receivePing = {
val recvBuf = new Array[Byte](Configs.MAX_PING_LEN)
val recvPacket = new DatagramPacket(recvBuf, Configs.MAX_PING_LEN)
socket.receive(recvPacket)
println("Received message from " +
recvPacket.getAddress + ":" + recvPacket.getPort)
val recvMsg = new String(recvPacket.getData)
val reply = new PingMessage(recvPacket.getAddress,
recvPacket.getPort, recvMsg)
reply
}
}
UDP Pinger Client
The multithreaded client class:
class UDPClient(remoteHost: String, remotePort: Int, numberOfPings: Int)
extends UDPPinger with Runnable {
var numReplies = 0
val replies = new Array[Boolean](numberOfPings)
val rtt = new Array[Long](numberOfPings)
def this(remoteHost: String, remotePort: Int) =
this(remoteHost, remotePort, Configs.NUM_PINGS)
def run {
sendAllPings
sendReplyTimeout
printStatistics
}
private def sendAllPings {
socket.setSoTimeout(Configs.TIMEOUT)
for (i < - 0 until numberOfPings) {
val message = "PING " + i + " " + (new Date).getTime + " "
replies(i) = false
rtt(i) = Configs.RTT
val hostName = InetAddress.getByName(remoteHost)
val ping = new PingMessage(hostName, remotePort, message)
sendPing(ping)
handleReply(receivePing.getMessage)
}
}
private def sendReplyTimeout {
socket.setSoTimeout(Configs.REPLY_TIMEOUT)
while (numReplies < numberOfPings) {
try {
handleReply(receivePing.getMessage)
} catch {
case e: SocketTimeoutException =>
numReplies = numberOfPings
}
}
}
private def printStatistics {
for (i < - 0 until numberOfPings) {
val s =
if (rtt(i) > 0)
rtt(i).toString
else
"1"
println("PING " + i + ": " + replies(i) + " RTT: " + s + " ms")
}
}
private def handleReply(reply: String) {
val tmp = reply.split(" ")
val pingNumber = tmp(1).toInt
val then = tmp(2).toLong
replies(pingNumber) = true
rtt(pingNumber) = (new Date).getTime - then
numReplies += 1
}
}
The Client:
object PingerClient {
def main(args: Array[String]) {
val host = "127.0.0.1"
val port = 9876
println("Contacting host " + host + " at port " + port)
val client = new UDPClient(host, port, 20)
client.run
}
}
UDP Pinger Server
object PingerServer {
def main(args: Array[String]) {
val serverPort = 9876
val serverSocket = new DatagramSocket(serverPort)
val receiveData = new Array[Byte](Configs.MAX_PING_LEN)
while (true) {
val receivePacket = new DatagramPacket(receiveData, receiveData.length)
serverSocket.receive(receivePacket)
val sentence = new String(receivePacket.getData)
if (sentence != null && !sentence.equals(""))
println(sentence)
val ip = receivePacket.getAddress
val port = receivePacket.getPort
val data = sentence.getBytes
val sendPacket = new DatagramPacket(data, data.length, ip, port)
serverSocket.send(sendPacket)
}
}
}
Secondary and auxiliary modules
object Configs {
val MAX_PING_LEN = 1024
val NUM_PINGS = 10
val TIMEOUT = 1000
val REPLY_TIMEOUT = 5000
val RTT = 1000000
}
Simulations
Assuming you’re running the client and server on the same machine (127.0.0.1), you can run the code:
object PingerTestSimulation {
def main(args: Array[String]) {
println("Start ping simulation...")
new Thread {
override def run {
PingerServer.main(args)
}
}.start
new Thread {
override def run {
PingerClient.main(args)
}
}.start
}
}
These codes can be found here: UDPPinger.scala, PingerServer.scala, PingerClient.scala e PingerTestSimulation.scala