Chat application using Socket: gửi File từ Client đến Server

Mình đang viết một app chat Server-Client. Mình đang gặp khó khăn trong vấn đề gửi File từ Client đến Server, thay vì gửi File vào nơi mình muốn thì giờ chương trình đọc luôn File. Mong mọi người giúp đỡ. Cảm ơn trước ạ.
Link Project: https://drive.google.com/drive/folders/1ZJUTdrdpM2JQEVgBvexSH3TOEd81T_bA?usp=sharing

ChatPanel.java

import javax.swing.*;
import java.io.*;
import java.net.*;
import javafx.stage.FileChooser;

public class ChatPanel extends javax.swing.JPanel {

    Socket socket = null;
    BufferedReader bf = null;
    DataOutputStream os = null;
    OutputThread t = null;
    String sender;
    String receiver;
    public File file;
    String staffName;
    String fileToSend;
    FileInputStream fis = null;
    BufferedInputStream bis = null;
    OutputStream Os = null;
    ServerSocket servsock = null;
    Socket sock = null;
    static InputStream in;
    static OutputStream out;

    public ChatPanel(Socket s, String sender, String receiver) {
        initComponents();
        socket = s;
        this.sender = sender;
        this.receiver = receiver;
        try {
            bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            os = new DataOutputStream(socket.getOutputStream());
            t = new OutputThread(s, txtMessages, sender, receiver);
            t.start();
        } catch (Exception e) {
        }
    }

