用java nio模拟下载服务

本文介绍了一个使用Java NIO技术实现的模拟下载服务系统。该系统包括服务端和客户端两部分,服务端能够同时处理多个文件的下载请求,客户端则可以并发地从服务端下载多个文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**

 * Fly_m at 2009-5-20

 */

package com.m_ylf.study.nio;

 

import java.io.File;

import java.io.FileInputStream;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.*;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.concurrent.Callable;

 

/** @author Fly_m */

//模拟下载服务

public class DownloadServer<T> implements Callable<T>{

private Selector selector;//创建全局selector

private Map<SocketChannel, Handle> map = new HashMap<SocketChannel, Handle>();//socketChannel和handle之间的映射

 

//创建一个服务器serverSocketChannel,并与selector进行注册

public DownloadServer() throws Exception {

selector = Selector.open();

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.configureBlocking(false);

serverSocketChannel.socket().bind(new InetSocketAddress(1234));

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

}

 

//对selector.select进行迭代,并依次进行处理

public T call() throws Exception {

System.out.println("startTo listen in 1234....");

for(; ;) {

selector.select();

Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();

while(keyIterator.hasNext()) {

SelectionKey key = keyIterator.next();

if(key.isValid())

handle(key);

keyIterator.remove();

}

}

}

 

//处理每个key,对于acceptable的key,由主类进行处理,而其他事件,则由内部类进行处理

private void handle(final SelectionKey key) throws Exception {

if(key.isAcceptable()) {

ServerSocketChannel channel = (ServerSocketChannel) key.channel();

SocketChannel socketChannel = channel.accept();

socketChannel.configureBlocking(false);

socketChannel.register(selector, SelectionKey.OP_READ);//注册读事件

map.put(socketChannel, new Handle());//把socket和handle进行绑定

}

//用map中的handle处理read和write事件,以模拟多个文件同时进行下载

if(key.isReadable() || key.isWritable()) {

SocketChannel socketChannel = (SocketChannel) key.channel();

final Handle handle = map.get(socketChannel);

if(handle != null)

handle.handle(key);

}

}

 

//内部类,模拟一个内部类处理一个文件下载服务,多个类可以处理多个文件下载服务

private class Handle{

private StringBuilder message;

private boolean writeOK = true;

private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

private FileChannel fileChannel;

private String fileName;

 

private void handle(SelectionKey key) throws Exception {

if(key.isReadable()) {

SocketChannel socketChannel = (SocketChannel) key.channel();

if(writeOK)

message = new StringBuilder();

while(true) {

byteBuffer.clear();

int r = socketChannel.read(byteBuffer);

if(r == 0)

break;

if(r == -1) {

socketChannel.close();

key.cancel();

return;

}

message.append(new String(byteBuffer.array(), 0, r));

}

//将接收到的信息转化成文件名,以映射到服务器上的指定文件

if(writeOK && invokeMessage(message)) {

socketChannel.register(selector, SelectionKey.OP_WRITE);

writeOK = false;

}

}

//向客户端写数据

if(key.isWritable()) {

if(!key.isValid())

return;

SocketChannel socketChannel = (SocketChannel) key.channel();

if(fileChannel == null)

fileChannel = new FileInputStream(fileName).getChannel();

byteBuffer.clear();

int w = fileChannel.read(byteBuffer);

//如果文件已写完,则关掉key和socket

if(w <= 0) {

fileName = null;

fileChannel.close();

fileChannel = null;

writeOK = true;

socketChannel.close();

key.channel();

return;

}

byteBuffer.flip();

socketChannel.write(byteBuffer);

}

}

 

//将信息转化成文件名

private boolean invokeMessage(StringBuilder message) {

String m = message.toString();

try {

File f = new File(m);

if(!f.exists())

return false;

fileName = m;

return true;

} catch(Exception e) {

return false;

}

}

 

}

 

public static void main(String[] args) throws Exception {

/*

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.submit(new DownloadServer<Object>());

executorService.shutdown();

*/

new DownloadServer().call();

}

}

