繁体   English   中英

使用Java 1.7进行套接字处理的最新技术是什么?

[英]What is the state of the art in socket handling with Java 1.7?

我正在寻找使用Java 1.7的适当功能( try-with-resources )实现客户端/服务器套接字通信的最佳模式。 必须确保所有线程和资源都已完全关闭和释放。 应考虑有效的Java / Clean代码项以及简单的可调试性(每行中只有一个语句)。

任何反馈或讨论将不胜感激。

第一种方法

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);
    }
}

优点:

  • 减少代码行数

缺点:

  • 大型try-with-resources块(Eclipse格式化程序中不支持)
  • 第二个Socke t实例需要使用try-with-resources
  • 由于InetAddress#getByName(null)UnknownHostException导致深度嵌套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);
    }
}

优点:

  • 清晰可读的资源块尝试
  • 支持Eclipse格式化程序

缺点:

  • 更多代码行
  • 需要自己的Connection
  • 关闭Connection类中的AutoCloseable实例可能会被遗忘(容易出错)

您对优缺点有反馈或补充吗? 还有更多的弱点吗?

第三种方法

这是以下讨论的结果:

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);
    }
}

优点:

  • 减少代码行数
  • 对于复杂且大部分可读的资源块尝试都不行
  • 没有内在的例外

缺点:

  • Eclipse格式化程序中没有支持
  • 一些违反规则的行“每行只有一个语句”(简单的可调试性)
  • 最终修饰符在try-with-resources中是多余的,因为规范写道

    如果未明确声明为final,则在ResourceSpecification中声明的资源将被隐式声明为final(第4.12.4节)。

  • 关闭阅读器也将关闭底层流,对于关闭作者也是如此。 因此,没有必要将中间流声明为资源。
  • 您的第二种方法错误地忽略了关闭资源时抛出的异常。 关闭一个流冲洗它,如果没有超过所有数据将被发送,这显然保证抛出异常。 相比之下,try-with-resources语句确实传播了关闭时抛出的异常(可能是被抑制的异常,请参阅规范了解详细信息 )。

因此,我建议:

try (
    Socket socket = this.socket;
    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()))
) {

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM