Java Socket编程 基于TCP方式的二进制文件传输【转】http://blog.csdn.net/jia20003/article/details/8248221

此人博客挺好的,推荐一个!

一个基于Java Socket协议之上文件传输的完整示例,基于TCP通信完成。

除了基于TCP的二进制文件传输,还演示了JAVA Swing的一些编程技巧,Demo程序

实现主要功能有以下几点:

1.      基于Java Socket的二进制文件传输(包括图片,二进制文件,各种文档work,

         PDF)

2.      SwingWorker集合JProgressBar显示实时传输/接受完成的百分比

3.      其它一些Swing多线程编程技巧

首先来看一下整个Dome的Class之间的关系图:


下面按照上图来详细解释各个类的功能与代码实现:

服务器端:

FileTransferServer类的功能首先是在端口9999创建一个服务器套接字并

开始监听连接。相关代码如下:

  1. private void startServer(int port) {  
  2.     try {  
  3.         serverSocket = new ServerSocket(port);  
  4.         System.out.println("Server started at port :" + port);  
  5.         while(true) {  
  6.             Socket client = serverSocket.accept(); // blocked & waiting for income socket  
  7.             System.out.println("Just connected to " + client.getRemoteSocketAddress());  
  8.             FileReceiveTask task = new FileReceiveTask(client);  
  9.             bar.setValue(0); // reset it now  
  10.             task.addPropertyChangeListener(new PropertyChangeListener() {  
  11.                 public void propertyChange(PropertyChangeEvent evt) {  
  12.                     if ("progress".equals(evt.getPropertyName())) {  
  13.                         bar.setValue((Integer) evt.getNewValue());  
  14.                     }  
  15.                 }  
  16.             });  
  17.               
  18.             task.execute();  
  19.         }  
  20.   
  21.     } catch (IOException e) {  
  22.         e.printStackTrace();  
  23.     }  
  24. }  

关于PropertyChangeListener, Java提供了一个非常有力的工具类来

监控任意Bean Model的数据改变,程序通过添加该监听器实现对

SwingWorker的progress属性值改变的事件捕获,然后更新JProgressBar

