Программирование сокетов Java

Я создаю простое приложение клиент/сервер, используя java-сокеты и экспериментируя с ObjectOutputStream и т.д.

Я следил за руководством по этому URL-адресу http://java.sun.com/developer/technicalArticles/ALT/sockets/, начиная с середины, когда речь идет о передаче объектов через сокеты.

См. мой код для клиента http://pastebin.com/m37e4c577 Однако это не работает, и я не могу понять, что не работает. Код, закомментированный внизу, напрямую скопирован из учебника, и это работает, когда я просто использую его вместо создания объекта клиента.

Может ли кто-нибудь увидеть что-нибудь, что я делаю неправильно?


person Malachi    schedule 22.01.2009    source источник
comment
Вам нужно раскомментировать его. А если серьезно, в чем ошибка компиляции?   -  person Filip Ekberg    schedule 23.01.2009
comment
Ошибки нет — просто это не работает с моим раскомментированным кодом, и я не понимаю, почему? Закомментированный код работает - я только что отделил его и поместил в реальный объект... это сводит меня с ума.   -  person Malachi    schedule 23.01.2009


Ответы (6)


Проблема в том, в каком порядке вы создаете потоки:

На сервере из статьи (который, как я предполагаю, вы используете), когда открывается новое соединение, сервер открывает сначала входной поток, а затем выходной поток:

public Connect(Socket clientSocket) {
 client = clientSocket;
 try {
  ois = new ObjectInputStream(client.getInputStream());
  oos = new ObjectOutputStream(client.getOutputStream());
 } catch(Exception e1) {
     // ...
 }
 this.start();
}

В коде примера с комментариями используется обратный порядок: сначала устанавливается поток вывода, а затем поток ввода:

// open a socket connection
socket = new Socket("localhost", 2000);
// open I/O streams for objects
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());

Но ваш код делает это наоборот:

server = new Socket(host, port);
in = new ObjectInputStream(server.getInputStream());
out = new ObjectOutputStream(server.getOutputStream());

Установление пары выходной поток/входной поток остановится до тех пор, пока они не обменялись информацией о квитировании, поэтому вы должны соответствовать порядку создания. Вы можете сделать это, просто поменяв местами строки 34 и 35 в вашем примере кода.

person Mike Houston    schedule 23.01.2009

Вы нигде не пишете объект.

Посмотрите эту ссылку еще раз, где-то вы должны написать:

 oos.writeObject( new Date() );

В вашем коде у вас есть только

ois.readObject();

Поэтому

person OscarRyz    schedule 22.01.2009
comment
Размещенный код включает только клиента, поэтому ему не нужно писать объект. - person Mike Houston; 23.01.2009

Просто напоминание.

Когда вы используете ObjectOutputStream, имейте в виду, что он хранит кеш ссылок. Если вы напишите объект, измените содержимое объекта, а затем снова отправите тот же объект, вы получите повторяющиеся данные. Например:

List list = new ArrayList();
list.add("value1");
out.writeObject(list);
list.clear();
list.add("value2");
out.writeObject(list);

Создаст на стороне клиента два списка со строкой "value1".

Чтобы избежать этого, необходимо вызвать метод reset для сброса кеша потока при многократной записи одной и той же ссылки на объект:

List list = new ArrayList();
list.add("value1");
out.writeObject(list);
out.reset();
list.clear();
list.add("value2");
out.writeObject(list);
person jassuncao    schedule 23.01.2009

Возможно, вам захочется сначала изучить самое основное.

Вот пример, который я только что закодировал.

Он запускает сервер, который обслуживает только ОДНОГО клиента, отправляет объект и умирает.

Когда пользователь (вы) нажимает ввод, создается новый клиент, он подключается к ранее созданному серверу и считывает объект, который отправит сервер.

Здесь не обрабатывается исключение. Просто чтобы упростить ситуацию, но это НЕ тот способ, которым следует обрабатывать исключения.

Когда вы поймете все концепции здесь, вам будет легче понять те, что описаны в учебнике.

import java.io.*;
import java.net.*;
import java.util.*;

public class SimpleServer implements Runnable { 

     // Creates the server, send a "date" and die.
    public void run() { 
        try {
            ServerSocket server = new ServerSocket( 8090 );
            Date creationDate = new Date();
            System.out.println("(Server) Server is ready " 
                                 + "and running at " + creationDate );

            Socket uniqueClient = server.accept();

            // We will write using this "chained" object.
            ObjectOutputStream out = new ObjectOutputStream( 
                                                 uniqueClient.getOutputStream());

            out.writeObject( creationDate );


            // close all, at this point forget about the exceptions.
            // this is lesson #1      
            out.close();

            uniqueClient.close();

            server.close();        

            System.out.println("(Server) The server is down");
        }catch( IOException ioe ) {}

     }

    public static void main ( String [] args ) throws IOException ,
                                                 ClassNotFoundException { 

         Thread serverThread = new Thread( new SimpleServer() );

         serverThread.start(); // start the server thread ... doh..

         System.out.println("(Client) Press enter when you want "+ 
                               " to connect to the server...");

         Scanner scanner = new Scanner( System.in );

         scanner.nextLine();

         Socket client = new Socket("localhost", 8090 );

         // Read from this object.
         ObjectInputStream in = new ObjectInputStream( client.getInputStream() );

         Date date = ( Date ) in.readObject();

         System.out.println("(Client) Current time is:          " + new Date() );
         System.out.println("(Client) Object read from server : " + date );

         in.close();
         client.close();

    }
}

Надеюсь, это поможет.

person OscarRyz    schedule 23.01.2009

Если бы вы попробовали отладчик, он бы сказал вам, в чем проблема (возможно, не почему).

Проблема заключается в том, что ObjectOutputStream записывает заголовок, а ObjectInputStream читает этот заголовок. Сначала вы создали ObjectInputStream, что означает, что он пытается прочитать заголовок, который никогда не будет записан.

Решение: Всегда сначала создавайте ObjectOutputStream и сбрасывайте() его перед созданием ObjectInputStream.

person Peter Lawrey    schedule 23.01.2009

Лучше открыть outputStream, потому что выходной поток не блокируется. Затем у вас есть входной поток, который ожидает Stream. После всех потоков вы пишете в поток и сбрасываете его — outputStream.flush() для отправки байтов данных. Вам также понадобится метод на другом конце для чтения ввода, будь то просто inputStream.read(), который считывает каждый байт как целое число для char, или с использованием BufferedReader или Scanner. Я использовал почти все возможные методы, но наиболее эффективным методом отправки является outputStream.write(String), который записывает в поток последовательность chars как bytes, а чтение inputStream.read() считывает одно char. Надеюсь, это поможет.

person kyle england    schedule 07.10.2014