我尝试使用Java非阻塞连接发送文件。当我使用小文件时,它可以很好地工作,但是当我发送大于30MB的文件时,文件就会刹车。
要查看问题,只需在第一个参数上运行SocketsFileServer,文件大于30MB。之后运行SocketsFileClient。
任何人都可以帮助我吗?
SocketsFileServer.java
public class SocketsFileServer {
public final static int SOCKET_PORT = 13267;
Selector selector = null;
ServerSocketChannel serverSocketChannel = null;
private File file;
SocketsFileServer(File fileToServe) {
try {
file = fileToServe;
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket(). setReuseAddress (true);
serverSocketChannel.socket(). bind (new InetSocketAddress(SOCKET_PORT));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
System.exit(-1);
e.printStackTrace();
}
}
public void run() {
try {
while (selector.select() > 0) {
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext ()) {
SelectionKey readyKey = it.next();
it.remove();
if (readyKey.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) readyKey.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
} else if (readyKey.isWritable()) {
SocketChannel channel = (SocketChannel) readyKey.channel();
sendFile(channel, file);
channel.close();
} else if (readyKey.isReadable()) {
receiveCommand((SocketChannel) readyKey.channel());
readyKey.interestOps(SelectionKey.OP_WRITE);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
stop();
}
}
private void receiveCommand(SocketChannel socketChannel)
{
int BUFFER_SIZE = 255;
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
try {
socketChannel.read(buffer);
buffer.flip();
// Não interessa o que ler, apenas segue para continuar o funcionamento do protocolo
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendFile(SocketChannel socketChannel, File file) {
FileInputStream fis = null;
FileChannel channel = null;
try {
try {
// Envia tamanho do arquivo
ByteBuffer bufferSize = ByteBuffer.allocate(8);
bufferSize.putLong(file.length());
bufferSize.rewind();
socketChannel.write(bufferSize);
// Envia arquivo de fato
fis = new FileInputStream(file);
channel = fis.getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
int size = 0;
while ((size = channel.read(buffer)) > -1) {
buffer.rewind();
buffer.limit(size);
socketChannel.write(buffer);
buffer.clear();
}
socketChannel.socket().shutdownOutput();
} finally {
channel.close();
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void stop() {
try {
selector.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main (String [] args) {
if (args.length > 1) {
System.err.println("USO: SocketsFileServer [Arquivo]");
System.exit(1);
}
try {
File file;
if (args.length > 0) {
file = new File(args[0]);
if (!file.exists()) {
System.err.println("ERRO: Arquivo '" + args[0] + "' não existe");
System.exit(-1);
}
} else {
file = File.createTempFile("SocketsFileServer", ".tmp");
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("Este é um arquivo temporário criado apenas para teste!");
fileWriter.close();
}
Thread.currentThread().setName(SocketsFileServer.class.getName() + ".main()");
SocketsFileServer socEx = new SocketsFileServer(file);
socEx.run();
} catch (Throwable e) {
System.out.flush();
System.err.println("ERRO: " + e);
e.printStackTrace(System.err);
System.exit(-1);
}
}
}
SocketsFileClient.java
public class SocketsFileClient {
public final static int SOCKET_PORT = 13267;
public final static String SOCKET_SERVER = "127.0.0.1";
public final static int FILE_SIZE = 6022386; // precisa ser maior que o arquivo recebido
private String ip;
SocketsFileClient(String ip) {
try {
this.ip = ip;
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println("Connecting...");
Socket sock = new Socket(ip, SOCKET_PORT);
try {
int bytesRead;
File content = new File("content");
byte [] mybytearray = new byte [FILE_SIZE];
InputStream is = sock.getInputStream();
DataInputStream dis = new DataInputStream(is);
OutputStream os = sock.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
FileOutputStream fos = new FileOutputStream(content);
BufferedOutputStream bos = new BufferedOutputStream(fos);
try {
long startTime = System.nanoTime();
bw.write("GET [FILE NAME]\n");
bw.flush();
long fileSize = dis.readLong();
long startTransferTime = System.nanoTime();
while ((bytesRead = is.read(mybytearray, 0, mybytearray.length)) > -1) {
bos.write(mybytearray, 0, bytesRead);
}
bos.flush();
long endTime = System.nanoTime();
if (content.length() == fileSize) {
System.out.println("Completo [" + (i + 1) + "]");
System.out.println("Tamanho: " + content.length() + " bytes");
System.out.println("Tempo solicitação: " + (startTransferTime - startTime) / 1e6 + " milisegundos");
System.out.println("Tempo transferência: " + (endTime - startTransferTime) / 1e6 + " milisegundos");
System.out.println("Tempo total: " + (endTime - startTime) / 1e6 + " milisegundos");
} else {
System.out.println("Falha ao baixar conteúdo");
}
} finally {
fos.close();
bos.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sock.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main (String [] args) {
if (args.length > 2) {
System.err.println("USO: SocketsFileClient [IP]");
System.exit(1);
}
try {
Thread.currentThread().setName(SocketsFileClient.class.getName() + ".main()");
String ip = "";
if (args.length == 0) {
ip = SOCKET_SERVER;
} else {
ip = args[0];
}
SocketsFileClient socEx = new SocketsFileClient(ip);
socEx.run();
} catch (Throwable e) {
System.out.flush();
System.err.println("ERRO: " + e);
e.printStackTrace(System.err);
System.exit(-1);
}
}
}
答案 0 :(得分:2)
while ((size = channel.read(buffer)) > -1) {
buffer.rewind();
buffer.limit(size);
socketChannel.write(buffer);
buffer.clear();
}
这根本不正确。它可能随时丢失数据。编写此循环的正确方法如下:
while (channel.read(buffer) >= 0 || buffer.position() > 0) {
buffer.flip();
socketChannel.write(buffer);
buffer.compact();
}