实例对象,实现了UI的刷新。FileTransferServer类的完整源代码如下:

  1. package com.gloomyfish.socket.tutorial.filetransfer;  
  2.   
  3. import java.awt.BorderLayout;  
  4. import java.awt.FlowLayout;  
  5. import java.awt.event.ActionEvent;  
  6. import java.awt.event.ActionListener;  
  7. import java.beans.PropertyChangeEvent;  
  8. import java.beans.PropertyChangeListener;  
  9. import java.io.IOException;  
  10. import java.net.ServerSocket;  
  11. import java.net.Socket;  
  12.   
  13. import javax.swing.BoxLayout;  
  14. import javax.swing.JButton;  
  15. import javax.swing.JFrame;  
  16. import javax.swing.JOptionPane;  
  17. import javax.swing.JPanel;  
  18. import javax.swing.JProgressBar;  
  19.   
  20. public class FileTransferServer extends JFrame implements ActionListener {  
  21.     /** 
  22.      *  
  23.      */  
  24.     public final static String START_SVR = "Start";  
  25.     public final static String SHUT_DOWN_SVR = "Shut Down";  
  26.     public final static String END_FLAG = "EOF";  
  27.     private static final long serialVersionUID = 1L;  
  28.     private ServerSocket serverSocket;  
  29.     private JButton startBtn;  
  30.     private JProgressBar bar;  
  31.     public FileTransferServer() {  
  32.         super("File Server");  
  33.         initComponent();  
  34.         setupListener();  
  35.     }  
  36.   
  37.     private void setupListener() {  
  38.         startBtn.addActionListener(this);  
  39.     }  
  40.   
  41.     private void initComponent() {  
  42.         startBtn = new JButton(START_SVR);  
  43.         JPanel progressPanel = new JPanel();  
  44.         progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.Y_AXIS));  
  45.         bar = new JProgressBar();  
  46.         bar.setMinimum(0);  
  47.         bar.setMaximum(100);  
  48.         progressPanel.add(bar);  
  49.         getContentPane().setLayout(new BorderLayout());  
  50.         JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));  
  51.         btnPanel.add(startBtn);  
  52.         getContentPane().add(btnPanel, BorderLayout.SOUTH);  
  53.         getContentPane().add(progressPanel, BorderLayout.CENTER);  
  54.     }  
  55.       
  56.     private void startServer(int port) {  
  57.         try {  
  58.             serverSocket = new ServerSocket(port);  
  59.             System.out.println("Server started at port :" + port);  
  60.             while(true) {  
  61.                 Socket client = serverSocket.accept(); // blocked & waiting for income socket  
  62.                 System.out.println("Just connected to " + client.getRemoteSocketAddress());  
  63.                 FileReceiveTask task = new FileReceiveTask(client);  
  64.                 bar.setValue(0); // reset it now  
  65.                 task.addPropertyChangeListener(new PropertyChangeListener() {  
  66.                     public void propertyChange(PropertyChangeEvent evt) {  
  67.                         if ("progress".equals(evt.getPropertyName())) {  
  68.                             bar.setValue((Integer) evt.getNewValue());  
  69.                         }  
  70.                     }  
  71.                 });  
  72.                   
  73.                 task.execute();  
  74.             }  
  75.   
  76.         } catch (IOException e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80.       
  81.     public void showSuccess() {  
  82.         bar.setValue(100);  
  83.         JOptionPane.showMessageDialog(this"file received successfully!");  
  84.     }  
  85.   
  86.     @Override  
  87.     public void actionPerformed(ActionEvent e) {  
  88.         if(START_SVR.equals(e.getActionCommand())) {  
  89.             Thread startThread = new Thread(new Runnable() {  
  90.                 public void run() {  
  91.                     startServer(9999);  
  92.                 }  
  93.             });  
  94.             startThread.start();  
  95.             startBtn.setEnabled(false);  
  96.         } else if(SHUT_DOWN_SVR.equals(e.getActionCommand())) {  
  97.   
  98.         } else {  
  99.             // do nothing...  
  100.         }  
  101.     }  
  102.       
  103.     public static void main(String[] args) {  
  104.         FileTransferServer server = new FileTransferServer();  
  105.         server.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  106.         server.setSize(400400);  
  107.         server.setResizable(false);  
  108.         server.setVisible(true);  
  109.     }  
  110. }  

FileReceiveTask是服务器端的文件接受类:

首先从建立的TCP流中得到文件名与文件大小,然后开始接受文件内容字节

并写入创建的文件对象流中,最后验证文件大小与写入的字节流是否相等

最后发送一条消息到文件发送方,告诉对方文件传输完成,可以关闭TCP流。

该类的完整源代码如下:

  1. package com.gloomyfish.socket.tutorial.filetransfer;  
  2.   
  3. import java.io.BufferedOutputStream;  
  4. import java.io.BufferedWriter;  
  5. import java.io.DataInputStream;  
  6. import java.io.File;  
  7. import java.io.FileOutputStream;  
  8. import java.io.OutputStreamWriter;  
  9. import java.net.Socket;  
  10.   
  11. import javax.swing.SwingWorker;  
  12.   
  13. public class FileReceiveTask extends SwingWorker<Integer, Object> {  
  14.     private Socket _mSocket;  
  15.     public FileReceiveTask(Socket client) {  
  16.         this._mSocket = client;  
  17.     }  
  18.   
  19.     @Override  
  20.     protected Integer doInBackground() throws Exception {  
  21.         // get file meta information  
  22.         DataInputStream input = new DataInputStream(_mSocket.getInputStream());  
  23.         String fileName = input.readUTF();  
  24.         int fileLength = (int)input.readLong(); // number of total bytes  
  25.         File file = new File("C:\\Users\\fish\\Downloads" + File.separator + fileName);  
  26.         BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));  
  27.         System.out.println("Received File Name = " + fileName);  
  28.         System.out.println("Received File size = " + fileLength/1024 + "KB");  
  29.           
  30.         // start to receive the content of the file and write them  
  31.         byte[] content = new byte[2048];  
  32.         int offset = 0;  
  33.         int numReadBytes = 0;  
  34.         while(offset < fileLength && (numReadBytes = input.read(content)) > 0) {  
  35.             output.write(content, 0, numReadBytes);  
  36.             float precent = 100.0f * ((float)offset)/((float)fileLength);  
  37.             setProgress((int)precent);  
  38.             offset += numReadBytes;  
  39.         }  
  40.         System.out.println("numReadBytes = " + numReadBytes);  
  41.         if(offset < fileLength) {  
  42.             numReadBytes = input.read(content);  
  43.             System.out.println("numReadBytes = " + numReadBytes);  
  44.             System.out.println("File content error at server side");  
  45.         } else {  
  46.             System.out.println("File Receive Task has done correctly");  
  47.         }  
  48.         setProgress(100);  
  49.           
  50.         // tell client to close the socket now, we already receive the file successfully!!  
  51.         BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(_mSocket.getOutputStream()));  
  52.         bufferedWriter.write("DONE\r\n");  
  53.         bufferedWriter.flush();  
  54.           
  55.         // close the file and socket  
  56.         output.close();  
  57.         _mSocket.close();  
  58.         return 100;  
  59.     }  
  60.   
  61. }  

