java.net.SocketException:Socket已关闭OOP设计错误

时间:2017-01-11 23:53:14

标签: java sockets oop

我正在写一个关于服务器 - 客户端通信的银行系统的大学项目。其中一个要求是应用程序的OOP设计。

我在课堂上有一个错误,我正在实现通信方法。

这是客户端代码。服务器端实现类似,并产生相同的错误。

错误的结果是java.net.SocketException: Socket is closed

实现通信方法的类:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

                            // My custom interface
public class DriveableImpl implements Driveable{

    private Socket socket;
    private ObjectOutputStream out; 
    private ObjectInputStream in; 

    protected DriveableImpl() {}

    // Method that set a socket for a current communication instance
    private void setSocket(Socket socket) {
        this.socket = socket;
    }

    // Method to connect to a server that takes parameters required to open socket 
    @Override
    public void connect(String ip, int port){
        try {
            socket = new Socket(ip, port); // open new socket
            setSocket(socket); // set a socket for a current instance to use in other methods
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Method to close connection with a server
    // Oddly, this method is not bugged and can close socket safely
    @Override
    public void disconnect(){
        sendMessage(0); // send a tag number of a method to a server
        if(socket != null){
            try {
                System.out.println("Closing socket: " + socket);
                socket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    // Method produce BUG
    // Method that accepts any java object as a parameter and send it through a socket of a current instance
    @Override
    public void sendMessage(Object message) {
        try{
            out = new ObjectOutputStream(socket.getOutputStream()); // new object stream with a given socket
            out.writeObject(message); // send an object stream through a socket
            out.flush(); // flush the stream to insure all data is sent and stream is free for new objects
            out.close(); // close a stream for current method invocation
        }
        catch(IOException ioException){
            ioException.printStackTrace();
        }
    }

    // Method has a BUG
    // Method that reads objects sent by server through a socket
    @Override
    public Object receiveMessage(){
        try{
            in = new ObjectInputStream(socket.getInputStream()); // creating new stream to read serialized objects from socket stream
            try {
                return in.readObject(); // return an object that was read from stream 
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally{
                in.close(); // close the stream when object is read and returned
            }
        } catch(IOException ioException){
            ioException.printStackTrace();
        }
        return null; // returns null if something went wrong
    }

    // Method that produce BUG
    // Method that processes user registration
    @Override
    public void registration(){
        sendMessage(2); // send a tag number of a method to a server

        try {
            String outcome = (String) receiveMessage(); // waiting for response from server
            System.out.println(outcome);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    // Method that produce BUG
    // Method that processes user login and returns login status as boolean expression
    @Override
    public boolean login(){
        sendMessage(1); // send a tag number of a method to a server

        // Block that receives message from a server about login status, i.e., logged in or reasons for not being logged in
        try {
            outcome = (String) receiveMessage();
            System.out.println(outcome);
            return (boolean)receiveMessage(); // method returns boolean (true/false) that is required 
                                              // to manipulate authentication and issue authorization privileges
        } catch (Exception e) {
            e.printStackTrace();
        } // waiting for response from server

        return false; // if something goes wrong, return false 
    }

} // end of DriveableImpl class

这是调用方法并返回被调用方法返回的标准类:

package ie.gmit.sw.client.methods;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MethodInvoker implements InvocationHandler{

    private Object returnObject = null; // object that will hold any returns from invoked methods
    private final Driveable userInterface; 


    protected MethodInvoker(Driveable ui) {
        this.userInterface = ui;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
                  throws IllegalAccessException, IllegalArgumentException,
                  InvocationTargetException{

        System.out.println("BEFORE");
        returnObject = method.invoke(userInterface, args);
        System.out.println(method.getName());
        System.out.println("AFTER");

        return returnObject;
    }
}

封装MethodInvoker类和实现通信方法的类之间绑定的类:

package ie.gmit.sw.client.methods;

import java.lang.reflect.Proxy;

                        // Extending a class that implements Driveable Interface
                        // I thought it's a good use of polymorphism 
                        // Used later to create instance of this class of a type Driveable to maximize abstraction
public class IssueDriver extends DriveableImpl {

    private DriveableImpl di = new DriveableImpl(); // creating Object with implemented methods that are required to handle
    private MethodInvoker handler = new MethodInvoker(di); // creating Object that handles/executes methods from implementation class
    // Creating driver through which methods can be called
    private final Driveable driver = (Driveable) Proxy.newProxyInstance(Driveable.class.getClassLoader(),
            new Class[] { Driveable.class },
            handler);

    // Constructor that carries driver object
    public IssueDriver() {
        getDriver();
    }

    // returns driver
    private Driveable getDriver() {
        return driver;
    }
}

使用驱动程序对象调用方法的类:

package ie.gmit.sw.client;

import java.util.Scanner;

import ie.gmit.sw.client.methods.Driveable;
import ie.gmit.sw.client.methods.IssueDriver;

public class UserInterface {

    private int option;
    private Scanner input;
    private Driveable driver; // methods driver object 
    private boolean authenticated = false; // Variable to verify authentication and give corresponding authorization rights.


    protected UserInterface() {
        driver = new IssueDriver(); // creating new instance of a methods driver i.e., user options in UI
    }

    protected void menu() {

        input = new Scanner(System.in);

        try {            /*  ip         port*/
            driver.connect("localhost", 2017); // Method that connects to a server. Excepts parameters with ip address and port number.
        } catch (Exception connect) {
            connect.printStackTrace();
        }


        do{

            if(authenticated == false){

                System.out.println("Choose from these choices");
                System.out.println("-------------------------\n");
                System.out.println("1 - Login");
                System.out.println("2 - Register");
                System.out.println("0 - Quit");

                option = input.nextInt();
                switch(option){
                    case 1: System.out.println("Login");
                            try {
                                authenticated = driver.login(); // Method sends an object with user input for login 
                                                                // and returns status response from the server.
                            } catch (Exception login) {
                                // TODO Auto-generated catch block
                                login.printStackTrace();
                            }

                            break;
                    case 2: System.out.println("Registration");
                            System.out.println();
                            try {
                                driver.registration(); // Method sends an object with registration data to the server. 
                                                       // Receives status message from server.
                            } catch (Exception registration) {
                                // TODO Auto-generated catch block
                                registration.printStackTrace();
                            }
                            break;
                    case 0: System.out.println("Quit");
                            try {
                                driver.disconnect(); // Method closes connection with server.
                            } catch (Exception discon){
                                discon.printStackTrace();
                            }
                            break;
                }
            }
            else if(authenticated == true){
                    // .... 
            }

        }while(option != 0);            
    }
}

我试图缩小代码并隔离问题。 类public class UserInterface进一步在Thread的run方法中实例化以运行app。

我是否过度使用OOP设计,或者我错过了一些套接字编程概念?

如果您不明白,请提出问题! 建议一般情况下如何做得更好! 谢谢!

2 个答案:

答案 0 :(得分:0)

关闭输入或输出流应关闭套接字。在sendMessage中,您关闭ObjectOutputStream,它可能会关闭您传递给构造函数的基础输出流。

答案 1 :(得分:0)

Clloing套接字的输入或输出流会关闭套接字。

您不应为每条消息创建一个新的ObjectOutputStream,并且在发送消息后不应将其关闭。接收时ObjectInputStream也是如此。在插座的使用寿命中使用相同的插座。