无法通过java ObjectOutputStream发送对象

时间:2018-01-11 23:25:35

标签: java serialization

我正在创建一个多客户聊天服务器程序。消息作为消息对象发送。但是,ServerClient类没有收到消息,我不知道为什么。

客户:

package client;

import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.*;

import constraints.Constraints;
import message.Message;
import tools.Tools;
import window.ApplicationWindow;

@SuppressWarnings("serial")
public class Client extends ApplicationWindow {
    private static final int portNumber = 4444;

    private boolean send = false;

    private String userName;
    private String serverHost;
    private int serverPort;

    private GridBagLayout layout;
    private JTextArea textArea;
    private Constraints c_textArea;
    private JTextField textField;
    private Constraints c_textField;

    public static void main(String[] args){

        String readName = System.getProperty("user.name");
        Client client = new Client(readName, portNumber);
        client.startClient();
    }

    private Client(String userName, int portNumber){
        super("ChatApp");
        Tools.setLookAndFeel();
        this.userName = userName;
        this.serverPort = portNumber;
        try {
    this.serverHost = InetAddress.getLocalHost().getHostAddress();
    }catch(Exception e) {

    }

        layout = new GridBagLayout();
        setLayout(layout);

        textArea = new JTextArea();
        textArea.setColumns(75);
        textArea.setRows(20);
        c_textArea = new Constraints(0,1);

        textArea.setLineWrap(true);
        textArea.setEditable(false);
        textArea.setVisible(true);

        JScrollPane scroll = new JScrollPane (textArea);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        add(scroll, c_textArea);

        textField = new JTextField();
        textField.setColumns(50);
        textField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                send = true;
            }
        });
        c_textField = new Constraints(0,2);
        add(textField, c_textField);

        pack();
        setVisible(true);
        setResizable(false);
    }

    public void output(String message) {
        this.textArea.setText(this.textArea.getText() + "\n" + message);
    }

    private void startClient() {
        try{
            Socket socket = new Socket(serverHost, serverPort);
            Thread.sleep(1000);
            ServerThread serverThread = new ServerThread(socket, userName, this);
            Thread serverAccessThread = new Thread(serverThread);
            serverAccessThread.start();
            while(serverAccessThread.isAlive() && this.isRunning()) {
                if (send) {
                    Message message = new Message(textField.getText(), "[" + "]", this.userName);
                    serverThread.addNextMessage(message);
                    send = false;
                    textField.setText("");
                }
                Thread.sleep(200);
            }
        }catch(IOException ex){
            output("Could not connect to server!");
        }catch(InterruptedException ex){
            output("Connection interrupted!");
        }
    }


    public void setHost(String serverHost) {
        this.serverHost = serverHost;
    }

}

ServerClient:

package server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;

import message.Message;

public class ServerClient implements Runnable {
    private Socket socket;
    private PrintWriter clientOut;
    private ChatServer server;

    private boolean running;

    public ServerClient(ChatServer server, Socket socket) {
        this.server = server;
        this.socket = socket;
        this.running = true;
    }

    private PrintWriter getWriter(){
        return clientOut;
    }

    public boolean isRunning() {
        return this.running;
    }

    public void stop() {
        this.running = false;
    }