客户端:

FileTransferClient是客户端UI类,用来实现到服务端的连接,然后选择

要传输的文件(图片,PDF,Word文档等各种二进制文件)。如果没有

输入服务器信息,会弹出提示要求输入。端口已经指定为:9999

【send File】按钮会打开文件选择框,用户选择要传输文件以后,创建

FileTransferTask线程,并开始执行文件传送。客户端UI代码如下:

  1. package com.gloomyfish.socket.tutorial.filetransfer;  
  2.   
  3. import java.awt.BorderLayout;  
  4. import java.awt.FlowLayout;  
  5. import java.awt.GridLayout;  
  6. import java.awt.event.ActionEvent;  
  7. import java.awt.event.ActionListener;  
  8. import java.beans.PropertyChangeEvent;  
  9. import java.beans.PropertyChangeListener;  
  10. import java.io.File;  
  11. import java.net.InetSocketAddress;  
  12. import java.net.SocketAddress;  
  13.   
  14. import javax.swing.BorderFactory;  
  15. import javax.swing.BoxLayout;  
  16. import javax.swing.JButton;  
  17. import javax.swing.JFileChooser;  
  18. import javax.swing.JFrame;  
  19. import javax.swing.JLabel;  
  20. import javax.swing.JOptionPane;  
  21. import javax.swing.JPanel;  
  22. import javax.swing.JProgressBar;  
  23. import javax.swing.JTextField;  
  24. /** 
  25.  * 我一般写英文注释,偶尔我也会写中文注释,只是觉得写英文 
  26.  * 注释跟代码比较统一,无他。 
  27.  *  
  28.  * @Date 2012-11-30 
  29.  * @author fish 
  30.  * 
  31.  */  
  32. public class FileTransferClient extends JFrame implements ActionListener {  
  33.     /** 
  34.      *  
  35.      */  
  36.     private static final long serialVersionUID = 1L;  
  37.     public final static String SEND_CMD = "Send File";  
  38.     public final static int MINIMUM = 0;  
  39.     public final static int MAXIMUM = 100;  
  40.     // public final static String CONNECT_CMD = "Connect";  
  41.     private JButton sendFileBtn;  
  42.     private JTextField serverField;  
  43.     private JTextField portField;  
  44.     private JProgressBar bar;  
  45.       
  46.     public FileTransferClient() {  
  47.         super("File Transfer Client");  
  48.         initComponents();  
  49.     }  
  50.   
  51.     private void initComponents() {  
  52.         getContentPane().setLayout(new BorderLayout());  
  53.         JPanel progressPanel = new JPanel();  
  54.         progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.Y_AXIS));  
  55.         bar = new JProgressBar();  
  56.         progressPanel.add(bar);  
  57.         bar.setMinimum(MINIMUM);  
  58.         bar.setMaximum(MAXIMUM);  
  59.         JPanel serverSettingPanel = new JPanel();  
  60.         serverSettingPanel.setLayout(new GridLayout(2,2,5,5));  
  61.         serverSettingPanel.setBorder(BorderFactory.createTitledBorder("Server Setting"));  
  62.         serverField = new JTextField();  
  63.         portField = new JTextField();  
  64.         serverSettingPanel.add(new JLabel("Server IP/Host:"));  
  65.         serverSettingPanel.add(serverField);  
  66.         serverSettingPanel.add(new JLabel("Server Port:"));  
  67.         serverSettingPanel.add(portField);  
  68.           
  69.         sendFileBtn = new JButton(SEND_CMD);  
  70.         JPanel btnPanel = new JPanel();  
  71.         btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));  
  72.         btnPanel.add(sendFileBtn);  
  73.         getContentPane().add(serverSettingPanel, BorderLayout.NORTH);  
  74.         getContentPane().add(btnPanel, BorderLayout.SOUTH);  
  75.         getContentPane().add(progressPanel, BorderLayout.CENTER);  
  76.         sendFileBtn.addActionListener(this);  
  77.     }  
  78.   
  79.     @Override  
  80.     public void actionPerformed(ActionEvent e) {  
  81.         String command = e.getActionCommand();  
  82.         if(command.equals(SEND_CMD)) {  
  83.             if(checkNull()) {  
  84.                 JOptionPane.showMessageDialog(this"Please enter server host and port in order to set up the connection!");  
  85.                 return;  
  86.             }  
  87.             JFileChooser chooser = new JFileChooser();  
  88.             int status = chooser.showOpenDialog(null);  
  89.             if (status == JFileChooser.APPROVE_OPTION) {  
  90.                 File f = chooser.getSelectedFile();  
  91.                 SocketAddress address = new InetSocketAddress(getServer(), getPort());  
  92.                 FileTransferTask task = new FileTransferTask(f, address, this);  
  93.                 bar.setValue(0);  
  94.                 task.addPropertyChangeListener(new PropertyChangeListener() {  
  95.                     public void propertyChange(PropertyChangeEvent evt) {  
  96.                         if ("progress".equals(evt.getPropertyName())) {  
  97.                             bar.setValue((Integer) evt.getNewValue());  
  98.                         }  
  99.                     }  
  100.                 });  
  101.                 task.execute(); // 异步task执行  
  102.             }  
  103.         } else {  
  104.             // do nothing  
  105.         }  
  106.     }  
  107.       
  108.     public void showSuccess() {  
  109.         bar.setValue(100);  
  110.         JOptionPane.showMessageDialog(this"file send successfully!");  
  111.     }  
  112.       
  113.     public String getServer() {  
  114.         return serverField.getText().trim();  
  115.     }  
  116.       
  117.     public int getPort() {  
  118.         return Integer.parseInt(portField.getText().trim());  
  119.     }  
  120.     /** 
  121.      * make sure the UI already have some correct input information here!!! 
  122.      * @return 
  123.      */  
  124.     private boolean checkNull() {  
  125.         String serverName = serverField.getText();  
  126.         String port = portField.getText();  
  127.         if(serverName == null || serverName.length() == 0 || port == null || port.length() == 0) {  
  128.             return true;  
  129.         }  
  130.           
  131.         try {  
  132.             Integer.parseInt(port); // try to parse it as server port number , validation code.  
  133.         } catch(NumberFormatException ne) {  
  134.             ne.printStackTrace();  
  135.             return true;  
  136.         }  
  137.         return false;  
  138.     }  
  139.       
  140.     public static void main(String[] args) {  
  141.         FileTransferClient client = new FileTransferClient();  
  142.         client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  143.         client.setSize(400400);  
  144.         client.setResizable(false);  
  145.         // client.pack();  
  146.         client.setVisible(true);  
  147.     }  
  148.   
  149. }  