    public JTextArea getTxtMessages() {
        return this.txtMessages;
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        panelMessage = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        txtMessage = new javax.swing.JTextArea();
        jPanel1 = new javax.swing.JPanel();
        btnSend = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jPanel3 = new javax.swing.JPanel();
        btnFile = new javax.swing.JButton();
        btnSendFile = new javax.swing.JButton();
        btnEmoji = new javax.swing.JButton();
        txtFile = new javax.swing.JTextField();
        jScrollPane2 = new javax.swing.JScrollPane();
        txtMessages = new javax.swing.JTextArea();
        jPanel4 = new javax.swing.JPanel();
        jScrollPane4 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList<>();

        setLayout(new java.awt.BorderLayout());

        panelMessage.setLayout(new java.awt.GridLayout(1, 0));

        txtMessage.setColumns(20);
        txtMessage.setRows(5);
        jScrollPane1.setViewportView(txtMessage);

        panelMessage.add(jScrollPane1);

        jPanel1.setLayout(new java.awt.GridLayout(3, 0));

        btnSend.setText("Send");
        btnSend.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSendActionPerformed(evt);
            }
        });
        jPanel1.add(btnSend);

        jPanel2.setLayout(null);

        jPanel3.setLayout(null);

        btnFile.setText("...");
        btnFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnFileActionPerformed(evt);
            }
        });
        jPanel3.add(btnFile);
        btnFile.setBounds(0, 0, 40, 32);

        btnSendFile.setText("Send file");
        btnSendFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSendFileActionPerformed(evt);
            }
        });
        jPanel3.add(btnSendFile);
        btnSendFile.setBounds(40, 0, 160, 32);

        jPanel2.add(jPanel3);
        jPanel3.setBounds(0, 0, 200, 32);

        btnEmoji.setText("Emoji");
        btnEmoji.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnEmojiActionPerformed(evt);
            }
        });
        jPanel2.add(btnEmoji);
        btnEmoji.setBounds(200, 0, 92, 32);

        jPanel1.add(jPanel2);

        txtFile.setText("File:");
        jPanel1.add(txtFile);

        panelMessage.add(jPanel1);

        add(panelMessage, java.awt.BorderLayout.PAGE_END);

        txtMessages.setColumns(20);
        txtMessages.setRows(5);
        jScrollPane2.setViewportView(txtMessages);

        add(jScrollPane2, java.awt.BorderLayout.CENTER);

        jList1.setModel(new javax.swing.AbstractListModel<String>() {
            String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
            public int getSize() { return strings.length; }
            public String getElementAt(int i) { return strings[i]; }
        });
        jScrollPane4.setViewportView(jList1);

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane4, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE)
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE)
        );

        add(jPanel4, java.awt.BorderLayout.LINE_END);
    }// </editor-fold>                        

    private void btnSendActionPerformed(java.awt.event.ActionEvent evt) {                                        
        if (txtMessage.getText().trim().length() == 0) {
            return;
        }
        try {
            os.writeBytes(txtMessage.getText());
            os.write(13);
            os.write(10);
            os.flush();
            this.txtMessages.append("\n" + sender + ": " + txtMessage.getText());
            txtMessage.setText("");
        } catch (Exception e) {
        }
    }                                       

    private void btnEmojiActionPerformed(java.awt.event.ActionEvent evt) {                                         
    }                                        

    private void btnFileActionPerformed(java.awt.event.ActionEvent evt) {                                        
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.showDialog(this, "Select File");
        file = fileChooser.getSelectedFile();
        if (file != null) {
            if (!file.getName().isEmpty()) {
                btnSendFile.setEnabled(true);
                String str;

                if (txtFile.getText().length() > 30) {
                    String t = file.getPath();
                    str = t.substring(0, 20) + " [...] " + t.substring(t.length() - 20, t.length());
                } else {
                    str = file.getPath();
                }
                txtFile.setText(str);
            }
        }
    }                                       

    private void btnSendFileActionPerformed(java.awt.event.ActionEvent evt) {                                            

        fileToSend = file.getAbsolutePath();
        File file = new File(fileToSend);
        byte[] b = new byte[16 * 1024];

        try {
            in = new FileInputStream(file);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }

        try {
            out = socket.getOutputStream();
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        try {
            int count;
            while ((count = in.read(b)) > 0) {
                out.write(b, 0, count);
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    }                                           


    // Variables declaration - do not modify                     
    private javax.swing.JButton btnEmoji;
    private javax.swing.JButton btnFile;
    private javax.swing.JButton btnSend;
    private javax.swing.JButton btnSendFile;
    private javax.swing.JList<String> jList1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JPanel panelMessage;
    private javax.swing.JTextField txtFile;
    private javax.swing.JTextArea txtMessage;
    private javax.swing.JTextArea txtMessages;
    // End of variables declaration                   
}

ClientChatter.java

import javax.swing.*;
import java.io.*;
import java.net.*;

public class ClientChatter extends javax.swing.JFrame {

Socket mngSocket = null;
BufferedReader bf = null;
DataOutputStream os = null;
OutputThread t = null;
String mngIP = "";
String staffName = "";
int mngPort = 0;
static InputStream in;
static OutputStream out;

public ClientChatter() {
    initComponents();
    this.setSize(600, 300);
}

@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    jPanel1 = new javax.swing.JPanel();
    jLabel1 = new javax.swing.JLabel();
    txtStaff = new javax.swing.JTextField();
    jLabel2 = new javax.swing.JLabel();
    txtSeverIP = new javax.swing.JTextField();
    jLabel3 = new javax.swing.JLabel();
    txtSeverPort = new javax.swing.JTextField();
    btnCode = new javax.swing.JButton();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    jPanel1.setLayout(new java.awt.GridLayout());

    jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
    jLabel1.setText("Staff");
    jLabel1.setToolTipText("");
    jPanel1.add(jLabel1);

    txtStaff.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            txtStaffActionPerformed(evt);
        }
    });
    jPanel1.add(txtStaff);

    jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
    jLabel2.setText("Mng IP");
    jPanel1.add(jLabel2);
    jPanel1.add(txtSeverIP);

    jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
    jLabel3.setText("Port:");
    jPanel1.add(jLabel3);
    jPanel1.add(txtSeverPort);

    btnCode.setText("Connect");
    btnCode.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnCodeActionPerformed(evt);
        }
    });
    jPanel1.add(btnCode);

    getContentPane().add(jPanel1, java.awt.BorderLayout.PAGE_START);

    pack();
}// </editor-fold>                        

private void txtStaffActionPerformed(java.awt.event.ActionEvent evt) {                                         
}                                        