    @Override
    public void run() {
        try{

            this.clientOut = new PrintWriter(socket.getOutputStream(), false);
            clientOut.flush();
            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

            while(!socket.isClosed()){
                try {

                    if (in.available() > 0) {
                        Object i = in.readObject();
                        if (!i.equals(null)) {
                            Message input = (Message) i;
                            String message = input.getText();
                            String time = input.getTimestamp();
                            String user = input.getUser();

                            for(ServerClient thatClient : server.getClients()){
                                PrintWriter thatClientOut = thatClient.getWriter();
                                if(thatClientOut != null){
                                    thatClientOut.write(time + " " + user + ": " + message + "\r\n");
                                    thatClientOut.flush();
                                }
                            }
                        }
                    }
                } catch (ClassNotFoundException e) {
                    System.out.println(e);
                } catch (SocketException ex) {
                    System.out.println(ex);
                    break;
                }
            }

            in.close();
            this.stop();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //public void fileWrite()
}

ChatServer:

package server;

import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

//import javax.swing.JComboBox;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import constraints.Constraints;
import tools.Tools;
import window.ApplicationWindow;

import message.Message;

@SuppressWarnings("serial")
public class ChatServer extends ApplicationWindow {

    private static final int portNumber = 4444;

    private ArrayList<Command> commands;

    private int serverPort;
    private String serverHost;
    private List<ServerClient> clients;
    ServerSocket serverSocket;

    private GridBagLayout layout;
    private JTextArea textArea;
    private Constraints c_textArea;
    private JTextField textField;
    private Constraints c_textField;

    public static void main(String[] args){

        ChatServer server = new ChatServer(portNumber);
        server.startServer();
    }

    public ChatServer(int portNumber){
        super("Server " + portNumber);
        this.serverPort = portNumber;
        try {
            this.serverHost = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            this.serverHost = "";
        }
        Tools.setLookAndFeel();

        initActions();

        layout = new GridBagLayout();
        setLayout(layout);

        textArea = new JTextArea();
        textArea.setColumns(75);
        textArea.setRows(20);
        c_textArea = new Constraints(0,1);

        textArea.setLineWrap(true);
        textArea.setEditable(false);
        textArea.setVisible(true);

        JScrollPane scroll = new JScrollPane (textArea);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        add(scroll, c_textArea);

        textField = new JTextField();
        textField.setColumns(50);
        textField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                process(textField.getText());
            }
        });
        c_textField = new Constraints(0,2);
        add(textField, c_textField);

        pack();
        setVisible(true);
        setResizable(false);
    }

    public List<ServerClient> getClients(){
        return clients;
    }

    private void startServer(){
        clients = new ArrayList<ServerClient>();
        serverSocket = null;
        try {
            InetAddress addr = InetAddress.getByName(this.serverHost);
            serverSocket = new ServerSocket(serverPort, 50, addr);
            acceptClients(serverSocket);
        } catch (IOException e){
            output("[ERROR] COULD NOT LISTEN ON PORT: " + serverPort);
            this.stop();
        }
    }

    private void acceptClients(ServerSocket serverSocket){

        output("[BEGIN] SERVER STARTING ON PORT: " + serverSocket.getLocalSocketAddress());
        while (this.isRunning()) {
            try{
                Socket socket = serverSocket.accept();
                output("[ACCEPT] ACCEPTED CLIENT AT: " + socket.getRemoteSocketAddress());
                ServerClient client = new ServerClient(this, socket);
                Thread thread = new Thread(client);
                thread.start();
                clients.add(client);
            } catch (IOException ex) {
                output("[ERROR] ACCEPT FAILED ON: " + serverPort);
            }
        }
    }

    private void initActions() {
        this.commands = new ArrayList<Command>();
        this.commands.add(new Command() {
            public void run() {
                output("[COMMAND] IP: " + serverSocket.getInetAddress().toString());
            }

            public String getTrigger() {
                return "-ip";
            }
        });
        this.commands.add(new Command() {
            public void run() {

                for (ServerClient client : clients) {
                    if (!client.isRunning()) {
                        clients.remove(client);
                    }
                }

                output("[COMMAND] CLIENTS: " + clients.size());
            }

            public String getTrigger() {
                return "-clients";
            }
        });
    }

    public boolean process(String command) {
        for (Command c : this.commands) {
            if (c.getTrigger().equals(command)) {
                c.run();
                textField.setText("");
                return true;
            }
        }
        return false;
    }

    public void output(String message) {
        this.textArea.setText(this.textArea.getText() + "\n" + message);
    }

}

ServerThread:

package client;

import java.io.*;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Scanner;

import message.Message;

@SuppressWarnings("unused")
public class ServerThread implements Runnable {

