Java/Network Protocol/Server
Версия от 18:01, 31 мая 2010; (обсуждение)
Содержание
- 1 A generic framework for a flexible, multi-threaded server
- 2 A multithreaded server
- 3 A server can use specialized streams to deliver typed data
- 4 Base class to build multithreaded servers easily
- 5 Client and Server Demo
- 6 Client estimates the speed of the network connection to the server
- 7 Logging Server based on SocketServer
- 8 Manage a pool of threads for clients
- 9 Quote Server
- 10 Reflector
- 11 Serve entire objects using ObjectOutputStream
- 12 Server allows connections on socket 6123
- 13 The client can specify information to control the output of a server
- 14 This server displays messages to a single client
- 15 This server retrieves the time using the RFC867 protocol.
- 16 URL Connection Test
A generic framework for a flexible, multi-threaded server
/*
* Copyright (c) 2000 David Flanagan. All rights reserved.
* This code is from the book Java Examples in a Nutshell, 2nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book (recommended),
* visit http://www.davidflanagan.ru/javaexamples2.
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/**
* This class is a generic framework for a flexible, multi-threaded server. It
* listens on any number of specified ports, and, when it receives a connection
* on a port, passes input and output streams to a specified Service object
* which provides the actual service. It can limit the number of concurrent
* connections, and logs activity to a specified stream.
*/
public class Server {
/**
* A main() method for running the server as a standalone program. The
* command-line arguments to the program should be pairs of servicenames and
* port numbers. For each pair, the program will dynamically load the named
* Service class, instantiate it, and tell the server to provide that
* Service on the specified port. The special -control argument should be
* followed by a password and port, and will start special server control
* service running on the specified port, protected by the specified
* password.
*/
public static void main(String[] args) {
try {
if (args.length < 2) // Check number of arguments
throw new IllegalArgumentException("Must specify a service");
// Create a Server object that uses standard out as its log and
// has a limit of ten concurrent connections at once.
Server s = new Server(System.out, 10);
// Parse the argument list
int i = 0;
while (i < args.length) {
if (args[i].equals("-control")) { // Handle the -control arg
i++;
String password = args[i++];
int port = Integer.parseInt(args[i++]);
// add control service
s.addService(new Control(s, password), port);
} else {
// Otherwise start a named service on the specified port.
// Dynamically load and instantiate a Service class
String serviceName = args[i++];
Class serviceClass = Class.forName(serviceName);
Service service = (Service) serviceClass.newInstance();
int port = Integer.parseInt(args[i++]);
s.addService(service, port);
}
}
} catch (Exception e) { // Display a message if anything goes wrong
System.err.println("Server: " + e);
System.err.println("Usage: java Server "
+ "[-control <password> <port>] "
+ "[<servicename> <port> ... ]");
System.exit(1);
}
}
// This is the state for the server
Map services; // Hashtable mapping ports to Listeners
Set connections; // The set of current connections
int maxConnections; // The concurrent connection limit
ThreadGroup threadGroup; // The threadgroup for all our threads
PrintWriter logStream; // Where we send our logging output to
/**
* This is the Server() constructor. It must be passed a stream to send log
* output to (may be null), and the limit on the number of concurrent
* connections.
*/
public Server(OutputStream logStream, int maxConnections) {
setLogStream(logStream);
log("Starting server");
threadGroup = new ThreadGroup(Server.class.getName());
this.maxConnections = maxConnections;
services = new HashMap();
connections = new HashSet(maxConnections);
}
/**
* A public method to set the current logging stream. Pass null to turn
* logging off
*/
public synchronized void setLogStream(OutputStream out) {
if (out != null)
logStream = new PrintWriter(out);
else
logStream = null;
}
/** Write the specified string to the log */
protected synchronized void log(String s) {
if (logStream != null) {
logStream.println("[" + new Date() + "] " + s);
logStream.flush();
}
}
/** Write the specified object to the log */
protected void log(Object o) {
log(o.toString());
}
/**
* This method makes the server start providing a new service. It runs the
* specified Service object on the specified port.
*/
public synchronized void addService(Service service, int port)
throws IOException {
Integer key = new Integer(port); // the hashtable key
// Check whether a service is already on that port
if (services.get(key) != null)
throw new IllegalArgumentException("Port " + port
+ " already in use.");
// Create a Listener object to listen for connections on the port
Listener listener = new Listener(threadGroup, port, service);
// Store it in the hashtable
services.put(key, listener);
// Log it
log("Starting service " + service.getClass().getName() + " on port "
+ port);
// Start the listener running.
listener.start();
}
/**
* This method makes the server stop providing a service on a port. It does
* not terminate any pending connections to that service, merely causes the
* server to stop accepting new connections
*/
public synchronized void removeService(int port) {
Integer key = new Integer(port); // hashtable key
// Look up the Listener object for the port in the hashtable
final Listener listener = (Listener) services.get(key);
if (listener == null)
return;
// Ask the listener to stop
listener.pleaseStop();
// Remove it from the hashtable
services.remove(key);
// And log it.
log("Stopping service " + listener.service.getClass().getName()
+ " on port " + port);
}
/**
* This nested Thread subclass is a "listener". It listens for connections
* on a specified port (using a ServerSocket) and when it gets a connection
* request, it calls the servers addConnection() method to accept (or
* reject) the connection. There is one Listener for each Service being
* provided by the Server.
*/
public class Listener extends Thread {
ServerSocket listen_socket; // The socket to listen for connections
int port; // The port we"re listening on
Service service; // The service to provide on that port
volatile boolean stop = false; // Whether we"ve been asked to stop
/**
* The Listener constructor creates a thread for itself in the
* threadgroup. It creates a ServerSocket to listen for connections on
* the specified port. It arranges for the ServerSocket to be
* interruptible, so that services can be removed from the server.
*/
public Listener(ThreadGroup group, int port, Service service)
throws IOException {
super(group, "Listener:" + port);
listen_socket = new ServerSocket(port);
// give it a non-zero timeout so accept() can be interrupted
listen_socket.setSoTimeout(600000);
this.port = port;
this.service = service;
}
/***********************************************************************
* This is the polite way to get a Listener to stop accepting
* connections
**********************************************************************/
public void pleaseStop() {
this.stop = true; // Set the stop flag
this.interrupt(); // Stop blocking in accept()
try {
listen_socket.close();
} // Stop listening.
catch (IOException e) {
}
}
/**
* A Listener is a Thread, and this is its body. Wait for connection
* requests, accept them, and pass the socket on to the addConnection
* method of the server.
*/
public void run() {
while (!stop) { // loop until we"re asked to stop.
try {
Socket client = listen_socket.accept();
addConnection(client, service);
} catch (InterruptedIOException e) {
} catch (IOException e) {
log(e);
}
}
}
}
/**
* This is the method that Listener objects call when they accept a
* connection from a client. It either creates a Connection object for the
* connection and adds it to the list of current connections, or, if the
* limit on connections has been reached, it closes the connection.
*/
protected synchronized void addConnection(Socket s, Service service) {
// If the connection limit has been reached
if (connections.size() >= maxConnections) {
try {
// Then tell the client it is being rejected.
PrintWriter out = new PrintWriter(s.getOutputStream());
out.print("Connection refused; "
+ "the server is busy; please try again later.\n");
out.flush();
// And close the connection to the rejected client.
s.close();
// And log it, of course
log("Connection refused to "
+ s.getInetAddress().getHostAddress() + ":"
+ s.getPort() + ": max connections reached.");
} catch (IOException e) {
log(e);
}
} else { // Otherwise, if the limit has not been reached
// Create a Connection thread to handle this connection
Connection c = new Connection(s, service);
// Add it to the list of current connections
connections.add(c);
// Log this new connection
log("Connected to " + s.getInetAddress().getHostAddress() + ":"
+ s.getPort() + " on port " + s.getLocalPort()
+ " for service " + service.getClass().getName());
// And start the Connection thread to provide the service
c.start();
}
}
/**
* A Connection thread calls this method just before it exits. It removes
* the specified Connection from the set of connections.
*/
protected synchronized void endConnection(Connection c) {
connections.remove(c);
log("Connection to " + c.client.getInetAddress().getHostAddress() + ":"
+ c.client.getPort() + " closed.");
}
/** Change the current connection limit */
public synchronized void setMaxConnections(int max) {
maxConnections = max;
}
/**
* This method displays status information about the server on the specified
* stream. It can be used for debugging, and is used by the Control service
* later in this example.
*/
public synchronized void displayStatus(PrintWriter out) {
// Display a list of all Services that are being provided
Iterator keys = services.keySet().iterator();
while (keys.hasNext()) {
Integer port = (Integer) keys.next();
Listener listener = (Listener) services.get(port);
out.print("SERVICE " + listener.service.getClass().getName()
+ " ON PORT " + port + "\n");
}
// Display the current connection limit
out.print("MAX CONNECTIONS: " + maxConnections + "\n");
// Display a list of all current connections
Iterator conns = connections.iterator();
while (conns.hasNext()) {
Connection c = (Connection) conns.next();
out.print("CONNECTED TO "
+ c.client.getInetAddress().getHostAddress() + ":"
+ c.client.getPort() + " ON PORT "
+ c.client.getLocalPort() + " FOR SERVICE "
+ c.service.getClass().getName() + "\n");
}
}
/**
* This class is a subclass of Thread that handles an individual connection
* between a client and a Service provided by this server. Because each such
* connection has a thread of its own, each Service can have multiple
* connections pending at once. Despite all the other threads in use, this
* is the key feature that makes this a multi-threaded server
* implementation.
*/
public class Connection extends Thread {
Socket client; // The socket to talk to the client through
Service service; // The service being provided to that client
/**
* This constructor just saves some state and calls the superclass
* constructor to create a thread to handle the connection. Connection
* objects are created by Listener threads. These threads are part of
* the server"s ThreadGroup, so all Connection threads are part of that
* group, too.
*/
public Connection(Socket client, Service service) {
super("Server.Connection:"
+ client.getInetAddress().getHostAddress() + ":"
+ client.getPort());
this.client = client;
this.service = service;
}
/**
* This is the body of each and every Connection thread. All it does is
* pass the client input and output streams to the serve() method of the
* specified Service object. That method is responsible for reading from
* and writing to those streams to provide the actual service. Recall
* that the Service object has been passed from the Server.addService()
* method to a Listener object to the addConnection() method to this
* Connection object, and is now finally being used to provide the
* service. Note that just before this thread exits it always calls the
* endConnection() method to remove itself from the set of connections
*/
public void run() {
try {
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
service.serve(in, out);
} catch (IOException e) {
log(e);
} finally {
endConnection(this);
}
}
}
/**
* Here is the Service interface that we have seen so much of. It defines
* only a single method which is invoked to provide the service. serve()
* will be passed an input stream and an output stream to the client. It
* should do whatever it wants with them, and should close them before
* returning.
*
* All connections through the same port to this service share a single
* Service object. Thus, any state local to an individual connection must be
* stored in local variables within the serve() method. State that should be
* global to all connections on the same port should be stored in instance
* variables of the Service class. If the same Service is running on more
* than one port, there will typically be different Service instances for
* each port. Data that should be global to all connections on any port
* should be stored in static variables.
*
* Note that implementations of this interface must have a no-argument
* constructor if they are to be dynamically instantiated by the main()
* method of the Server class.
*/
public interface Service {
public void serve(InputStream in, OutputStream out) throws IOException;
}
/**
* A very simple service. It displays the current time on the server to the
* client, and closes the connection.
*/
public static class Time implements Service {
public void serve(InputStream i, OutputStream o) throws IOException {
PrintWriter out = new PrintWriter(o);
out.print(new Date() + "\n");
out.close();
i.close();
}
}
/**
* This is another example service. It reads lines of input from the client,
* and sends them back, reversed. It also displays a welcome message and
* instructions, and closes the connection when the user enters a "." on a
* line by itself.
*/
public static class Reverse implements Service {
public void serve(InputStream i, OutputStream o) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(i));
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(o)));
out.print("Welcome to the line reversal server.\n");
out.print("Enter lines. End with a "." on a line by itself.\n");
for (;;) {
out.print("> ");
out.flush();
String line = in.readLine();
if ((line == null) || line.equals("."))
break;
for (int j = line.length() - 1; j >= 0; j--)
out.print(line.charAt(j));
out.print("\n");
}
out.close();
in.close();
}
}
/**
* This service is an HTTP mirror, just like the HttpMirror class
* implemented earlier in this chapter. It echos back the client"s HTTP
* request
*/
public static class HTTPMirror implements Service {
public void serve(InputStream i, OutputStream o) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(i));
PrintWriter out = new PrintWriter(o);
out.print("HTTP/1.0 200 \n");
out.print("Content-Type: text/plain\n\n");
String line;
while ((line = in.readLine()) != null) {
if (line.length() == 0)
break;
out.print(line + "\n");
}
out.close();
in.close();
}
}
/**
* This service demonstrates how to maintain state across connections by
* saving it in instance variables and using synchronized access to those
* variables. It maintains a count of how many clients have connected and
* tells each client what number it is
*/
public static class UniqueID implements Service {
public int id = 0;
public synchronized int nextId() {
return id++;
}
public void serve(InputStream i, OutputStream o) throws IOException {
PrintWriter out = new PrintWriter(o);
out.print("You are client #: " + nextId() + "\n");
out.close();
i.close();
}
}
/**
* This is a non-trivial service. It implements a command-based protocol
* that gives password-protected runtime control over the operation of the
* server. See the main() method of the Server class to see how this service
* is started.
*
* The recognized commands are: password: give password; authorization is
* required for most commands add: dynamically add a named service on a
* specified port remove: dynamically remove the service running on a
* specified port max: change the current maximum connection limit. status:
* display current services, connections, and connection limit help: display
* a help message quit: disconnect
*
* This service displays a prompt, and sends all of its output to the user
* in capital letters. Only one client is allowed to connect to this service
* at a time.
*/
public static class Control implements Service {
Server server; // The server we control
String password; // The password we require
boolean connected = false; // Whether a client is already connected
/**
* Create a new Control service. It will control the specified Server
* object, and will require the specified password for authorization
* Note that this Service does not have a no argument constructor, which
* means that it cannot be dynamically instantiated and added as the
* other, generic services above can be.
*/
public Control(Server server, String password) {
this.server = server;
this.password = password;
}
/**
* This is the serve method that provides the service. It reads a line
* the client, and uses java.util.StringTokenizer to parse it into
* commands and arguments. It does various things depending on the
* command.
*/
public void serve(InputStream i, OutputStream o) throws IOException {
// Setup the streams
BufferedReader in = new BufferedReader(new InputStreamReader(i));
PrintWriter out = new PrintWriter(o);
String line; // For reading client input lines
// Has the user has given the password yet?
boolean authorized = false;
// If there is already a client connected to this service, display
// a message to this client and close the connection. We use a
// synchronized block to prevent a race condition.
synchronized (this) {
if (connected) {
out.print("ONLY ONE CONTROL CONNECTION ALLOWED.\n");
out.close();
return;
} else
connected = true;
}
// This is the main loop: read a command, parse it, and handle it
for (;;) { // infinite loop
out.print("> "); // Display a prompt
out.flush(); // Make it appear right away
line = in.readLine(); // Get the user"s input
if (line == null)
break; // Quit if we get EOF.
try {
// Use a StringTokenizer to parse the user"s command
StringTokenizer t = new StringTokenizer(line);
if (!t.hasMoreTokens())
continue; // if input was empty
// Get first word of the input and convert to lower case
String command = t.nextToken().toLowerCase();
// Now compare to each of the possible commands, doing the
// appropriate thing for each command
if (command.equals("password")) { // Password command
String p = t.nextToken(); // Get the next word
if (p.equals(this.password)) { // Is it the password?
out.print("OK\n"); // Say so
authorized = true; // Grant authorization
} else
out.print("INVALID PASSWORD\n"); // Otherwise fail
} else if (command.equals("add")) { // Add Service command
// Check whether password has been given
if (!authorized)
out.print("PASSWORD REQUIRED\n");
else {
// Get the name of the service and try to
// dynamically load and instantiate it.
// Exceptions will be handled below
String serviceName = t.nextToken();
Class serviceClass = Class.forName(serviceName);
Service service;
try {
service = (Service) serviceClass.newInstance();
} catch (NoSuchMethodError e) {
throw new IllegalArgumentException(
"Service must have a "
+ "no-argument constructor");
}
int port = Integer.parseInt(t.nextToken());
// If no exceptions occurred, add the service
server.addService(service, port);
out.print("SERVICE ADDED\n"); // acknowledge
}
} else if (command.equals("remove")) { // Remove service
if (!authorized)
out.print("PASSWORD REQUIRED\n");
else {
int port = Integer.parseInt(t.nextToken());
server.removeService(port); // remove the service
out.print("SERVICE REMOVED\n"); // acknowledge
}
} else if (command.equals("max")) { // Set connection limit
if (!authorized)
out.print("PASSWORD REQUIRED\n");
else {
int max = Integer.parseInt(t.nextToken());
server.setMaxConnections(max);
out.print("MAX CONNECTIONS CHANGED\n");
}
} else if (command.equals("status")) { // Status Display
if (!authorized)
out.print("PASSWORD REQUIRED\n");
else
server.displayStatus(out);
} else if (command.equals("help")) { // Help command
// Display command syntax. Password not required
out.print("COMMANDS:\n" + "\tpassword <password>\n"
+ "\tadd <service> <port>\n"
+ "\tremove <port>\n"
+ "\tmax <max-connections>\n" + "\tstatus\n"
+ "\thelp\n" + "\tquit\n");
} else if (command.equals("quit"))
break; // Quit command.
else
out.print("UNRECOGNIZED COMMAND\n"); // Error
} catch (Exception e) {
out.print("ERROR WHILE PARSING OR EXECUTING COMMAND:\n" + e
+ "\n");
}
}
connected = false;
out.close();
in.close();
}
}
}
A multithreaded server
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MultiThreadServer implements Runnable {
Socket csocket;
MultiThreadServer(Socket csocket) {
this.csocket = csocket;
}
public static void main(String args[]) throws Exception {
ServerSocket ssock = new ServerSocket(1234);
System.out.println("Listening");
while (true) {
Socket sock = ssock.accept();
System.out.println("Connected");
new Thread(new MultiThreadServer(sock)).start();
}
}
public void run() {
try {
PrintStream pstream = new PrintStream(csocket.getOutputStream());
for (int i = 100; i >= 0; i--) {
pstream.println(i + " bottles of beer on the wall");
}
pstream.close();
csocket.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
A server can use specialized streams to deliver typed data
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class DataServer {
public static void main(String args[]) throws Exception {
ServerSocket ssock = new ServerSocket(1234);
while (true) {
System.out.println("Listening");
Socket sock = ssock.accept();
DataOutputStream dstream = new DataOutputStream(sock
.getOutputStream());
dstream.writeFloat(3.14159265f);
dstream.close();
sock.close();
}
}
}
class DataClient {
public static void main(String[] args) throws Exception {
Socket sock = new Socket(args[0], 1234);
DataInputStream dis = new DataInputStream(sock.getInputStream());
float f = dis.readFloat();
System.out.println("PI=" + f);
dis.close();
sock.close();
}
}
Base class to build multithreaded servers easily
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
class MTServerBase extends Thread {
protected Socket socket;
public void run() {
try {
String s = "Server.";
socket.getOutputStream().write(s.getBytes());
socket.close();
} catch (Exception e) {
System.out.println(e);
}
}
static public void startServer(int port, Class clobj) {
ServerSocket ssock;
Socket sock;
try {
ssock = new ServerSocket(port);
while (true) {
Socket esock = null;
try {
esock = ssock.accept();
MTServerBase t = (MTServerBase) clobj.newInstance();
t.socket = esock;
t.start();
} catch (Exception e) {
try {
esock.close();
} catch (Exception ec) {
}
}
}
} catch (IOException e) {
}
}
}
public class UCServer extends MTServerBase {
public void run() {
try {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
while (true) {
char c = (char) is.read();
// end on Control+C or Control+D
if (c == "\003" || c == "\004")
break;
os.write(Character.toUpperCase(c));
}
socket.close();
} catch (Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
System.out.println("Starting server on port " + n);
startServer(n, UCServer.class);
}
}
Client and Server Demo
/* From http://java.sun.ru/docs/books/tutorial/index.html */
/*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class EchoClient {
public static void main(String[] args) throws IOException {
Socket echoSocket = null;
PrintWriter out = null;
BufferedReader in = null;
try {
echoSocket = new Socket("taranis", 7);
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
echoSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don"t know about host: taranis.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn"t get I/O for "
+ "the connection to: taranis.");
System.exit(1);
}
BufferedReader stdIn = new BufferedReader(
new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("echo: " + in.readLine());
}
out.close();
in.close();
stdIn.close();
echoSocket.close();
}
}
////////////////////////////////////////////////////////////
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class KnockKnockServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(1);
}
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
String inputLine, outputLine;
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye."))
break;
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
}
class KnockKnockProtocol {
private static final int WAITING = 0;
private static final int SENTKNOCKKNOCK = 1;
private static final int SENTCLUE = 2;
private static final int ANOTHER = 3;
private static final int NUMJOKES = 5;
private int state = WAITING;
private int currentJoke = 0;
private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who",
"Who" };
private 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 String processInput(String theInput) {
String theOutput = null;
if (state == WAITING) {
theOutput = "Knock! Knock!";
state = SENTKNOCKKNOCK;
} else if (state == SENTKNOCKKNOCK) {
if (theInput.equalsIgnoreCase("Who"s there?")) {
theOutput = clues[currentJoke];
state = SENTCLUE;
} else {
theOutput = "You"re supposed to say \"Who"s there?\"! "
+ "Try again. Knock! Knock!";
}
} else if (state == SENTCLUE) {
if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) {
theOutput = answers[currentJoke] + " Want another? (y/n)";
state = ANOTHER;
} else {
theOutput = "You"re supposed to say \"" + clues[currentJoke]
+ " who?\"" + "! Try again. Knock! Knock!";
state = SENTKNOCKKNOCK;
}
} else if (state == ANOTHER) {
if (theInput.equalsIgnoreCase("y")) {
theOutput = "Knock! Knock!";
if (currentJoke == (NUMJOKES - 1))
currentJoke = 0;
else
currentJoke++;
state = SENTKNOCKKNOCK;
} else {
theOutput = "Bye.";
state = WAITING;
}
}
return theOutput;
}
}
////////////////////////////////////////////////////////////
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class KKMultiServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(-1);
}
while (listening)
new KKMultiServerThread(serverSocket.accept()).start();
serverSocket.close();
}
}
class KKMultiServerThread extends Thread {
private Socket socket = null;
public KKMultiServerThread(Socket socket) {
super("KKMultiServerThread");
this.socket = socket;
}
public void run() {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
String inputLine, outputLine;
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye"))
break;
}
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class KnockKnockProtocol {
private static final int WAITING = 0;
private static final int SENTKNOCKKNOCK = 1;
private static final int SENTCLUE = 2;
private static final int ANOTHER = 3;
private static final int NUMJOKES = 5;
private int state = WAITING;
private int currentJoke = 0;
private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who",
"Who" };
private 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 String processInput(String theInput) {
String theOutput = null;
if (state == WAITING) {
theOutput = "Knock! Knock!";
state = SENTKNOCKKNOCK;
} else if (state == SENTKNOCKKNOCK) {
if (theInput.equalsIgnoreCase("Who"s there?")) {
theOutput = clues[currentJoke];
state = SENTCLUE;
} else {
theOutput = "You"re supposed to say \"Who"s there?\"! "
+ "Try again. Knock! Knock!";
}
} else if (state == SENTCLUE) {
if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) {
theOutput = answers[currentJoke] + " Want another? (y/n)";
state = ANOTHER;
} else {
theOutput = "You"re supposed to say \"" + clues[currentJoke]
+ " who?\"" + "! Try again. Knock! Knock!";
state = SENTKNOCKKNOCK;
}
} else if (state == ANOTHER) {
if (theInput.equalsIgnoreCase("y")) {
theOutput = "Knock! Knock!";
if (currentJoke == (NUMJOKES - 1))
currentJoke = 0;
else
currentJoke++;
state = SENTKNOCKKNOCK;
} else {
theOutput = "Bye.";
state = WAITING;
}
}
return theOutput;
}
}
////////////////////////////////////////////////////////////
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class KnockKnockClient {
public static void main(String[] args) throws IOException {
Socket kkSocket = null;
PrintWriter out = null;
BufferedReader in = null;
try {
kkSocket = new Socket("taranis", 4444);
out = new PrintWriter(kkSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(kkSocket
.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don"t know about host: taranis.");
System.exit(1);
} catch (IOException e) {
System.err
.println("Couldn"t get I/O for the connection to: taranis.");
System.exit(1);
}
BufferedReader stdIn = new BufferedReader(new InputStreamReader(
System.in));
String fromServer;
String fromUser;
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
out.close();
in.close();
stdIn.close();
kkSocket.close();
}
}
Client estimates the speed of the network connection to the server
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Calendar;
public class UdpEchoClient {
static final String testString = "Greeks bearing gifts";
public static void main(String[] args) {
InetAddress address;
try {
address = InetAddress.getByName(args[0]);
} catch (UnknownHostException host) {
System.out.println(host);
return;
}
DatagramPacket pack = new DatagramPacket(testString.getBytes(),
testString.length(), address, 7);
DatagramPacket incoming = new DatagramPacket(new byte[256], 256);
DatagramSocket sock = null;
try {
Calendar start, end;
sock = new DatagramSocket();
start = Calendar.getInstance();
sock.send(pack);
sock.setSoTimeout(5000);
sock.receive(incoming);
end = Calendar.getInstance();
String reply = new String(incoming.getData());
reply = reply.substring(0, testString.length());
if (reply.equals(testString)) {
System.out.println("Success");
System.out.println("Time = "
+ (end.getTime().getTime() - start.getTime().getTime())
+ "mS");
} else
System.out.println("Reply data did not match");
} catch (SocketException socke) {
System.out.println(socke);
} catch (IOException ioe) {
System.out.println(ioe);
} finally {
sock.close();
}
}
}
Logging Server based on SocketServer
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
public static void main(String args[]) throws Exception {
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
InputStream inStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
String str = null;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
}
}
Manage a pool of threads for clients
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class PoolServerBase extends Thread {
protected Socket socket;
static PoolServerBase head = null;
protected PoolServerBase next = null;
static protected boolean init(Class clobj, int threads) {
try {
for (int i = 0; i < threads; i++) {
PoolServerBase thread = (PoolServerBase) clobj.newInstance();
thread.next = head;
head = thread;
thread.start();
}
} catch (Exception e) {
return false;
}
return true;
}
static synchronized protected void addToList(PoolServerBase me) {
me.next = head;
head = me;
}
synchronized protected void waitForSignal() {
try {
wait();
} catch (InterruptedException e) {
}
}
public void run() {
while (true) {
waitForSignal();
doClientProcessing();
addToList(this);
}
}
synchronized protected void handleClient(Socket s) {
socket = s;
notify();
}
protected void doClientProcessing() {
try {
String s = "Server.";
s += "Thread: " + this.toString();
socket.getOutputStream().write(s.getBytes());
sleep(10000); // simulate processing
s = "Complete";
socket.getOutputStream().write(s.getBytes());
socket.close();
} catch (Exception e) {
System.out.println(e);
}
}
static synchronized protected boolean listEmpty() {
return head == null;
}
static protected void assignThread(Socket sock) {
PoolServerBase t = head;
head = head.next;
t.socket = sock;
synchronized (t) {
t.notify();
}
}
static public void startServer(int port) {
ServerSocket ssock;
Socket sock;
try {
ssock = new ServerSocket(port);
while (true) {
Socket esock = null;
try {
esock = ssock.accept();
while (listEmpty())
yield();
assignThread(esock);
} catch (Exception e) {
try {
esock.close();
} catch (Exception ec) {
}
}
}
} catch (IOException e) {
}
}
static public void main(String args[]) {
init(PoolServerBase.class, 3);
System.out.println("Starting server on port 808");
startServer(808);
}
}
Quote Server
/* From http://java.sun.ru/docs/books/tutorial/index.html */
/*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class QuoteServer {
public static void main(String[] args) throws IOException {
new QuoteServerThread().start();
}
}
class QuoteServerThread extends Thread {
private DatagramSocket socket = null;
private DataInputStream in = null;
private boolean moreQuotes = true;
public QuoteServerThread() throws IOException {
super("QuoteServerThread");
socket = new DatagramSocket(4445);
try {
in = new DataInputStream(new FileInputStream("one-liners.txt"));
} catch (FileNotFoundException e) {
System.err
.println("Could not open quote file. Serving time instead.");
}
}
public void run() {
while (moreQuotes) {
try {
byte[] buf = new byte[256];
// receive request
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
// figure out response
String dString = null;
if (in == null)
dString = new Date().toString();
else
dString = getNextQuote();
dString.getBytes(0, dString.length(), buf, 0);
// send the response to the client at "address" and "port"
InetAddress address = packet.getAddress();
int port = packet.getPort();
packet = new DatagramPacket(buf, buf.length, address, port);
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
moreQuotes = false;
}
}
socket.close();
}
private String getNextQuote() {
String returnValue = null;
try {
if ((returnValue = in.readLine()) == null) {
in.close();
moreQuotes = false;
returnValue = "No more quotes. Goodbye.";
}
} catch (IOException e) {
returnValue = "IOException occurred in server.";
}
return returnValue;
}
}
/////////////////////////////////////////////////
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class QuoteClient {
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.out.println("Usage: java QuoteClient <hostname>");
return;
}
// get a datagram socket
DatagramSocket socket = new DatagramSocket();
// send request
byte[] buf = new byte[256];
InetAddress address = InetAddress.getByName(args[0]);
DatagramPacket packet = new DatagramPacket(buf, buf.length, address,
4445);
socket.send(packet);
// get response
packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
// display response
String received = new String(packet.getData());
System.out.println("Quote of the Moment: " + received);
socket.close();
}
}
/////////////////////////////////////////////////
Reflector
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
public class Reflector implements Runnable {
// valid names found in the config file:
public static final String MODE = "Mode";
public static final String SOURCE_IP = "SourceIP";
public static final String SOURCE_PORT = "SourcePort";
public static final String DEST_IP = "DestIP";
public static final String DEST_PORT = "DestPort";
// valid modes in the config file, unicast to
// multicast or multicast to unicast
public static final String INPUT_UNITOMULTI = "UNI_TO_MULTI";
public static final String INPUT_MULTITOUNI = "MULTI_TO_UNI";
// possible modes the reflector can be set to:
public static final int MODE_NONE = 0;
public static final int MODE_UNI_TO_MULTI = 1;
public static final int MODE_MULTI_TO_UNI = 2;
// variables to indicate source or destination
public static final int SOURCE = 1;
public static final int DEST = 2;
// min and max network ports allowed
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65095;
// which mode the reflector is being run in:
private int mode = 0;
// source and destination hold variables:
private Address source;
private Hashtable dest;
private Address hold_dest = null;
// logging toggle and logger class
boolean logging = true;
Logger logger;
public Reflector() {
}
public void run() {
// validate the config file
if (readConfig() != 0) {
System.err.println("Error parsing config file\n");
System.exit(-1);
}
// start the logger
logger = new Logger(logging);
// spawn a thread to listen for packets
ReflectorListener listener = new ReflectorListener(source, mode, logger);
System.out.println("Listening on " + source.toString());
// spawn threads for each source address packets
// are to be forwarded on. Register each thread as
// PacketListenerInterface with the listener thread.
System.out.println("Sending on:");
for (Enumeration e = dest.elements(); e.hasMoreElements();) {
Address a = (Address) e.nextElement();
ReflectorSender sender = new ReflectorSender(a, mode, logger);
sender.start();
listener.addPacketListener((PacketListenerInterface) sender);
System.out.println(" " + a.toString());
}
// start the listener
listener.start();
}
public int readConfig() {
// validate the contents of the config file
BufferedReader input = null;
String name, value, inputLine = null;
dest = new Hashtable();
// open and read the config file
try {
input = new BufferedReader(new FileReader("reflector.conf"));
inputLine = input.readLine();
} catch (IOException e) {
System.err.println("Error reading reflector.conf.");
return (-1);
}
// loop until entire config file is read
while (inputLine != null) {
// skip comments:
if (inputLine.charAt(0) != "#") {
// extract a name/value pair, and branch
// based on the name:
StringTokenizer tokenizer = new StringTokenizer(inputLine, "=");
name = tokenizer.nextToken();
value = tokenizer.nextToken();
if (name == null) {
System.out.println("no name");
continue;
} else if (name.equals(MODE)) {
if (setMode(value) != 0) {
System.err.println("Error setting mode to " + value);
return (-1);
}
} else if (name.equals(SOURCE_IP)) {
if (setSourceIP(value) != 0) {
System.err.println("Error setting src IP address to "
+ value);
return (-1);
}
} else if (name.equals(SOURCE_PORT)) {
if (setSourcePort(value) != 0) {
System.err
.println("Error setting src port to " + value);
return (-1);
}
} else if (name.equals(DEST_IP)) {
if (setDestIP(value) != 0) {
System.err.println("Error setting dest IP address to "
+ value);
return (-1);
}
} else if (name.equals(DEST_PORT)) {
if (setDestPort(value) != 0) {
System.err.println("Error setting dest port to "
+ value);
return (-1);
}
} else {
System.err.println("Skipping invalid config file value: "
+ name);
}
}
// read next line in the config file
try {
inputLine = input.readLine();
} catch (IOException e) {
System.err.println("Error reading reflector.conf.");
return (-1);
}
}
// close the config file
try {
input.close();
} catch (IOException e) {
System.err.println("Error closing reflector.conf.");
return (-1);
}
// validate that the combined contents of the config file
// make sense
if (!isConfigValid()) {
System.err.println("Configuration file is not complete.");
return (-1);
}
return (0);
}
private int setMode(String value) {
// validate and set the mode from the config file
if (value.equals(INPUT_UNITOMULTI)) {
mode = MODE_UNI_TO_MULTI;
return (0);
} else if (value.equals(INPUT_MULTITOUNI)) {
mode = MODE_MULTI_TO_UNI;
return (0);
} else {
return (-1);
}
}
private int setSourceIP(String value) {
// validate and set the source IP from the config file
// call modeToAddress to validate IP address
InetAddress inet = modeToAddress(value, SOURCE);
if (inet == null)
return -1;
if (source != null) {
if (source.getAddress() != null)
System.err.println("Warning: overwriting src address "
+ source.getAddress().getHostAddress() + " with "
+ inet.getHostAddress() + ".");
source.setAddress(inet);
} else {
source = new Address(inet);
}
return (0);
}
private int setSourcePort(String value) {
// validate and set the source port from the config file
int port;
try {
port = Integer.parseInt(value);
} catch (NumberFormatException nfe) {
return (-1);
}
if ((port < MIN_PORT) || (port > 65095))
return (-1);
if (source != null) {
if (source.getPort() != 0)
System.err.println("Warning: overwriting src port "
+ source.getPort() + " with port " + port + ".");
source.setPort(port);
} else {
source = new Address(port);
}
return (0);
}
private int setDestIP(String value) {
// validate and set the dest IP from the config file
// call modeToAddress to validate IP address
InetAddress inet = modeToAddress(value, DEST);
if (inet == null)
return -1;
if (hold_dest != null) {
if (hold_dest.getAddress() != null)
System.err.println("Warning: overwriting dest address "
+ hold_dest.getAddress().getHostAddress() + " with "
+ inet.getHostAddress() + ".");
hold_dest.setAddress(inet);
if (hold_dest.isComplete())
return (addDest());
} else {
hold_dest = new Address(inet);
}
return (0);
}
private int setDestPort(String value) {
// validate and set the dest port from the config file
int port;
try {
port = Integer.parseInt(value);
} catch (NumberFormatException nfe) {
return (-1);
}
if ((port < MIN_PORT) || (port > MAX_PORT))
return (-1);
if (hold_dest != null) {
if (hold_dest.getPort() != 0)
System.err.println("Warning: overwriting dest port "
+ hold_dest.getPort() + " with port " + port + ".");
hold_dest.setPort(port);
if (hold_dest.isComplete())
return (addDest());
} else {
hold_dest = new Address(port);
}
return (0);
}
private int addDest() {
// once both a dest IP and port have been read, add them
// to our vector of all destinations.
switch (mode) {
case MODE_UNI_TO_MULTI:
if (!dest.isEmpty()) {
System.err.println("Warning: dest address overwritten");
dest.clear();
}
dest.put(hold_dest.toString(), hold_dest);
break;
case MODE_MULTI_TO_UNI:
dest.put(hold_dest.toString(), hold_dest);
break;
default:
// no mode set
System.err.println("Destination " + hold_dest.toString()
+ " skipped because no mode set.");
hold_dest = null;
return (-1);
}
hold_dest = null;
return (0);
}
private InetAddress modeToAddress(String value, int type) {
// validate the IP Address based on its text value, its
// type (DEST or SOURCE), and the mode (UNI_TO_MULTI or
// MULTI_TO_UNI). Returns an InetAddress if succesfull and
// null on failure.
InetAddress inet;
if ((type != DEST) && (type != SOURCE)) {
System.err.println("Invalid type passed to modeToAddress (" + type
+ ")");
return (null);
}
switch (mode) {
case MODE_UNI_TO_MULTI:
if (type == DEST)
inet = returnValidMCIP(value);
else
inet = returnValidIP(value);
break;
case MODE_MULTI_TO_UNI:
if (type == DEST)
inet = returnValidIP(value);
else
inet = returnValidMCIP(value);
break;
default:
// no mode set
System.err.println("Error: No Mode Selected.");
return (null);
}
if (inet == null)
System.err.println("Invalid dest IP address (" + value + ").");
return (inet);
}
private InetAddress returnValidIP(String IP) {
// return InetAddress if IP is valid, null otherwise
InetAddress inet;
try {
inet = InetAddress.getByName(IP);
} catch (UnknownHostException e) {
return (null);
}
return (inet);
}
private InetAddress returnValidMCIP(String IP) {
// return InetAddress if IP is valid multicast addr,
// null otherwise
InetAddress inet = returnValidIP(IP);
if (inet.isMulticastAddress()) {
return (inet);
} else {
return (null);
}
}
public boolean isConfigValid() {
// validate that the mode, source IP/port, and
// dest IP(s)/port(s) are all valid and a valid
// combination.
if (mode == MODE_NONE) {
System.err.println("No mode selected.");
return (false);
}
if (!source.isComplete()) {
if ((source.getPort() != 0) && (mode == MODE_UNI_TO_MULTI)) {
// if source is unicast local IP is implied
try {
source.setAddress(InetAddress.getLocalHost());
} catch (UnknownHostException e) {
System.err.println("Incomplete source address.");
return (false);
}
} else {
System.err.println("Incomplete source address.");
return (false);
}
}
if (dest.isEmpty()) {
System.err.println("No destination addresses.");
return (false);
}
for (Enumeration e = dest.elements(); e.hasMoreElements();) {
Address a = (Address) e.nextElement();
if (!a.isComplete()) {
System.err.println("Incompete destination address.");
return (false);
}
}
return (true);
}
public static void main(String args[]) {
Reflector r = new Reflector();
r.run();
}
}
//Address class is used to store an IP address and port
//combination.
class Address {
private InetAddress address = null;
private int port = 0;
public Address(InetAddress address, int port) {
this.address = address;
this.port = port;
}
public Address(InetAddress address) {
this.address = address;
}
public Address(int port) {
this.port = port;
}
public InetAddress getAddress() {
return (address);
}
public int getPort() {
return (port);
}
public void setPort(int port) {
this.port = port;
}
public void setAddress(InetAddress address) {
this.address = address;
}
public boolean isComplete() {
// return true if both IP and port are populated,
// false otherwise.
if ((address != null) && (port != 0))
return (true);
else
return (false);
}
public String toString() {
// return a string representation of the IP/port.
String str;
if (address == null)
str = "";
else
str = address.getHostAddress();
str = str + "/" + port;
return (str);
}
}
//Logger class opens and writes to the log file
//if boolean true is passed as constructor argument.
class Logger {
private boolean logging;
private FileWriter logfile;
public Logger(boolean logging) {
this.logging = logging;
if (logging) {
try {
// open logfile for append
logfile = new FileWriter("reflector.log", true);
} catch (IOException e) {
System.err.println("Error opening log file.");
}
log("Reflector started: " + new Date());
}
}
public void log(String str) {
// write string to logfile
// if logging disabled return
if (!logging)
return;
try {
logfile.write(str + "\n");
logfile.flush();
} catch (IOException e) {
System.err.println("Error writing to log file.");
}
}
}
//ReflectorSender creates a unicast or multicast socket
//and is registered to receive incoming packet notifications
//via the PacketListenerInterface. Incoming packets
//are forwarded on the outgoing socket.
class ReflectorSender extends Thread implements PacketListenerInterface {
private InetAddress sendAddr;
private int sendPort;
private int mode;
private DatagramSocket ds = null;
private Logger logger;
public ReflectorSender(Address a, int mode, Logger logger) {
sendAddr = a.getAddress();
sendPort = a.getPort();
this.mode = mode;
this.logger = logger;
}
public void run() {
// initialize a DatagramSocket or MulticastSocket
// based on the mode:
switch (mode) {
case Reflector.MODE_MULTI_TO_UNI:
ds = initUnicastSocket();
break;
case Reflector.MODE_UNI_TO_MULTI:
ds = (DatagramSocket) initMulticastSocket();
break;
default:
break;
}
}
private MulticastSocket initMulticastSocket() {
// initialize a MulticastSocket
MulticastSocket mc;
try {
mc = new MulticastSocket(sendPort);
} catch (Exception e) {
e.printStackTrace();
return (null);
}
return (mc);
}
private DatagramSocket initUnicastSocket() {
// initialize a DatagramSocket
DatagramSocket ds;
try {
ds = new DatagramSocket(sendPort);
} catch (Exception e) {
e.printStackTrace();
return (null);
}
return (ds);
}
public void packetReceived(DatagramPacket packet) {
// An incoming packet has been received. Override
// the old packet addressing to the new outgoing
// addressing, send it and log it.
try {
packet.setAddress(sendAddr);
packet.setPort(sendPort);
ds.send(packet);
logger.log("Packet forwarded to "
+ packet.getAddress().getHostAddress() + "/"
+ packet.getPort() + ", " + packet.getLength() + " bytes");
} catch (IOException e) {
System.err.println("Error sending packet");
e.printStackTrace();
}
}
}
//ReflectorListener thread listens for packets
//and notifies one or more interested threads
//who register as PacketListenerInterfaces.
class ReflectorListener extends Thread {
private InetAddress listenAddr;
private int listenPort;
private int mode;
private Vector packetListeners;
private Logger logger;
private static final int MAX_PACKET_SIZE = 1500;
public ReflectorListener(Address a, int mode, Logger logger) {
listenAddr = a.getAddress();
listenPort = a.getPort();
this.mode = mode;
this.logger = logger;
packetListeners = new Vector();
}
public void run() {
// create a unicast or multicast socket
// depending on the mode and listen for packets.
switch (mode) {
case Reflector.MODE_UNI_TO_MULTI:
DatagramSocket ds = initUnicastSocket();
if (ds != null)
listen(ds);
break;
case Reflector.MODE_MULTI_TO_UNI:
MulticastSocket mc = initMulticastSocket();
if (mc != null)
listen((DatagramSocket) mc);
break;
default:
break;
}
}
private MulticastSocket initMulticastSocket() {
// initialize a MulticastSocket and join the group
MulticastSocket mc;
try {
mc = new MulticastSocket(listenPort);
mc.joinGroup(listenAddr);
} catch (Exception e) {
System.err.println("Failed to create MulticastSocket on " + "port "
+ listenPort);
return (null);
}
return (mc);
}
private DatagramSocket initUnicastSocket() {
// initialize a DatagramSocket
DatagramSocket ds;
try {
ds = new DatagramSocket(listenPort);
} catch (Exception e) {
System.err.println("Failed to create DatagramSocket on " + "port "
+ listenPort);
return (null);
}
return (ds);
}
private void listen(DatagramSocket ds) {
// loop forever listening to packets, when they
// arrive log them and notify all interested threads.
byte[] buffer;
DatagramPacket packet;
while (true) {
try {
buffer = new byte[MAX_PACKET_SIZE];
packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
logger.log("Packet received, " + packet.getLength() + " bytes");
eventNotify(packet);
} catch (IOException e) {
System.err.println("Error receiving packet\n");
e.printStackTrace();
}
}
}
public void addPacketListener(PacketListenerInterface pl) {
// add interested thread to listeners vector
packetListeners.addElement(pl);
}
public void removePacketListener(PacketListenerInterface pl) {
// remove thread to listeners vector
packetListeners.removeElement(pl);
}
private void eventNotify(DatagramPacket packet) {
// notify all registered threads that a packet has arrived
// using the packetReceived(DatagramPacket) method.
for (Enumeration e = packetListeners.elements(); e.hasMoreElements();) {
PacketListenerInterface pl = (PacketListenerInterface) e
.nextElement();
pl.packetReceived(packet);
}
}
}
//PacketListenerInterface used by threads that need to
//be notified of datagram packet receipt. A single
//interface function packetReceived passes the packet
//information to the thread requiring the information.
interface PacketListenerInterface {
public void packetReceived(DatagramPacket packet);
}
Serve entire objects using ObjectOutputStream
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;
public class ObjServer {
public static void main(String args[]) throws Exception {
ServerSocket ssock = new ServerSocket(1234);
Hashtable hash = new Hashtable();
hash.put("Java Source and Support", "www.jexp.ru");
while (true) {
System.out.println("Listening");
Socket sock = ssock.accept();
ObjectOutputStream ostream = new ObjectOutputStream(sock
.getOutputStream());
ostream.writeObject(hash);
ostream.close();
sock.close();
}
}
}
// A Java-specific client can read objects from Java servers.
class ObjClient {
public static void main(String[] args) throws Exception {
Socket sock = new Socket(args[0], 1234);
ObjectInputStream ois = new ObjectInputStream(sock.getInputStream());
Hashtable hash = (Hashtable) ois.readObject();
System.out.println(hash);
ois.close();
sock.close();
}
}
Server allows connections on socket 6123
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketDemo {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(6123);
while (true) {
System.out.println("Listening");
Socket sock = server.accept();
InetAddress addr = sock.getInetAddress();
System.out.println("Connection made to " + addr.getHostName()
+ " (" + addr.getHostAddress() + ")");
pause(5000);
sock.close();
}
} catch (IOException e) {
System.out.println("Exception detected: " + e);
}
}
private static void pause(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
}
}
}
The client can specify information to control the output of a server
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class AnotherBeerServer {
public static void main(String args[]) throws Exception {
ServerSocket ssock = new ServerSocket(1234);
System.out.println("Listening");
Socket sock = ssock.accept();
ssock.close(); // no more connects
PrintStream ps = new PrintStream(sock.getOutputStream());
// ask for count
ps.print("count? ");
BufferedReader input = new BufferedReader(new InputStreamReader(sock
.getInputStream()));
// read and parse it
String line = input.readLine();
ps.println("");
int count = Integer.parseInt(line);
for (int i = count; i >= 0; i--) {
ps.println(i + " Java Source and Support.");
}
ps.close();
sock.close();
}
}
This server displays messages to a single client
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BeerServer {
public static void main(String args[]) throws Exception {
ServerSocket ssock = new ServerSocket(1234);
System.out.println("Listening");
Socket sock = ssock.accept();
ssock.close(); // no more connects
PrintStream ps = new PrintStream(sock.getOutputStream());
for (int i = 100; i >= 0; i--) {
ps.println(i + " from Java Source and Support.");
}
ps.close();
sock.close();
}
}
This server retrieves the time using the RFC867 protocol.
/*
Chapter 3 - Simple Protocols
Java 2 Network Protocols Black Book
by Al Williams
Paraglyph Press 2001
*/
import java.net.*;
import java.io.*;
public class Time867 {
public static void main(String[] args) {
String hostname = "tock.usno.navy.mil";
if (args.length==1) hostname=args[0];
try {
int c;
Socket sock=new Socket(hostname,13);
InputStream is = sock.getInputStream();
do {
c=is.read();
if (c!=-1) System.out.print((char)c);
} while (c!=-1);
}
catch (IOException e) { System.out.println(e); }
}
}
URL Connection Test
/**
* version 1.00 1999-08-27 author Cay Horstmann
*/
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLConnectionTest {
public static void main(String[] args) {
try {
String urlName;
if (args.length > 0)
urlName = args[0];
else
urlName = "http://java.sun.ru";
URL url = new URL(urlName);
URLConnection connection = url.openConnection();
// set username, password if specified on command line
if (args.length > 2) {
String username = args[1];
String password = args[2];
String input = username + ":" + password;
String encoding = base64Encode(input);
connection.setRequestProperty("Authorization", "Basic "
+ encoding);
}
connection.connect();
// print header fields
int n = 1;
String key;
while ((key = connection.getHeaderFieldKey(n)) != null) {
String value = connection.getHeaderField(n);
System.out.println(key + ": " + value);
n++;
}
// print convenience functions
System.out.println("----------");
System.out
.println("getContentType: " + connection.getContentType());
System.out.println("getContentLength: "
+ connection.getContentLength());
System.out.println("getContentEncoding: "
+ connection.getContentEncoding());
System.out.println("getDate: " + connection.getDate());
System.out.println("getExpiration: " + connection.getExpiration());
System.out.println("getLastModifed: "
+ connection.getLastModified());
System.out.println("----------");
BufferedReader in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
// print first ten lines of contents
String line;
n = 1;
while ((line = in.readLine()) != null && n <= 10) {
System.out.println(line);
n++;
}
if (line != null)
System.out.println(". . .");
} catch (IOException exception) {
System.out.println("Error: " + exception);
}
}
public static String base64Encode(String s) {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
Base64OutputStream out = new Base64OutputStream(bOut);
try {
out.write(s.getBytes());
out.flush();
} catch (IOException exception) {
}
return bOut.toString();
}
}
/*
* BASE64 encoding encodes 3 bytes into 4 characters.
* |11111122|22223333|33444444| Each set of 6 bits is encoded according to the
* toBase64 map. If the number of input bytes is not a multiple of 3, then the
* last group of 4 characters is padded with one or two = signs. Each output
* line is at most 76 characters.
*/
class Base64OutputStream extends FilterOutputStream {
public Base64OutputStream(OutputStream out) {
super(out);
}
public void write(int c) throws IOException {
inbuf[i] = c;
i++;
if (i == 3) {
super.write(toBase64[(inbuf[0] & 0xFC) >> 2]);
super.write(toBase64[((inbuf[0] & 0x03) << 4)
| ((inbuf[1] & 0xF0) >> 4)]);
super.write(toBase64[((inbuf[1] & 0x0F) << 2)
| ((inbuf[2] & 0xC0) >> 6)]);
super.write(toBase64[inbuf[2] & 0x3F]);
col += 4;
i = 0;
if (col >= 76) {
super.write("\n");
col = 0;
}
}
}
public void flush() throws IOException {
if (i == 1) {
super.write(toBase64[(inbuf[0] & 0xFC) >> 2]);
super.write(toBase64[(inbuf[0] & 0x03) << 4]);
super.write("=");
super.write("=");
} else if (i == 2) {
super.write(toBase64[(inbuf[0] & 0xFC) >> 2]);
super.write(toBase64[((inbuf[0] & 0x03) << 4)
| ((inbuf[1] & 0xF0) >> 4)]);
super.write(toBase64[(inbuf[1] & 0x0F) << 2]);
super.write("=");
}
}
private static char[] toBase64 = { "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
"V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
"v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "+", "/" };
private int col = 0;
private int i = 0;
private int[] inbuf = new int[3];
}