/**
 * Fly_m at 2009-5-20
 */
package com.m_ylf.study.nio;
import java.io.RandomAccessFile;
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.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.net.InetSocketAddress;
/** @author Fly_m */
//模拟下载客户端
public class DownloadClient<T> implements Callable<T>{
private FileChannel fileChannel;
private static Selector selector;
private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
private String serverFileName;//服务器上的文件
private String localFileName;//下载到客户端的文件名
public DownloadClient(String serverFileName, String localFileName) {
this.serverFileName = serverFileName;
this.localFileName = localFileName;
}
public T call() throws Exception {
//开启selector,并建立socket到指定端口的连接
if(selector == null)
selector = Selector.open();
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("localhost", 1234));
channel.register(selector, SelectionKey.OP_CONNECT);
//进行信息读取
for(; ;) {
selector.select();
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
//连接事件
if(key.isConnectable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
if(socketChannel.isConnectionPending())
socketChannel.finishConnect();
socketChannel.write(ByteBuffer.wrap(serverFileName.getBytes()));//向服务器发信息,信息中即服务器上的文件名
socketChannel.register(selector, SelectionKey.OP_READ);
}
//读事件
if(key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
byteBuffer.clear();
if(!socketChannel.isConnected())
return null;
//向本机下载文件创建文件channel
if(fileChannel == null)
fileChannel = new RandomAccessFile(localFileName, "rw").getChannel();
int r = socketChannel.read(byteBuffer);
//如果文件下载完毕,则关掉channel,同时关掉socketChannel
if(r <= 0) {
if(fileChannel != null)
fileChannel.close();
channel.close();
key.cancel();
return null;
}
byteBuffer.flip();
//写到下载文件中
fileChannel.write(byteBuffer);
}
}
}
}
//客户端用10个线程向服务器端下载文件,并保存为不同的文件
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i = 0; i < 10; i++) {
executorService.submit(new DownloadClient<Object>("d:/log4j.log", "d:/down" + i + ".log"));
}
executorService.shutdown();
}
}
### Java NIO 异步文件下载示例 为了展示如何利用 `AsynchronousFileChannel` 实现异步文件下载,下面提供了一个简单的例子。此代码片段展示了如何创建一个异步文件通道并执行读取操作。 ```java import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.concurrent.Future; public class AsyncDownloadExample { public static void main(String[] args) { Path path = Paths.get("downloaded_file.txt"); try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { // 创建新的异步文件通道[^3] ByteBuffer buffer = ByteBuffer.allocate(8 * 1024); // 分配缓冲区 byte[] dataToWrite = "Data to be written asynchronously".getBytes(); // 准备要写入的数据 buffer.put(dataToWrite); buffer.flip(); Future<Integer> writeResultFuture = fileChannel.write(buffer, 0); // 执行异步写入操作 while (!writeResultFuture.isDone()) { System.out.println("Waiting for the asynchronous operation to complete..."); Thread.sleep(1000); // 模拟等待时间 } Integer bytesWritten = writeResultFuture.get(); System.out.printf("%d bytes were successfully written.%n", bytesWritten); } catch (IOException | InterruptedException e) { e.printStackTrace(); } catch (Exception ex){ ex.printStackTrace(); } } } ``` 这段程序模拟了从网络接收数据并将这些数据保存到本地磁盘的过程。实际应用中,应该替换 `"Data to be written asynchronously"` 这部分逻辑为真正的HTTP请求或其他形式的远程资源获取机制,并将接收到的内容填充至 `ByteBuffer` 中再调用 `fileChannel.write()` 方法完成异步存储过程[^1]。 对于真实的文件下载场景来说,通常会涉及到更复杂的错误处理、进度报告以及可能存在的并发控制等问题,在这里仅给出基本框架供理解概念之用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值