FileTransferTask实现的功能主要有:

1. 发送文件meta信息到接受方(文件名与文件大小)

2. 读取文件内容字节写入Socket字节流中,发送到接受方

3. 从Socket字节流中读取对方接受完成通知信息,调用弹出文件传输成功信息

该类完全源代码如下:

  1. package com.gloomyfish.socket.tutorial.filetransfer;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.BufferedReader;  
  5. import java.io.DataInputStream;  
  6. import java.io.DataOutputStream;  
  7. import java.io.File;  
  8. import java.io.FileInputStream;  
  9. import java.io.IOException;  
  10. import java.io.InputStreamReader;  
  11. import java.net.Socket;  
  12. import java.net.SocketAddress;  
  13.   
  14. import javax.swing.SwingWorker;  
  15.   
  16. public class FileTransferTask extends SwingWorker<Integer, Object> {  
  17.     private File selectedFile;  
  18.     private Socket mSocket;  
  19.     private SocketAddress address;  
  20.     private FileTransferClient parent;  
  21.       
  22.     public FileTransferTask(File file, SocketAddress address, FileTransferClient owner /*, JProgressBar progress*/) {  
  23.         this.address = address;  
  24.         this.selectedFile = file;  
  25.         mSocket = new Socket();  
  26.         this.parent = owner;  
  27.     }  
  28.       
  29.     @Override  
  30.     protected Integer doInBackground() throws Exception {  
  31.         // Get the size of the file  
  32.         long length = selectedFile.length();  
  33.         if (length > Integer.MAX_VALUE) {  
  34.             throw new IOException("Could not completely read file " + selectedFile.getName() + " as it is too long (" + length + " bytes, max supported " + Integer.MAX_VALUE + ")");  
  35.         }  
  36.           
  37.         mSocket.connect(address);  
  38.           
  39.         // Create the byte array to hold the file data  
  40.         mSocket.setSoLinger(true60);  
  41.         DataOutputStream dout = new DataOutputStream(mSocket.getOutputStream());  
  42.         // now we start to send the file meta info.  
  43.         dout.writeUTF(selectedFile.getName());  
  44.         dout.writeLong(length);  
  45.         dout.flush();  
  46.         // end comment  
  47.         FileDataPackage pData = new FileDataPackage();  
  48.         DataInputStream is = new DataInputStream(new FileInputStream(selectedFile));  
  49.         byte[] bytes = new byte[2048];  
  50.   
  51.         // Read in the bytes  
  52.         int offset = 0;  
  53.         int numRead = 0;  
  54.         int fsize = (int)length;  
  55.         while (offset < fsize && (numRead=is.read(bytes, 0, bytes.length)) >= 0) {  
  56.             pData.setData(bytes, numRead);  
  57.             dout.write(pData.getPackageData(), 0, pData.getPackageData().length);  
  58.             dout.flush();  
  59.             offset += numRead;  
  60.             float precent = 100.0f * ((float)offset)/((float)fsize);  
  61.             setProgress((int)precent);  
  62.         }  
  63.         System.out.println("total send bytes = " + offset);  
  64.         // Ensure all the bytes have been read in  
  65.         if (offset < fsize) {  
  66.             throw new IOException("Could not completely transfer file " + selectedFile.getName());  
  67.         }  
  68.         mSocket.shutdownOutput();  
  69.           
  70.         // receive the file transfer successfully message from connection  
  71.           
  72.         BufferedInputStream streamReader = new BufferedInputStream(mSocket.getInputStream());  
  73.         BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(streamReader));  
  74.         String doneMsg = bufferedReader.readLine();  
  75.         if("DONE".equals(doneMsg)) {  
  76.             parent.showSuccess();  
  77.         }  
  78.         // Close the file input stream   
  79.         setProgress(100);  
  80.         // dout.close();  
  81.         mSocket.close();  
  82.         is.close();  
  83.         System.out.println("close it now......");  
  84.         return 100;  
  85.     }  
  86. }  

