Joseph Haugh
University of New Mexico
Client Server
| |
Socket <===== TCP connection =====> Socket
| |
InputStream InputStream
OutputStream OutputStream
Socket(String host, int port)InputStream getInputStream()OutputStream getOutputStream()void close()ServerSocket(int port)Socket accept() – blocks until connection is madevoid close()The server drives the conversation – the client just responds:
Server Client
| ---- "Knock! Knock!" ----------> |
| <--- "Who's there?" ------------ |
| ---- "Turnip" -----------------> |
| <--- "Turnip who?" ------------- |
| ---- "Turnip the heat, ..." ---> |
| <--- "y" ----------------------- |
| ---- "Knock! Knock!" ----------> |
| ... |
| ---- "n" ----------------------- |
| ---- "Bye." -------------------> |
public static void main(String[] args) throws IOException {
String hostName = args[0];
int portNumber = Integer.parseInt(args[1]);
try (Socket socket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
String fromServer = in.readLine();
while(fromServer != null) {
System.out.println("Server: " + fromServer);
if(fromServer.equals("Bye.")) { break; }
String fromUser = stdIn.readLine();
if(fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
fromServer = in.readLine();
}
}
}
public static void main(String[] args) throws IOException {
String hostName = args[0];
int portNumber = Integer.parseInt(args[1]);
// try-with-resources: socket and streams are closed automatically
try (Socket socket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
// Not inside try since it would close System.in
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
// server speaks first, client reads before doing anything
String fromServer = in.readLine();
while(fromServer != null) {
System.out.println("Server: " + fromServer);
if(fromServer.equals("Bye.")) { break; }
String fromUser = stdIn.readLine();
if(fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser); // forward user input to server
}
fromServer = in.readLine();
}
}
}
The server must handle multiple clients simultaneously
public static void main(String[] args) throws IOException {
int portNumber = Integer.parseInt(args[0]);
System.out.println("Listening on port " + portNumber + " ...");
try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
while (true) {
Socket clientSocket = serverSocket.accept();
KnockKnock kk = new KnockKnock(clientSocket);
Thread t = new Thread(kk);
t.start();
}
}
}
public static void main(String[] args) throws IOException {
int portNumber = Integer.parseInt(args[0]);
System.out.println("Listening on port " + portNumber + " ...");
// bind to the port, no connection yet, just listening
try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
// Listen for new clients forever
while (true) {
// accept() blocks here until a client connects, then returns a Socket
Socket clientSocket = serverSocket.accept();
// hand the socket to a new thread, main loop is free immediately
KnockKnock kk = new KnockKnock(clientSocket);
Thread t = new Thread(kk);
t.start();
}
}
}
private static String BYE = "Bye.";
private enum State {
WAITING,
SENT_KNOCK_KNOCK,
SENT_CLUE,
ANOTHER
}
private static String[] clues =
{ "Turnip",
"Little Old Lady",
"Atch", "Who", "Who" };
private static String[] answers =
{ "Turnip the heat, it's cold in here!",
"I didn't know you could yodel!",
"Bless you!",
"Is there an owl in here?",
"Is there an echo in here?" };
public static class KnockKnock implements Runnable {
private final Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
private State state = State.WAITING;
private int currentJoke = 0;
public KnockKnock(Socket clientSocket)
throws IOException {
this.clientSocket = clientSocket;
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
}
}
public void run() {
String inputLine = null;
String outputLine;
do {
outputLine = processInput(inputLine);
out.println(outputLine);
if(outputLine.equals(BYE)) {
break;
}
try {
inputLine = in.readLine();
} catch (IOException ex) {
inputLine = null;
}
} while(inputLine != null);
}
public void run() {
String inputLine = null; // server speaks first
String outputLine;
do {
outputLine = processInput(inputLine); // state machine produces response
out.println(outputLine); // send response to client
if(outputLine.equals(BYE)) {
break; // conversation over, exit loop
}
try {
inputLine = in.readLine(); // wait for client's reply
} catch (IOException ex) {
inputLine = null;
}
} while(inputLine != null); // null means client disconnected
}
processInput implements a finite state machine
case corresponds to a state in the State enumThe state machine ensures the conversation follows the knock knock protocol
private String processInput(String input) {
String output = null;
switch (state) {
case WAITING:
output = "Knock! Knock!";
state = State.SENT_KNOCK_KNOCK;
break;
case SENT_KNOCK_KNOCK:
if(input.equalsIgnoreCase("Who's there?")) {
output = clues[currentJoke];
state = State.SENT_CLUE;
} else {
output =
"You're supposed to say \"Who's there?\"! " +
"Try again. Knock! Knock!";
}
break;
// ...
}
return output;
}
private String processInput(String input) {
String output = null;
switch (state) {
case WAITING: // initial state: start the joke
output = "Knock! Knock!";
state = State.SENT_KNOCK_KNOCK;
break;
case SENT_KNOCK_KNOCK: // waiting for "Who's there?"
if(input.equalsIgnoreCase("Who's there?")) {
output = clues[currentJoke];
state = State.SENT_CLUE;
} else {
output =
"You're supposed to say \"Who's there?\"! " +
"Try again. Knock! Knock!";
}
break;
case SENT_CLUE: // waiting for "clue who?"
if(input.equalsIgnoreCase(clues[currentJoke] + " who?")) {
output = answers[currentJoke] +
" Want another? (y/n)";
state = State.ANOTHER;
} else {
output = "You're supposed to say \"" +
clues[currentJoke] + " who?\"! " +
"Try again. Knock! Knock!";
state = State.SENT_KNOCK_KNOCK;
}
break;
case ANOTHER: // waiting for "y" or "n"
if(input.equalsIgnoreCase("y")) {
output = "Knock! Knock!";
currentJoke = (currentJoke + 1) % clues.length;
state = State.SENT_KNOCK_KNOCK;
} else {
output = BYE;
state = State.WAITING;
}
break;
}
return output;
}