private void btnCodeActionPerformed(java.awt.event.ActionEvent evt) {                                        
    mngIP = this.txtSeverIP.getText();
    mngPort = Integer.parseInt(this.txtSeverPort.getText());
    staffName = this.txtStaff.getText();
    try {
        mngSocket = new Socket(mngIP, mngPort);
        if (mngSocket != null) {
            ChatPanel p = new ChatPanel(mngSocket, staffName, "Manager");
            this.getContentPane().add(p);
            p.getTxtMessages().append("Manager is running\n");
            p.updateUI();

            bf = new BufferedReader(new InputStreamReader(mngSocket.getInputStream()));
            os = new DataOutputStream(mngSocket.getOutputStream());
            os.writeBytes("Staff: " + staffName);
            os.write(13);
            os.write(10);
            os.flush();
        }
    } catch (Exception e) {
        e.printStackTrace();
        JOptionPane.showMessageDialog(this, "Manager is not running");
        System.exit(0);
    }
}                                       

public static void main(String args[]) {
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(ClientChatter.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(ClientChatter.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(ClientChatter.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(ClientChatter.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }

    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new ClientChatter().setVisible(true);
        }
    });
}

void download(String str) {
    File file = new File(str);

    byte[] b = new byte[16 * 1024];

    try {
        in = new FileInputStream(file);
    } catch (FileNotFoundException e1) {
        e1.printStackTrace();
    }

    try {
        out = mngSocket.getOutputStream();
    } catch (IOException e1) {
        e1.printStackTrace();
    }

    try {
        int count;
        while ((count = in.read(b)) > 0) {
            out.write(b, 0, count);
        }
    } catch (IOException e1) {
        e1.printStackTrace();
    }
}

// Variables declaration - do not modify                     
private javax.swing.JButton btnCode;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JPanel jPanel1;
private javax.swing.JTextField txtSeverIP;
private javax.swing.JTextField txtSeverPort;
private javax.swing.JTextField txtStaff;
// End of variables declaration                   
}

ManagerChatter

import java.net.*;
import java.io.*;
import javax.print.attribute.standard.Severity;

public class ManagerChatter extends javax.swing.JFrame implements Runnable {

    static ServerSocket srvSocket = null;
    BufferedReader br = null;
    Thread t;
    public int serverPort;
    static InputStream in;
    static OutputStream out;

    public ManagerChatter() {
        initComponents();
        this.setSize(600, 300);
        serverPort = Integer.parseInt(txtSeverPort.getText());
        try {
            srvSocket = new ServerSocket(serverPort);
            this.IbMessage.setText("Mng. Server is running at the port ");
        } catch (Exception e) {
        }
        t = new Thread(this);
        t.start();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        IbMessage = new javax.swing.JLabel();
        txtSeverPort = new javax.swing.JTextField();
        jTabbedPane1 = new javax.swing.JTabbedPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jPanel1.setLayout(new java.awt.GridLayout(1, 0));

        IbMessage.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        IbMessage.setText("Manager Port:");
        jPanel1.add(IbMessage);

        txtSeverPort.setText("12345");
        txtSeverPort.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtSeverPortActionPerformed(evt);
            }
        });
        jPanel1.add(txtSeverPort);

        getContentPane().add(jPanel1, java.awt.BorderLayout.PAGE_START);
        getContentPane().add(jTabbedPane1, java.awt.BorderLayout.CENTER);

        pack();
    }// </editor-fold>                        

    private void txtSeverPortActionPerformed(java.awt.event.ActionEvent evt) {                                             
    }                                            

    public static void main(String args[]) throws IOException {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ManagerChatter().setVisible(true);
            }
        });
        Socket socket = srvSocket.accept();
        in = socket.getInputStream();
        out = new FileOutputStream("C:\\Users\\tring\\Downloads");

        byte[] b = new byte[16 * 1024];

        int count;
        while ((count = in.read(b)) > 0) {
            out.write(b, 0, count);
            System.out.println("New file");
        }

    }

    // Variables declaration - do not modify                     
    private javax.swing.JLabel IbMessage;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JTextField txtSeverPort;
    // End of variables declaration                   

    @Override
    public void run() {
        while (true) {
            try {
                Socket aStaffSocket = srvSocket.accept();
                if (aStaffSocket != null) {
                    br = new BufferedReader(new InputStreamReader(aStaffSocket.getInputStream()));
                    String S = br.readLine();
                    int pos = S.indexOf(":");
                    String staffName = S.substring(pos + 1);

                    ChatPanel p = new ChatPanel(aStaffSocket, "Manager", staffName);
                    jTabbedPane1.add(staffName, p);
                    p.updateUI();
                }
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }
    }
}

