I'm searching for the best pattern to implement client/server socket communication using appropriate features of Java 1.7 ( try-with-resources ). It must be assured all threads and resources are closed and released completely. Effective Java / Clean Code items should be considered as well as simple debugability (only one statement in each line).
Any feedback or discussion would be appreciated.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import org.junit.Test;
public final class ThreadedServerTest1 {
private static final class ThreadedServer implements Runnable {
private final Socket socket;
ThreadedServer(final Socket socket) {
this.socket = socket;
}
public static void main(final String arguments[]) {
final int port = arguments.length > 0 ? Integer.parseInt(arguments[0]) : 9999;
try (final ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
final Socket socket = serverSocket.accept();
final ThreadedServer server = new ThreadedServer(socket);
final Thread thread = new Thread(server);
thread.start();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// @formatter:off
try (
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7027552 and
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7013420:
final Socket socket = this.socket;
final InputStream inputStream = socket.getInputStream();
final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
final OutputStream outputStream = socket.getOutputStream();
final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
final PrintWriter printWriter = new PrintWriter(outputStreamWriter);
) {
// @formatter:on
final String request = bufferedReader.readLine();
System.out.println(String.format("Received from client: %s", request));
printWriter.println("Hello client!");
printWriter.flush();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private static final class Client {
public static void main(final String arguments[]) {
final int port = arguments.length > 0 ? Integer.parseInt(arguments[0]) : 9999;
try {
final InetAddress loopbackAddress = InetAddress.getByName(null);
// @formatter:off
try (
final Socket socket = new Socket(loopbackAddress, port);
final OutputStream outputStream = socket.getOutputStream();
final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
final PrintWriter printWriter = new PrintWriter(outputStreamWriter);
final InputStream inputStream = socket.getInputStream();
final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader)
) {
// @formatter:on
printWriter.println("Hello server!");
printWriter.flush();
final String answer = bufferedReader.readLine();
System.out.println(String.format("Answer from server: %s", answer));
} catch (final IOException e) {
e.printStackTrace();
}
} catch (final UnknownHostException e) {
e.printStackTrace();
}
}
}
@Test
public void test() {
final String[] arguments = new String[] { "9999" };
final Thread tread = new Thread() {
@Override
public void run() {
ThreadedServer.main(arguments);
};
};
tread.start();
Client.main(arguments);
}
}
Pros:
Cons:
Socke
t instance needed to use try-with-resources UnknownHostException
from InetAddress#getByName(null)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.Test;
public final class ThreadedServerTest2 {
private static final class Connection implements AutoCloseable {
final Socket socket;
final InputStream inputStream;
final InputStreamReader inputStreamReader;
final BufferedReader bufferedReader;
final OutputStream outputStream;
final OutputStreamWriter outputStreamWriter;
final PrintWriter printWriter;
private Connection(final Socket socket) throws IOException {
this.socket = socket;
inputStream = socket.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
bufferedReader = new BufferedReader(inputStreamReader);
outputStream = socket.getOutputStream();
outputStreamWriter = new OutputStreamWriter(outputStream);
printWriter = new PrintWriter(outputStreamWriter);
}
static Connection serverConnection(final Socket socket) throws IOException {
return new Connection(socket);
}
static Connection clientConnection(final String hostname, final int port) throws IOException {
final InetAddress inetAddress = InetAddress.getByName(hostname);
final Socket socket = new Socket(inetAddress, port);
return new Connection(socket);
}
static Connection localhostCientConnection(final int port) throws IOException {
return clientConnection(null, port);
}
@Override
public void close() {
closeAndIgnoreException(printWriter, outputStreamWriter, outputStream, bufferedReader, inputStreamReader, inputStream, socket);
}
private void closeAndIgnoreException(final AutoCloseable... closeables) {
for (final AutoCloseable closeable : closeables) {
try {
closeable.close();
} catch (final Exception ignore) {
}
}
}
}
private static final class ThreadedServer implements Runnable {
private final Socket socket;
private ThreadedServer(final Socket socket) {
this.socket = socket;
}
public static void main(final String arguments[]) {
final int port = arguments.length > 0 ? Integer.parseInt(arguments[0]) : 9999;
try (final ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
final Socket socket = serverSocket.accept();
final ThreadedServer server = new ThreadedServer(socket);
final Thread thread = new Thread(server);
thread.start();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try (Connection connection = Connection.serverConnection(socket)) {
final String request = connection.bufferedReader.readLine();
System.out.println(String.format("Received from client: %s", request));
connection.printWriter.println("Hello client!");
connection.printWriter.flush();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private static final class Client {
public static void main(final String arguments[]) {
final int port = arguments.length > 0 ? Integer.parseInt(arguments[0]) : 9999;
try (Connection connection = Connection.localhostCientConnection(port)) {
connection.printWriter.println("Hello server!");
connection.printWriter.flush();
final String answer = connection.bufferedReader.readLine();
System.out.println(String.format("Answer from server: %s", answer));
} catch (final IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test() {
final String[] arguments = new String[] { "9999" };
final Thread tread = new Thread() {
@Override
public void run() {
ThreadedServer.main(arguments);
};
};
tread.start();
Client.main(arguments);
}
}
Pros:
Cons:
Connection
class needed AutoCloseable
instances in the Connection
class may be forgotten (error prone) Do you have feedback or additions to the pros and cons? Are there more weaknesses?
That's the result of the discussion below:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.Test;
public final class ThreadedServerTest3 {
private static final class ThreadedServer implements Runnable {
private final Socket socket;
private ThreadedServer(final Socket socket) {
this.socket = socket;
}
public static void main(final String arguments[]) {
final int port = arguments.length > 0 ? Integer.parseInt(arguments[0]) : 9999;
try (final ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
final Socket socket = serverSocket.accept();
final ThreadedServer server = new ThreadedServer(socket);
final Thread thread = new Thread(server);
thread.start();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// @formatter:off
try (
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7027552 and
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7013420:
Socket socket = this.socket;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
) {
// @formatter:on
final String request = bufferedReader.readLine();
System.out.println(String.format("Received from client: %s", request));
printWriter.println("Hello client!");
printWriter.flush();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private static final class Client {
public static void main(final String arguments[]) {
final int port = arguments.length > 0 ? Integer.parseInt(arguments[0]) : 9999;
// @formatter:off
try (
Socket socket = new Socket(InetAddress.getByName(null), port);
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()))
) {
// @formatter:on
printWriter.println("Hello server!");
printWriter.flush();
final String answer = bufferedReader.readLine();
System.out.println(String.format("Answer from server: %s", answer));
} catch (final IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test() {
final String[] arguments = new String[] { "9999" };
final Thread tread = new Thread() {
@Override
public void run() {
ThreadedServer.main(arguments);
};
};
tread.start();
Client.main(arguments);
}
}
Pros:
Cons:
A resource declared in a ResourceSpecification is implicitly declared final (§4.12.4) if it is not explicitly declared final.
Therefore, I recommend:
try (
Socket socket = this.socket;
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()))
) {
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.