java nio文件傳輸例子
使用nio傳輸文件需要注意的是會出現粘包和服務器端緩沖區滿的情況。第一種情況,客戶端發送30次數據,而服務器端只接收到18次的情況,這種情況出現 主要是服務器端是以流的方式接收數據,它并不知道每次客戶端傳輸數據的大小而造成的。第二種情況是服務器端緩沖區滿,導致客戶端數據傳輸失敗,這種情況 下,需要判斷傳輸int send = client.write(sendBuffer)的send值,如果send值為0,則服務器端的數據緩沖區可能滿了。
客戶端實現代碼:
import java.io.FileInputStream; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Set;public class FileClient { private int port = 8000; / 發送數據緩沖區 / private static ByteBuffer sendBuffer = ByteBuffer.allocate(1024); / 接受數據緩沖區 / private static ByteBuffer revBuffer = ByteBuffer.allocate(1024); private InetSocketAddress SERVER; private static Selector selector; private static SocketChannel client;
public FileClient(){ try{ SERVER = new InetSocketAddress("localhost", port); init(); } catch(Exception e){ e.printStackTrace(); } } private void init(){ try { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_CONNECT); socketChannel.connect(SERVER); while (true) { selector.select(); Set<SelectionKey> keySet = selector.selectedKeys(); for (final SelectionKey key : keySet) { if(key.isConnectable()){ client = (SocketChannel)key.channel(); client.finishConnect(); client.register(selector, SelectionKey.OP_WRITE); } else if(key.isWritable()){ sendFile(client); } } keySet.clear(); } } catch (Exception e) { e.printStackTrace(); } } private void sendFile(SocketChannel client) { FileInputStream fis = null; FileChannel channel = null; try {
// fis = new FileInputStream("E:\1.txt"); // fis = new FileInputStream("E:\1.rar"); fis = new FileInputStream("G:\3.rar"); channel = fis.getChannel(); int i = 1; int count = 0; while((count = channel.read(sendBuffer)) != -1) { sendBuffer.flip(); int send = client.write(sendBuffer); System.out.println("i===========" + (i++) + " count:" + count + " send:" + send); // 服務器端可能因為緩存區滿,而導致數據傳輸失敗,需要重新發送 while(send == 0){ Thread.sleep(10); send = client.write(sendBuffer); System.out.println("i重新傳輸====" + i + " count:" + count + " send:" + send); } sendBuffer.clear(); } } catch (Exception e) { e.printStackTrace(); } finally { try { channel.close(); fis.close(); client.close(); } catch (Exception e) { e.printStackTrace(); }
} } public static void main(String[] args){ new FileClient(); }
}</pre>
服務器端import java.io.FileOutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Set;public class FileServer { private int port = 8000; / 發送數據緩沖區 / private static ByteBuffer revBuffer = ByteBuffer.allocate(1024); private static Selector selector; private static FileOutputStream fout; private static FileChannel ch; public FileServer(){ try{ init(); } catch(Exception e){ e.printStackTrace(); } } private void init() throws Exception{ ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); ServerSocket serverSocket = serverSocketChannel.socket(); serverSocket.bind(new InetSocketAddress(port)); selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("server start on port:" + port); while (true) { try { selector.select();// 返回值為本次觸發的事件數 Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey key : selectionKeys) { ServerSocketChannel server = null; SocketChannel client = null; int count = 0; if (key.isAcceptable()) { server = (ServerSocketChannel) key.channel(); System.out.println("有客戶端連接進入=============)"); client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); fout = new FileOutputStream("G:\\" + client.hashCode() + ".rar"); ch = fout.getChannel(); } else if (key.isReadable()) { client = (SocketChannel) key.channel(); revBuffer.clear(); count = client.read(revBuffer); int k = 0; // 循環讀取緩存區的數據, while(count > 0){ System.out.println("k=" + (k++) + " 讀取到數據量:" + count); revBuffer.flip(); ch.write(revBuffer); fout.flush(); revBuffer.clear(); count = client.read(revBuffer); } if(count == -1){ client.close(); ch.close(); fout.close(); } } else if (key.isWritable()) { System.out.println("selectionKey.isWritable()"); } } System.out.println("=======selectionKeys.clear()"); selectionKeys.clear(); } catch (Exception e) { e.printStackTrace(); break; } } } public static void main(String[] args){ new FileServer(); }
}</pre>