使用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(); } }

在使用IntelliJIDEAUltimate版本启动Spring...

在使用MyBatis-Plus或其他ORM框架进行数据库操作时,经常需要根据实体类的属性名构造查询条件。如果每次都手动...

Redis缓存方案如何实现产品排行榜列表的需求?在开发过程中,我们常常需要处理排行榜的需求,例如展示一个�...

将姓名转换为数字以实现排序的解决方案在许多应用场景中,用户可能需要在群组中进行排序,尤其是在一个用...

电商平台SKU和SPU表设计详解本文将探讨电商平台中SKU和SPU的数据库设计问题,特别是如何处理用户自定义销售属...

在Idea中如何设置SpringBoot项目默认运行配置列表在使用IntelliJ...


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

Atom编辑器mac版下载
最流行的的开源编辑器

SublimeText3 Linux新版
SublimeText3 Linux最新版

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

禅工作室 13.0.1
功能强大的PHP集成开发环境

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。