OutputThread

import javax.swing.*;
import java.io.*;
import java.net.*;

public class OutputThread extends Thread {

    public ObjectOutputStream Out;
    Socket socket;
    JTextArea txt;
    BufferedReader bf;
    String sender;
    String receiver;

    public OutputThread(Socket s, JTextArea txt, String sender, String receiver) {
        super();
        this.socket = s;
        this.txt = txt;
        this.sender = sender;
        this.receiver = receiver;
        try {
            bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, "Network Error!");
            System.exit(0);
        }
    }

    public void run() {
        while (true) {
            try {
                if (socket != null) {
                    String msg = "";
                    if ((msg = bf.readLine()) != null && msg.length() > 0) {
                        txt.append("\n" + receiver + ": " + msg);
                    }
                }
                sleep(1000);
            } catch (Exception e) {
            }
        }
    }
}

Bạn phải có phần tín hiệu (signal hoặc header gì đấy) để phân biệt nội dung.
Ví dụ đơn giản:

  • Khi gửi nhận văn bản (đoạn chat) thì gửi 1 byte có giá trị là 1 trước, theo sau là 4 byte (int) độ dài của văn bản, rồi đến nội dung.
  • Khi gửi tập tin thì gửi 1 byte có giá trị là 2, theo sau là 4 byte độ dài tên tập tin > tên > 4 byte (hoặc 8 byte (long)) kích thước của tập tin, sau nữa là nội dung tập tin.
    Ví dụ dữ liệu:
01 | 00 00 00 0B | 48 65 6C 6C 6F 20 57 6F 72 6C 64 | 02 | 00 00 00 09 | 49 6D 61 67 65 2E 70 6E 67 | 00 00 00 06 | 50 4E 47 49 4D 47 | 
 1 |          11 |             Hello World          |  2 |           9 |          Image.png         |           6 |       PNGIMG      |
^ Văn bản                                            ^ Tập tin

Khi đọc:

  • Đọc 1 byte = 1 => dữ liệu văn bản (chat).
    • Đọc 4 byte tiếp theo = 11 => văn bản có độ dài = 11.
      • Đọc 11 byte tiếp theo => “Hello World”, hiển thị.
  • Đọc 1 byte = 2 => dữ liệu là tập tin.
    • Đọc 4 byte tiếp = 9 => tên tập tin dài 9 byte.
      • Đọc 9 byte để lấy tên tập tin => “Image.png”.
        • Đọc 4 byte => 6 là kích thước tập tin.
          • Đọc 6 byte để lấy dữ liệu của tập tin. Kết hợp với tên lấy được để ghi vào ổ đĩa.

Trên là ví dụ tham khảo, bạn có thể làm theo, hoặc tự quy định.

11 Likes

amazing, Bác Pro ghê

Mình chưa học đến cái tín hiệu này nên nói không hiểu gì cũng ngại thật, do bạn trả lời nhiệt tình quá. Cảm ơn bạn, mình sẽ nguyên cứu thêm.

1 Like

Mình hiểu ý bạn rồi, mình làm được rồi.

2 Likes

A post was merged into an existing topic: Topic lưu trữ các post off-topic - version 3

Bạn ơi, bạn có thể cho mình xin full code bao gồm cả send icon được không. Mình đang làm dự án này thi cuối kì mà còn 1 số lỗi cần bạn giúp.
Email: [email protected]

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?