Java-通过socket发送文件(聊天客户端->服务器->另一个聊天客户端)

3

我正在开发一个聊天程序,现在想添加发送文件的选项。我尝试添加它并且它也起作用了,但是在文件传输完成后,两个客户端的套接字都会关闭。以下是聊天客户端的SSCCE代码:

public class SSCCEChatClient extends JFrame {

    private JPanel contentPane;
    private JTextField inputUsernameField;
    private JTextArea textArea;
    String serversock = "84.252.37.82";
    String username;
    Socket sock;
    BufferedReader reader;
    PrintWriter writer;
    InputStreamReader streamreader;

    public class IncomingReader implements Runnable{

        public void run() {
            String stream;
            String[] data;

            try {
                while ((stream = reader.readLine()) != null) {

                    data = stream.split("`");
                     if(data[2].equals("receiveFile")&&(!data[3].equals(username))){
                        DataInputStream in = new DataInputStream(sock.getInputStream());
                        byte[] bytes = new byte[Integer.parseInt(data[1])];
                        in.read(bytes);
                        FileOutputStream fos = new FileOutputStream(System.getProperty("user.home") + "\\Desktop\\" + data[0]);
                        fos.write(bytes);
                        fos.close();
                        in.close();
                        textArea.append("Success!");
                    }else if(data[2].equals("server")){
                        textArea.append(data[0]);
                    }

                }
           }catch(Exception ex) {
           }
        }
    }//Incoming Reader

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    SSCCEChatClient frame = new SSCCEChatClient();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public SSCCEChatClient() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        textArea = new JTextArea();
        contentPane.add(textArea, BorderLayout.SOUTH);

        JButton btnNewButton = new JButton("Send File");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                File transferFile = new File (System.getProperty("user.home") + "\\Desktop\\PNG\\Night.png");
                byte [] bytearray  = new byte [(int)transferFile.length()];
                try {
                    BufferedInputStream bin = new BufferedInputStream(new FileInputStream(transferFile));
                    bin.read(bytearray,0,bytearray.length);
                    DataOutputStream os = new DataOutputStream(sock.getOutputStream());
                    writer.println(transferFile.getName() + "`" + transferFile.length() + "`receiveFile`" + username);
                    writer.flush();
                    os.write(bytearray,0,bytearray.length);
                    os.flush();
                    bin.close();
                    os.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
                System.out.println("File transfer complete");
            }
        });
        contentPane.add(btnNewButton, BorderLayout.CENTER);

        JButton btnNewButton_1 = new JButton("Connect");
        btnNewButton_1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    username = inputUsernameField.getText();
                    sock = new Socket(serversock, 5000);
                    streamreader = new InputStreamReader(sock.getInputStream());
                    reader = new BufferedReader(streamreader);
                    writer = new PrintWriter(sock.getOutputStream());
                    Thread IncomingReader = new Thread(new IncomingReader());
                    IncomingReader.start();
                    writer.println(username + "``connect");
                    writer.flush();

                } catch (Exception ex) {
                    textArea.append("\nCannot Connect!");
                }
            }
        });
        contentPane.add(btnNewButton_1, BorderLayout.WEST);

        inputUsernameField = new JTextField();
        contentPane.add(inputUsernameField, BorderLayout.NORTH);
        inputUsernameField.setColumns(10);
    }

}

下面是服务器端代码:

public class SSCCEServer {
    static ArrayList<PrintWriter> clientOutputStreams;
    static ArrayList<DataOutputStream> clientDataOutputStreams;
    static ArrayList<String> onlineUsers = new ArrayList<>();
    public class ClientHandler implements Runnable  {
        BufferedReader reader;
        Socket sock;
        PrintWriter client;


