简体   繁体   English

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

[英]Java - send a file over socket(chat client -> server -> another chat client)

So I'm working on a chat program and now I want to add a send file option. 所以我正在开发一个聊天程序,现在我想添加一个发送文件选项。 I tried adding it and it worked but right after the file transfer finishes, both of the sockets close(the sockets of the two clients). 我尝试添加它并且它工作但文件传输完成后,两个套接字关闭(两个客户端的套接字)。 Here is an SSCCE for the Chat client: 这是聊天客户端的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);
    }

}

and here is the Server side: 这是服务器端:

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");
        }
    }

}

Sorry if it's really long but this is the minimal amount I could bring it to. 对不起,如果它真的很长,但这是我可以带来的最小数量。 Also it's not the whole thing because it misses the send text method but that doesn't affect the SSCCE. 此外,它不是整件事,因为它错过了发送文本方法但不影响SSCCE。 I've demonstrated the send method with the method 'tellEveryone' from the server side. 我已经从服务器端使用'tellEveryone'方法演示了send方法。 Also, the "\\PNG\\Night.png" is just an example, you can make your own folder and file in order to run the SSCCE. 此外,“\\ PNG \\ Night.png”只是一个示例,您可以创建自己的文件夹和文件以运行SSCCE。 What can I do to fix the closing of the socket after the file is send? 发送文件后,如何修复套接字的关闭?

  1. close all Objects in finally block ( try - catch - finally ) 关闭finally块中的所有Objectstry - catch - finally

  2. you have got issue with Concurency in Swing , but there are three ways 在Swing中遇到了Concurency问题,但有三种方法

    a) proper ways a)正确的方法

    • wrap code to the Runnable#Thread (easiest), have to wrap any changes to the Swing GUI into invokeLater() 将代码包装到Runnable#Thread (最简单),必须将对Swing GUI的任何更改包装到invokeLater()

    • use SwingWorker (implemented in standard ways), where methods publish , process and done quite guarante that all events are done on EDT 使用SwingWorker (以标准方式实现),方法publishprocessdone保证所有events都在EDT上完成

    b) shortcuts, works but not proper of ways b)捷径,有效但不合适

    • wrap Swing GUI code into invokeLater() directly 直接将Swing GUI代码包装到invokeLater()

The socket closes when you close the output stream. 关闭输出流时,套接字将关闭。 If you want to keep the socket open, do not close the stream. 如果要保持套接字打开,请不要关闭流。 For reference have a look at SocketOutputStream.java 有关参考,请查看SocketOutputStream.java

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM