Java Socket編程 - 基於TCP方式的二進制文件傳輸

一個基於Java Socket協議之上文件傳輸的完整示例,基於TCP通訊完成。java

除了基於TCP的二進制文件傳輸,還演示了JAVA Swing的一些編程技巧,Demo程序編程

實現主要功能有如下幾點:服務器

1.      基於Java Socket的二進制文件傳輸(包括圖片,二進制文件,各類文檔work,多線程

         PDF)異步

2.      SwingWorker集合JProgressBar顯示實時傳輸/接受完成的百分比socket

3.      其它一些Swing多線程編程技巧ide

首先來看一下整個Dome的Class之間的關係圖:工具


下面按照上圖來詳細解釋各個類的功能與代碼實現:this

服務器端:spa

FileTransferServer類的功能首先是在端口9999建立一個服務器套接字並

開始監聽鏈接。相關代碼以下:

private void startServer(int port) { 	try { 		serverSocket = new ServerSocket(port); 		System.out.println("Server started at port :" + port); 		while(true) { 			Socket client = serverSocket.accept(); // blocked & waiting for income socket 			System.out.println("Just connected to " + client.getRemoteSocketAddress()); 			FileReceiveTask task = new FileReceiveTask(client); 			bar.setValue(0); // reset it now 			task.addPropertyChangeListener(new PropertyChangeListener() { 	            public void propertyChange(PropertyChangeEvent evt) { 	                if ("progress".equals(evt.getPropertyName())) { 	                	bar.setValue((Integer) evt.getNewValue()); 	                } 	            } 	        }); 			 			task.execute(); 		}  	} catch (IOException e) { 		e.printStackTrace(); 	} }
關於PropertyChangeListener, Java提供了一個很是有力的工具類來

監控任意Bean Model的數據改變,程序經過添加該監聽器實現對

SwingWorker的progress屬性值改變的事件捕獲,而後更新JProgressBar

實例對象,實現了UI的刷新。FileTransferServer類的完整源代碼以下:

package com.gloomyfish.socket.tutorial.filetransfer;  import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket;  import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar;  public class FileTransferServer extends JFrame implements ActionListener { 	/** 	 *  	 */ 	public final static String START_SVR = "Start"; 	public final static String SHUT_DOWN_SVR = "Shut Down"; 	public final static String END_FLAG = "EOF"; 	private static final long serialVersionUID = 1L; 	private ServerSocket serverSocket; 	private JButton startBtn; 	private JProgressBar bar; 	public FileTransferServer() { 		super("File Server"); 		initComponent(); 		setupListener(); 	}  	private void setupListener() { 		startBtn.addActionListener(this); 	}  	private void initComponent() { 		startBtn = new JButton(START_SVR); 		JPanel progressPanel = new JPanel(); 		progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.Y_AXIS)); 		bar = new JProgressBar(); 		bar.setMinimum(0); 		bar.setMaximum(100); 		progressPanel.add(bar); 		getContentPane().setLayout(new BorderLayout()); 		JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); 		btnPanel.add(startBtn); 		getContentPane().add(btnPanel, BorderLayout.SOUTH); 		getContentPane().add(progressPanel, BorderLayout.CENTER); 	} 	 	private void startServer(int port) { 		try { 			serverSocket = new ServerSocket(port); 			System.out.println("Server started at port :" + port); 			while(true) { 				Socket client = serverSocket.accept(); // blocked & waiting for income socket 				System.out.println("Just connected to " + client.getRemoteSocketAddress()); 				FileReceiveTask task = new FileReceiveTask(client); 				bar.setValue(0); // reset it now 				task.addPropertyChangeListener(new PropertyChangeListener() { 		            public void propertyChange(PropertyChangeEvent evt) { 		                if ("progress".equals(evt.getPropertyName())) { 		                	bar.setValue((Integer) evt.getNewValue()); 		                } 		            } 		        }); 				 				task.execute(); 			}  		} catch (IOException e) { 			e.printStackTrace(); 		} 	} 	 	public void showSuccess() { 		bar.setValue(100); 		JOptionPane.showMessageDialog(this, "file received successfully!"); 	}  	@Override 	public void actionPerformed(ActionEvent e) { 		if(START_SVR.equals(e.getActionCommand())) { 			Thread startThread = new Thread(new Runnable() {                 public void run() {                 	startServer(9999);                 }             }); 			startThread.start(); 			startBtn.setEnabled(false); 		} else if(SHUT_DOWN_SVR.equals(e.getActionCommand())) {  		} else { 			// do nothing... 		} 	} 	 	public static void main(String[] args) { 		FileTransferServer server = new FileTransferServer(); 		server.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 		server.setSize(400, 400); 		server.setResizable(false); 		server.setVisible(true); 	} } 
FileReceiveTask是服務器端的文件接受類:

首先從創建的TCP流中獲得文件名與文件大小,而後開始接受文件內容字節

並寫入建立的文件對象流中,最後驗證文件大小與寫入的字節流是否相等

最後發送一條消息到文件發送方,告訴對方文件傳輸完成,能夠關閉TCP流。

該類的完整源代碼以下:

package com.gloomyfish.socket.tutorial.filetransfer;  import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.net.Socket;  import javax.swing.SwingWorker;  public class FileReceiveTask extends SwingWorker<Integer, Object> { 	private Socket _mSocket; 	public FileReceiveTask(Socket client) { 		this._mSocket = client; 	}  	@Override 	protected Integer doInBackground() throws Exception { 		// get file meta information 		DataInputStream input = new DataInputStream(_mSocket.getInputStream()); 		String fileName = input.readUTF(); 		int fileLength = (int)input.readLong(); // number of total bytes 		File file = new File("C:\\Users\\fish\\Downloads" + File.separator + fileName); 		BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file)); 		System.out.println("Received File Name = " + fileName); 		System.out.println("Received File size = " + fileLength/1024 + "KB"); 		 		// start to receive the content of the file and write them 		byte[] content = new byte[2048]; 		int offset = 0; 		int numReadBytes = 0; 		while(offset < fileLength && (numReadBytes = input.read(content)) > 0) { 			output.write(content, 0, numReadBytes); 			float precent = 100.0f * ((float)offset)/((float)fileLength); 			setProgress((int)precent); 			offset += numReadBytes; 		} 		System.out.println("numReadBytes = " + numReadBytes); 		if(offset < fileLength) { 			numReadBytes = input.read(content); 			System.out.println("numReadBytes = " + numReadBytes); 			System.out.println("File content error at server side"); 		} else { 			System.out.println("File Receive Task has done correctly"); 		} 		setProgress(100); 		 		// tell client to close the socket now, we already receive the file successfully!! 	    BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(_mSocket.getOutputStream())); 	    bufferedWriter.write("DONE\r\n"); 	    bufferedWriter.flush(); 	     	    // close the file and socket 		output.close(); 		_mSocket.close(); 		return 100; 	}  } 
客戶端:

FileTransferClient是客戶端UI類,用來實現到服務端的鏈接,而後選擇

要傳輸的文件(圖片,PDF,Word文檔等各類二進制文件)。若是沒有

輸入服務器信息,會彈出提示要求輸入。端口已經指定爲:9999

【send File】按鈕會打開文件選擇框,用戶選擇要傳輸文件之後,建立

FileTransferTask線程,並開始執行文件傳送。客戶端UI代碼以下:

package com.gloomyfish.socket.tutorial.filetransfer;  import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.net.InetSocketAddress; import java.net.SocketAddress;  import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JTextField; /**  * 我通常寫英文註釋,偶爾我也會寫中文註釋,只是以爲寫英文  * 註釋跟代碼比較統一,無他。  *   * @Date 2012-11-30  * @author fish  *  */ public class FileTransferClient extends JFrame implements ActionListener { 	/** 	 *  	 */ 	private static final long serialVersionUID = 1L; 	public final static String SEND_CMD = "Send File"; 	public final static int MINIMUM = 0; 	public final static int MAXIMUM = 100; 	// public final static String CONNECT_CMD = "Connect"; 	private JButton sendFileBtn; 	private JTextField serverField; 	private JTextField portField; 	private JProgressBar bar; 	 	public FileTransferClient() { 		super("File Transfer Client"); 		initComponents(); 	}  	private void initComponents() { 		getContentPane().setLayout(new BorderLayout()); 		JPanel progressPanel = new JPanel(); 		progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.Y_AXIS)); 		bar = new JProgressBar(); 		progressPanel.add(bar); 		bar.setMinimum(MINIMUM); 		bar.setMaximum(MAXIMUM); 		JPanel serverSettingPanel = new JPanel(); 		serverSettingPanel.setLayout(new GridLayout(2,2,5,5)); 		serverSettingPanel.setBorder(BorderFactory.createTitledBorder("Server Setting")); 		serverField = new JTextField(); 		portField = new JTextField(); 		serverSettingPanel.add(new JLabel("Server IP/Host:")); 		serverSettingPanel.add(serverField); 		serverSettingPanel.add(new JLabel("Server Port:")); 		serverSettingPanel.add(portField); 		 		sendFileBtn = new JButton(SEND_CMD); 		JPanel btnPanel = new JPanel(); 		btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); 		btnPanel.add(sendFileBtn); 		getContentPane().add(serverSettingPanel, BorderLayout.NORTH); 		getContentPane().add(btnPanel, BorderLayout.SOUTH); 		getContentPane().add(progressPanel, BorderLayout.CENTER); 		sendFileBtn.addActionListener(this); 	}  	@Override 	public void actionPerformed(ActionEvent e) { 		String command = e.getActionCommand(); 		if(command.equals(SEND_CMD)) { 			if(checkNull()) { 				JOptionPane.showMessageDialog(this, "Please enter server host and port in order to set up the connection!"); 				return; 			} 			JFileChooser chooser = new JFileChooser(); 			int status = chooser.showOpenDialog(null); 			if (status == JFileChooser.APPROVE_OPTION) { 				File f = chooser.getSelectedFile(); 				SocketAddress address = new InetSocketAddress(getServer(), getPort()); 				FileTransferTask task = new FileTransferTask(f, address, this); 				bar.setValue(0); 				task.addPropertyChangeListener(new PropertyChangeListener() { 		            public void propertyChange(PropertyChangeEvent evt) { 		                if ("progress".equals(evt.getPropertyName())) { 		                	bar.setValue((Integer) evt.getNewValue()); 		                } 		            } 		        }); 				task.execute(); // 異步task執行 			} 		} else { 			// do nothing 		} 	} 	 	public void showSuccess() { 		bar.setValue(100); 		JOptionPane.showMessageDialog(this, "file send successfully!"); 	} 	 	public String getServer() { 		return serverField.getText().trim(); 	} 	 	public int getPort() { 		return Integer.parseInt(portField.getText().trim()); 	} 	/** 	 * make sure the UI already have some correct input information here!!! 	 * @return 	 */ 	private boolean checkNull() { 		String serverName = serverField.getText(); 		String port = portField.getText(); 		if(serverName == null || serverName.length() == 0 || port == null || port.length() == 0) { 			return true; 		} 		 		try { 			Integer.parseInt(port); // try to parse it as server port number , validation code. 		} catch(NumberFormatException ne) { 			ne.printStackTrace(); 			return true; 		} 		return false; 	} 	 	public static void main(String[] args) { 		FileTransferClient client = new FileTransferClient(); 		client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 		client.setSize(400, 400); 		client.setResizable(false); 		// client.pack(); 		client.setVisible(true); 	}  } 
FileTransferTask實現的功能主要有:

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

2. 讀取文件內容字節寫入Socket字節流中,發送到接受方

3. 從Socket字節流中讀取對方接受完成通知信息,調用彈出文件傳輸成功信息

該類徹底源代碼以下:

package com.gloomyfish.socket.tutorial.filetransfer;  import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.SocketAddress;  import javax.swing.SwingWorker;  public class FileTransferTask extends SwingWorker<Integer, Object> { 	private File selectedFile; 	private Socket mSocket; 	private SocketAddress address; 	private FileTransferClient parent; 	 	public FileTransferTask(File file, SocketAddress address, FileTransferClient owner /*, JProgressBar progress*/) { 		this.address = address; 		this.selectedFile = file; 		mSocket = new Socket(); 		this.parent = owner; 	} 	 	@Override 	protected Integer doInBackground() throws Exception { 		// Get the size of the file 	    long length = selectedFile.length(); 	    if (length > Integer.MAX_VALUE) { 	        throw new IOException("Could not completely read file " + selectedFile.getName() + " as it is too long (" + length + " bytes, max supported " + Integer.MAX_VALUE + ")"); 	    } 	     	    mSocket.connect(address); 		 	    // Create the byte array to hold the file data 	    mSocket.setSoLinger(true, 60); 	    DataOutputStream dout = new DataOutputStream(mSocket.getOutputStream()); 	    // now we start to send the file meta info. 	    dout.writeUTF(selectedFile.getName()); 	    dout.writeLong(length); 	    dout.flush(); 	    // end comment 	    FileDataPackage pData = new FileDataPackage(); 	    DataInputStream is = new DataInputStream(new FileInputStream(selectedFile)); 	    byte[] bytes = new byte[2048];  		// Read in the bytes 		int offset = 0; 		int numRead = 0; 		int fsize = (int)length; 		while (offset < fsize && (numRead=is.read(bytes, 0, bytes.length)) >= 0) { 			pData.setData(bytes, numRead); 			dout.write(pData.getPackageData(), 0, pData.getPackageData().length); 			dout.flush(); 			offset += numRead; 			float precent = 100.0f * ((float)offset)/((float)fsize); 			setProgress((int)precent); 		} 		System.out.println("total send bytes = " + offset); 		// Ensure all the bytes have been read in 		if (offset < fsize) { 			throw new IOException("Could not completely transfer file " + selectedFile.getName()); 		} 		mSocket.shutdownOutput(); 		 		// receive the file transfer successfully message from connection 		 		BufferedInputStream streamReader = new BufferedInputStream(mSocket.getInputStream()); 		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(streamReader)); 		String doneMsg = bufferedReader.readLine(); 		if("DONE".equals(doneMsg)) { 			parent.showSuccess(); 		} 		// Close the file input stream  		setProgress(100); 		// dout.close(); 		mSocket.close(); 		is.close(); 		System.out.println("close it now......"); 		return 100; 	} } 
數據包類以下,不解釋!

package com.gloomyfish.socket.tutorial.filetransfer; /**  * this is very simple file transfer protocol over TCP socket  * @date 2012-12-01  * @author zhigang jia  *  */ public class FileDataPackage {  	private int dataLength; // 數據包中數據長度,兩個字節 	private byte[] databuff; // 數據包中數據,meici最大不超過2048字節 	 	public final static byte[] EOF = new byte[]{'E', 'O','F'}; 	 	public FileDataPackage() { 		dataLength = 0; 		databuff = new byte[2048]; 	} 	 	public byte[] getPackageData() { 		byte[] pData = new byte[dataLength]; 		// end comment 		System.arraycopy(databuff, 0, pData, 0, dataLength); 		return pData; 	} 	 	public void setData(byte[] data, int bsize) { 		dataLength = bsize; 		for(int i=0; i<databuff.length; i++) { 			if(i<bsize) { 				databuff[i] = data[i]; 			} else { 				databuff[i] = ' '; 			} 		} 	} } 
每次發送的最大字節數爲2048個字節。程序最終運行效果以下(win7 + JDK6u30):



以爲不錯,請頂一下啊,謝謝!

轉載務必註明

相關文章
相關標籤/搜索