        public ClientHandler(Socket clientSocket, PrintWriter user) {
        // new inputStreamReader and then add it to a BufferedReader
            client = user;
            try {
                sock = clientSocket;
                System.out.println(clientSocket.getRemoteSocketAddress().toString() + " - ");
                InputStreamReader isReader = new InputStreamReader(sock.getInputStream());
                reader = new BufferedReader(isReader);
            }
            catch (Exception ex) {
                System.out.println("error beginning StreamReader");
            }

        }

        public void run() {
            String message;
            String[] data;
            try {
                while ((message = reader.readLine()) != null) {

                    data = message.split("`");

                    if(data[2].equals("receiveFile")){
                        DataInputStream in = new DataInputStream(sock.getInputStream());
                        byte[] bytes = new byte[Integer.parseInt(data[1])];
                        in.read(bytes);
                        tellEveryone(data[0] + "`" + data[1] + "`" + data[2] + "`" + data[3]);
                        for(DataOutputStream dos:clientDataOutputStreams){
                            try {
                                dos.write(bytes);
                                dos.close();
                            }
                            catch (Exception ex) {
                                System.out.println("error telling everyone");
                            }
                        }
                        tellEveryone("File transfer complete``server");
                    }else if(data[2].equals("connect")){
                        System.out.println(data[0] + "has connected.");
                    }else {
                        System.out.println("No Conditions were met.");
                      }
                 }
            }
            catch (Exception ex) {
                System.out.println("lost a connection");
                System.out.println(ex.getMessage().toString());
                clientOutputStreams.remove(client);
            }
        }
    }
    public void tellEveryone(String message) {
    // sends message to everyone connected to server
        for(PrintWriter writer:clientOutputStreams){
                try {
                    writer.println(message);
                    //pop("Sending: " + message);
                    writer.flush();
                }
                catch (Exception ex) {
                    System.out.println("error telling everyone");
                }
        }
       }
    public static void main(String[] args) {
        new SSCCEServer().go();
    }
    public void go(){
        clientOutputStreams = new ArrayList<PrintWriter>();
        clientDataOutputStreams = new ArrayList<>();

        try {
            @SuppressWarnings("resource")
            ServerSocket serverSock = new ServerSocket(5000);
            while(true){
                Socket clientSock = serverSock.accept();
                PrintWriter writer = new PrintWriter(clientSock.getOutputStream());
                clientOutputStreams.add(writer);
                clientDataOutputStreams.add(new DataOutputStream(clientSock.getOutputStream()));
                Thread listener = new Thread(new ClientHandler(clientSock, writer));
                listener.start();
            }
        } 
        catch (Exception ex)
        {
            System.out.println("error making a connection");
        }
    }

}

抱歉如果这篇文章有点长,但这已经是我可以缩短的最小长度了。另外,这并不是整个内容,因为它缺少了发送文本方法,但这并不影响SSCCE。我使用服务器端的“tellEveryone”方法演示了发送方法。 此外,“\PNG\Night.png”只是一个例子,您可以创建自己的文件夹和文件以运行SSCCE。 我该如何解决在发送文件后关闭套接字的问题?

如何改进我的代码是在codereview.SE上提出的问题。关于套接字关闭,您可以复制一个更简洁的问题https://dev59.com/Nk3Sa4cB1Zd3GeqPx8Rm - Val
2个回答

3
  1. finally块中关闭所有Objects (try - catch - finally)。

  2. 你遇到了关于Swing并发的问题,但有三种解决方法:

    a) 正确的方式:

    • 将代码封装到Runnable#Thread中(最简单的方法),必须将对Swing GUI的任何更改包装到invokeLater()中。

    • 使用SwingWorker(以标准方式实现),其中方法publishprocessdone可以保证所有events都在EDT上完成。

    b) 快捷方式,可行但不是正确的方式:

    • 直接将Swing GUI代码包装到invokeLater()中。

1

当你关闭输出流时,套接字也会被关闭。如果要保持套接字的打开状态,请不要关闭流。有关详情,请参考SocketOutputStream.java


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接