    private Socket socket;
    private String userName;
    private boolean isAlived;
    private final LinkedList<Message> messagesToSend;
    private boolean hasMessages = false;
    private Client client;

    public ServerThread(Socket socket, String userName, Client client){
        this.socket = socket;
        this.userName = userName;
        this.client = client;
        messagesToSend = new LinkedList<Message>();
    }

    public void addNextMessage(Message message) {
        synchronized (messagesToSend) {
            hasMessages = true;
            messagesToSend.push(message);
        }
    }

    @Override
    public void run(){
        print("Welcome :" + userName);
        print("Local Port :" + socket.getLocalPort());
        print("Server = " + socket.getRemoteSocketAddress() + ":" + socket.getPort());

        try {
            ObjectOutputStream serverOut = new ObjectOutputStream(socket.getOutputStream());
            serverOut.flush();
            ObjectInputStream serverIn = new ObjectInputStream(socket.getInputStream());

            while(!socket.isClosed()){
                if (serverIn.available() > 0) {
                    try {
                        if (serverIn.readObject() != null) {
                            Message input = (Message) serverIn.readObject();
                        }
                    } catch (ClassNotFoundException e) {
                        System.out.println(e);
                    }
                }
                if(hasMessages){
                    Message nextSend;
                    synchronized(messagesToSend){
                        nextSend = messagesToSend.pop();
                        hasMessages = !messagesToSend.isEmpty();
                    }
                    serverOut.writeObject(nextSend);
                    serverOut.flush();
                }
            }
            serverOut.close();
            serverIn.close();
        } catch(Exception ex){
            ex.printStackTrace();
        }

    }

    public void print(String message) {
        this.client.output(message);
    }
}

我知道客户端能够连接到服务器,并且两者都可以获取消息并成功调用writeObject()方法。但是,消息永远不会被收到。有人能帮助我吗?

1 个答案:

答案 0 :(得分:0)

while(!socket.isClosed()){
    if (serverIn.available() > 0) {
        try {
            if (serverIn.readObject() != null) {
                Message input = (Message) serverIn.readObject();
            }
        } catch (ClassNotFoundException e) {
            System.out.println(e);
        }
    }
    if(hasMessages){
        Message nextSend;
        synchronized(messagesToSend){
            nextSend = messagesToSend.pop();
            hasMessages = !messagesToSend.isEmpty();
        }
        serverOut.writeObject(nextSend);
        serverOut.flush();
    }
}

这都是胡说八道。

    关闭套接字时,
  1. isClosed()返回true。不是在对等设备断开连接时。
  2. 在这里调用available()并且只是在假的情况下旋转循环只是浪费时间。
  3. ObjectInputStream.readObect()仅在您发送空值时返回null。
  4. 您正在调用它两次,将第一个对象读出,然后尝试读取下一个对象。所以你扔掉了每个奇数对象。
  5. 应该更像这样:

    for (;;) {
            try {
                Message input = (Message) serverIn.readObject();
                // process input here
            } catch (EOFException e) {
                break;
            } catch (ClassNotFoundException e) {
                System.out.println(e);
            }
        }
        if(hasMessages){
            Message nextSend;
            synchronized(messagesToSend){
                nextSend = messagesToSend.pop();
                hasMessages = !messagesToSend.isEmpty();
            }
            serverOut.writeObject(nextSend);
            serverOut.flush();
        }
    }
    

    您应该将其拆分为阅读线程和写作线程,因为writeObject()flush()也可以阻止。相同的评论适用于您使用isClosed()available(),测试null等的其他地方。

    hasMessages标志也不起作用。即使随后添加了消息,它仍将是假的。最好删除它,只需依赖同步块内的messagesToSend.isEmpty()

    synchronized(messagesToSend){
        while (!messagesToSend.isEmpty()){
            Message nextSend = messagesToSend.pop();
            serverOut.writeObject(nextSend);
        }
    }
    serverOut.flush();