使用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(); } }
服务器端
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(); } }