Java Sockets - получение, но не то, что было отправлено! - PullRequest
4 голосов
/ 05 декабря 2010

Я пытался отладить это в течение 2 часов, и я просто не могу объяснить это. У меня есть сервер и клиент. (сервер управляет некоторыми Аукционами).

Что происходит:

  1. Клиент запрашивает что-то, сервер отправляет данные обратно, а клиент получает их очень хорошо.

  2. Клиент отправляет что-то на сервер, а сервер обновляет некоторые данные.

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

Отправляемые данные - это просто Java-бин с двумя списками. И код:

 // CLIENT CLASS
// creates socket, sends and listens on the socket
// listening is done on a separate thread
public class ServerConnector {

private Socket socket = null;
private ObjectOutputStream out = null;
private Display display;
private ServerListener listener;
public ServerConnector(Display display) {
    this.display = display;
        try {
            socket = new Socket("localhost",33333);
            out = new ObjectOutputStream(socket.getOutputStream());
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        listener = new ServerListener(socket, display);
        new Thread(listener).start();


}



public void sendRequest(Request request) {
    try {
        out.writeObject(request);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

class ServerListener implements Runnable {
    private Socket socket;
    private ObjectInputStream in = null;
    private Display display;
    public ServerListener(Socket socket,Display display) {
        this.socket = socket;
        this.display = display;
        try {
            in = new ObjectInputStream(socket.getInputStream());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        Response response =null;

        try {
            while ((response = (Response)in.readObject()) != null) {
                if (response.getCars().size() > 0) {
                    display.showAvailableCars(response.getCars());
                }
                if(response.getAucs().size() > 0) {
                    List<Auction> auctionz  = response.getAucs();//HERE 1st time it gets the GOOD data, 2nd time should get UPDATED DATA but instead receives the OLD DATA (same as 1st time).
                    display.showOpenAuctions(auctionz);
                }
                response = null;
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

}
//CLIENT CLASS
// controls when something should be sent, and print out responses
public class Display {
Scanner console = new Scanner(System.in);
ServerConnector server = new ServerConnector(this);
List<Car> cars;
List<Auction> aucs;

public void show() {
    int opt = 0;
        System.out.println("1. Show available cars for auction.");
        System.out.println("2. Show open auctions.");
        opt = console.nextInt();
        Request request = new Request();
        if (opt == 1)
            request.setRequest(Request.GET_CARS);
        if (opt == 2) {
            request.setRequest(Request.GET_OPEN_AUCTIONS);
        }
        server.sendRequest(request);

    }


public void showAvailableCars(List<Car> cars) {
    int i = 0;
    for (Car c : cars ){
        i++;
        System.out.println(i +". " + c.getMaker() + " " + c.getModel() + " price: " + c.getPrice());
    }
    System.out.println("Select car to open Auction for:");
    int selectedCar = console.nextInt();
    if (selectedCar != 0) {
        if (selectedCar <= cars.size()) {
            Request request= new Request();
            request.setRequest(Request.OPEN_AUCTION);
            Car c = cars.get(selectedCar-1);
            request.setCar(c);
            server.sendRequest(request);
        }
    }
    show();
}


public void setCars(List<Car> cars) {
    this.cars = cars;

}


public void showOpenAuctions(List<Auction> aucs2) {
    int i = 0;
    for (Auction auc : aucs2) {
        i++;
        System.out.println(i+ ". " + auc.getCar().getModel() + " " + auc.getCar().getMaker() + " last price: " + auc.getPrice());
    }
    System.out.println("You can now make offers");
    System.out.println("Input auction number:");
    int selectedAuction = 0;
    selectedAuction = console.nextInt();
    if (selectedAuction > 0 && selectedAuction <= aucs2.size()) {
        System.out.println("Offer new price:");
        int price = console.nextInt();
        Request request= new Request();
        request.setRequest(Request.MAKE_OFFER);
        request.setAuctionId(aucs2.get(selectedAuction-1).getId());
        request.setPrice(price);
        server.sendRequest(request);
    }
    show();

}


public void setOpenAuctions(List<Auction> aucs2) {
    this.aucs = aucs2;

}

}
// SERVER CLASS : send and receives
public class ClientManager implements Runnable {

private AuctionManager manager = new AuctionManagerImpl();
private Socket client;
private ObjectInputStream in = null;
private ObjectOutputStream out = null;


public ClientManager(Socket socket) {
    this.client = socket;
     try {
          in = new ObjectInputStream(client.getInputStream());
          out = new ObjectOutputStream(client.getOutputStream());
     } catch(Exception e1) {
             try {
                 e1.printStackTrace();
                client.close();
       }catch(Exception e) {
               System.out.println(e.getMessage());
             }
             return;
         }
}

@Override
public void run() {
    Request req = null;
    try {
            while ((req = (Request)in.readObject()) != null) {
                if (req.getRequest() != null) {
                    if (req.getRequest().equals(Request.GET_CARS)) {
                        Response response = new Response();
                        response.setCars(manager.getAvailableCars());
                        out.writeObject(response);
                        continue;
                    }

                    if (req.getRequest().equals(Request.OPEN_AUCTION)) {
                        manager.openAuction(req.getCar());
                        continue;
                    }

                    if (req.getRequest().equals(Request.GET_OPEN_AUCTIONS)) {
                        Response response = new Response();
                        response.setAucs(manager.getHoldedAuctions()); //this line ALWAYS sends to the client GOOD, UPDATED DATA

                        out.writeObject(response);
                        out.flush();
                        continue;
                    }
                    if (req.getRequest().equals(Request.MAKE_OFFER)) {
                        Auction auction = manager.getOpenAuction(req.getAuctionId());
                        manager.updateAuction(auction, req.getPrice(),client.getRemoteSocketAddress().toString());
                        continue;
                    }
            }
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

}

Ответы [ 3 ]

3 голосов
/ 05 декабря 2010

Это может быть потому, что вы используете ObjectOutputStreams. Помните, что ObjectOutputStreams будет кэшировать все записанные в них объекты, так что если тот же объект будет записан снова в будущем, он может написать обратную ссылку вместо перезаписи всего объекта. Это необходимо при написании графа объектов.

Ваш фрагмент кода:

if (req.getRequest().equals(Request.GET_CARS)) {
    Response response = new Response();
    response.setCars(manager.getAvailableCars());
    out.writeObject(response);
    continue;
}

пишет объект, возвращенный manager.getAvailableCars(). При следующем получении запроса записывается тот же объект (но теперь с другим содержимым), но ObjectOutputStream не знает о новом содержимом, поэтому он просто записывает обратную ссылку. ObjectInputStream на другом конце видит обратную ссылку и возвращает тот же объект, который читал в прошлый раз, то есть исходные данные.

Вы можете исправить это, вызывая ObjectOutputStream.reset() после каждого ответа. Это очистит кэш потока.

2 голосов
/ 05 декабря 2010

См. ObjectOutputStream.writeUnshared () и .reset ().

0 голосов
/ 05 декабря 2010

Хорошо. Я только что нашел решение. отсюда http://java.sun.com/developer/technicalArticles/ALT/sockets/:

Подводный камень сериализации объектов

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

Если объект записывается в поток, а затем изменяется и записывается во второй раз, изменения не будут замечены при десериализации потока. Опять же, причина в том, что последующая запись приводит к записи дескриптора, но измененный объект не копируется в поток. Чтобы решить эту проблему, вызовите метод ObjectOutputStream.reset, который отбрасывает память об отправке объекта, поэтому последующие записи копируют объект в поток. OutOfMemoryError может быть выдан после записи большого количества объектов в ObjectOutputStream. Причина этого заключается в том, что в хеш-таблице хранятся ссылки на объекты, которые в противном случае могли бы быть недоступны приложению. Эту проблему можно решить, просто вызвав метод ObjectOutputStream.reset для сброса таблицы объекта / дескриптора в ее начальное состояние. После этого вызова все ранее написанные объекты будут иметь право на сборку мусора. Метод сброса сбрасывает состояние потока так, как если бы оно было только что создано. Этот метод не может быть вызван во время сериализации объектов. Неправильные вызовы этого метода приводят к IOException.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...