数据包类如下,不解释!

  1. package com.gloomyfish.socket.tutorial.filetransfer;  
  2. /** 
  3.  * this is very simple file transfer protocol over TCP socket 
  4.  * @date 2012-12-01 
  5.  * @author zhigang jia 
  6.  * 
  7.  */  
  8. public class FileDataPackage {  
  9.   
  10.     private int dataLength; // 数据包中数据长度,两个字节  
  11.     private byte[] databuff; // 数据包中数据,meici最大不超过2048字节  
  12.       
  13.     public final static byte[] EOF = new byte[]{'E''O','F'};  
  14.       
  15.     public FileDataPackage() {  
  16.         dataLength = 0;  
  17.         databuff = new byte[2048];  
  18.     }  
  19.       
  20.     public byte[] getPackageData() {  
  21.         byte[] pData = new byte[dataLength];  
  22.         // end comment  
  23.         System.arraycopy(databuff, 0, pData, 0, dataLength);  
  24.         return pData;  
  25.     }  
  26.       
  27.     public void setData(byte[] data, int bsize) {  
  28.         dataLength = bsize;  
  29.         for(int i=0; i<databuff.length; i++) {  
  30.             if(i<bsize) {  
  31.                 databuff[i] = data[i];  
  32.             } else {  
  33.                 databuff[i] = ' ';  
  34.             }  
  35.         }  
  36.     }  
  37. }  

每次发送的最大字节数为2048个字节。程序最终运行效果如下(win7 + JDK6u30):



觉得不错,请顶一下啊,谢谢!

原文地址:https://www.cnblogs.com/songtzu/p/2922288.html