Сеть Java: объясните InputStream и OutputStream в сокете

Я создал сервер с помощью ServerSocket. После этого я создал клиент, используя Socket, и подключиться к этому серверу.

после этого я делаю "некоторые вещи" с InputStream и OutputStream берется из объекта Socket. Но я действительно не понимаю inputStream и outputStream так много. Вот мой простой код :

private Socket sock = null;
private InputStream sockInput = null;
private OutputStream sockOutput = null;

...
            String msg = "Hello World";
            byte[] buffer = null;

            try {
                sockOutput.write(msg.getBytes(), 0, test.length());
                sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
                buffer = new byte[test.length()];
                sockInput.read(buffer, 0, test.length());
                System.out.println(new String(buffer));
                sockInput.read(buffer, 0, test.length());
                System.out.println(new String(buffer));
            } catch (IOException e1) {
                e1.printStackTrace();
                }

результатом будет: "Hello World"и" Hello StackOverFlow".

вот код на стороне сервера :

private int serverPort = 0;
    private ServerSocket serverSock = null;

    public VerySimpleServer(int serverPort) {
        this.serverPort = serverPort;

        try {
            serverSock = new ServerSocket(this.serverPort);
        }
        catch (IOException e){
            e.printStackTrace(System.err);
        }
    }

    // All this method does is wait for some bytes from the
    // connection, read them, then write them back again, until the
    // socket is closed from the other side.
    public void handleConnection(InputStream sockInput, OutputStream sockOutput) {
        while(true) {
            byte[] buf=new byte[1024];
            int bytes_read = 0;
            try {
                // This call to read() will wait forever, until the
                // program on the other side either sends some data,
                // or closes the socket.
                bytes_read = sockInput.read(buf, 0, buf.length);

                // If the socket is closed, sockInput.read() will return -1.
                if(bytes_read < 0) {
                    System.err.println("Server: Tried to read from socket, read() returned < 0,  Closing socket.");
                    return;
                }
                System.err.println("Server: Received "+bytes_read
                                   +" bytes, sending them back to client, data="
                                   +(new String(buf, 0, bytes_read)));
                sockOutput.write(buf, 0, bytes_read);
                // This call to flush() is optional - we're saying go
                // ahead and send the data now instead of buffering
                // it.
                sockOutput.flush();
            }
            catch (Exception e){
                System.err.println("Exception reading from/writing to socket, e="+e);
                e.printStackTrace(System.err);
                return;
            }
        }

    }

    public void waitForConnections() {
        Socket sock = null;
        InputStream sockInput = null;
        OutputStream sockOutput = null;
        while (true) {
            try {
                // This method call, accept(), blocks and waits
                // (forever if necessary) until some other program
                // opens a socket connection to our server.  When some
                // other program opens a connection to our server,
                // accept() creates a new socket to represent that
                // connection and returns.
                sock = serverSock.accept();
                System.err.println("Server : Have accepted new socket.");

                // From this point on, no new socket connections can
                // be made to our server until we call accept() again.

                sockInput = sock.getInputStream();
                sockOutput = sock.getOutputStream();
            }
            catch (IOException e){
                e.printStackTrace(System.err);
            }

            // Do something with the socket - read bytes from the
            // socket and write them back to the socket until the
            // other side closes the connection.
            handleConnection(sockInput, sockOutput);

            // Now we close the socket.
            try {
                System.err.println("Closing socket.");
                sock.close();
            }
            catch (Exception e){
                System.err.println("Exception while closing socket.");
                e.printStackTrace(System.err);
            }

            System.err.println("Finished with socket, waiting for next connection.");
        }
    }

    public static void main(String argv[]) {
        int port = 54321;
        VerySimpleServer server = new VerySimpleServer(port);
        server.waitForConnections();
    }

мой вопрос :

  1. когда я использую sockOutput.write и я могу вернуть это сообщение обратно sockInput.read. Значит, сообщение сохранено, так? Если это правда, сохраняется ли он на сервере, который я создал или просто сохранил в какой-то другой вещи, такой как Socket Object.

  2. если я написал в строку сокета A1, A2,... An поэтому я получу A1, A2,... Строка соответственно, верно?

2 ответов


сокет это абстракция, которую вы используете, чтобы говорить что-то по сети. См. диаграмму ниже...

в Java, чтобы отправить данные через сокет, вы получаете OutputStream (1) из него и напишите в OutputStream (вывода некоторых данных).

чтобы прочитать данные из сокета, вы получаете его InputStream, и считывать входные данные из этого второго потока.

вы можете думать о потоках как о паре односторонняя трубы, подключенные к розетке на стене. Что происходит по ту сторону стены это не ваша проблема!

в вашем случае сервер имеет другой сокет (другой конец соединения) и другую пару потоков. Он использует свой InputStream (2) читать из сети, и его OutputStream (3) писать то же самое данные обратно по сети к вашему клиенту, который читает его снова через его InputStream (4) завершение поездки туда и обратно.

      Client                                                     Server

1. OutputStream -->\                                     /--> 2. InputStream -->
                    Socket <--> network <--> ServerSocket                       |
4. InputStream  <--/                                     \<--3. OutputStream <--

обновление: в ответ на комментарий:

обратите внимание, что потоки и сокеты просто отправляют необработанные байты; они не имеют понятия о "сообщении" на этом уровне абстракции. Поэтому, если вы отправляете X байтов и еще X байтов, а затем читаете X байтов и читаете еще X байтов, ваша система ведет себя так, как будто есть два сообщения, потому что именно так вы разделили байты.

если вы отправляете X байтов и еще X байтов, а затем читаете ответ длины 2X, то вы может смогите прочитать одиночное совмещенное "сообщение", но по мере того как вы замечали, базовая реализация потоков может выбирать, когда доставлять куски байтов, поэтому она может возвращать X байтов, затем X байтов, позже, или 2X сразу, или 0.5 X четыре раза...


InputStream и OutputStream - это два совершенно отдельных потока. То, что вы пишете в одном, не имеет априори отношение к тому, что Вы читаете от другого. The InputStream дает вам любые данные, которые сервер решает отправить вам. Я также хотел бы прокомментировать этот кусок кода:

sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());

вы используете длину строки test (не показано в вашем коде), который не имеет ничего общего с массивом байтов, который вы передаете в качестве первого аргумента. Это может вызвать ArrayIndexOutOfBoundsException или усечение вашего предполагаемого сообщения.

дополнительные комментарии к обновленному вопросу

просматривая ваш серверный код, он не совсем правильно написан. Вы должны иметь try { handleConnection(...); } finally { socket.close(); } для обеспечения надлежащей очистки после ошибки, а также при обычном заполнении. Ваш код никогда ничего не закрывает на стороне сервера.

наконец, и самое главное, весь ваш код написан таким образом, что может привести к взаимоблокировке. Обычно вам нужен отдельный поток для чтения и записи; в противном случае может произойти следующее:

  1. вы пытаетесь записать некоторые данные на выход;
  2. сервер читает его и пытается ответить данными в вашем вводе;
  3. но, поскольку буферы слишком малы, вам не удается отправить все, потому что сервер хочет сначала отправить что-то вам, а затем получить остальное; но вы не добираетесь до принимающей части, прежде чем вы отправили все, что у вас есть полученный.