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

Posted

技术标签:

【中文标题】Java - 通过套接字发送文件(聊天客户端 -> 服务器 -> 另一个聊天客户端)【英文标题】:Java - send a file over socket(chat client -> server -> another chat client) 【发布时间】:2012-12-15 17:17:20 【问题描述】:

所以我正在开发一个聊天程序,现在我想添加一个发送文件选项。 我尝试添加它并且它工作但在文件传输完成后,两个套接字都关闭(两个客户端的套接字)。 这是聊天客户端的 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 中询问了如何让我的代码变得更好。关于套接字关闭,您重复了一个更简洁的问题***.com/questions/2826311/… 【参考方案1】:

    关闭finally 块中的所有Objects (try - catch - finally)

    Concurency in Swing 有问题,但有三种方法

    a) 正确的方法

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

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

    b) 捷径,有效但不正确

    直接将 Swing GUI 代码封装到 invokeLater()

【讨论】:

【参考方案2】:

当您关闭输出流时,套接字会关闭。如果要保持套接字打开,请不要关闭流。参考看看SocketOutputStream.java

【讨论】:

以上是关于Java - 通过套接字发送文件(聊天客户端 -> 服务器 -> 另一个聊天客户端)的主要内容,如果未能解决你的问题,请参考以下文章

无法通过 Java 中的套接字发送大文件

套接字编程/通过无线发送二进制图像数据

java 通过TCPUDP 协议实现多人聊天,点对点,文件传送-----分服务器端和客户端

Java中使用UDP实现简单的聊天功能

使用Django Channels 2上传文件

在Python中使用select()方法进行客户端/服务器聊天