java 使用 SocketChannel 进行文件传输
java Filetransfer with SocketChannel
我在使用 SocketChannels 进行文件传输时遇到问题:客户端结束传输文件,但服务器仍在等待来自客户端的更多字节。这会导致超时,文件会保存不到一小部分。服务器仍然卡在此处:"fileChannel.transferFrom(socketChannel, 0, fileLength);"。
之前发生的一切都正常工作。
服务器:
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import javax.imageio.ImageIO;
public class RequestHandler implements Runnable {
private SocketChannel socketChannel;
BufferedReader stringIn;
public RequestHandler(SocketChannel socketChannel) {
this.socketChannel = socketChannel;
// this.serverSocketChannel = socketChannel;
System.out.println("RequestHandler initialized");
}
public static int getLastPush(String dir) {
return new File("./" + dir).listFiles().length + 1;
}
public void run() {
LoadConfig config = null;
try {
config = new LoadConfig();
} catch (IOException e) {
e.printStackTrace();
}
String type = null;
try {
socketChannel.socket().setSoTimeout(10000);
MainServer.log("Client connected from: " + socketChannel);
// Prendere immagine
DataInputStream dis = new DataInputStream(socketChannel.socket().getInputStream());
// Leggo string
stringIn = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));
// Invio al client
DataOutputStream dos = new DataOutputStream(socketChannel.socket().getOutputStream());
// leggo in ricezione
MainServer.log("Attendo auth");
String auth = stringIn.readLine();
// check auth
MainServer.log("Auth ricevuto: " + auth);
String pass = config.getPass();
if (pass.equals(auth)) {
dos.writeBytes("OK\n");
System.out.println("Client Authenticated");
type = stringIn.readLine();
System.out.println("fileType: " + type);
dos.writeBytes(type + "\n");
Integer i = getLastPush(config.getFolder());
String fileName = i.toString();
System.out.println("fileName: " + fileName);
switch (type) {
case "img":
// transfer image
int len = dis.readInt();
System.out.println("Transfer started.");
byte[] data = new byte[len];
dis.readFully(data);
System.out.println("Transfer ended.");
File toWrite = new File(config.getFolder() + "/" + fileName + ".png");
ImageIO.write(ImageIO.read(new ByteArrayInputStream(data)), "png", toWrite);
dos.writeBytes("http://" + config.getDomain() + "/" + toWrite.getName());
break;
case "file":
// transfer file
System.out.println("Transfer started.");
readFileFromSocket(config.getFolder() + "/" + fileName + ".zip");
System.out.println("Transfer ended.");
System.out.println("Sending link...");
dos.writeBytes("http://" + config.getDomain() + "/" + fileName + ".zip");
break;
default:
}
i++;
System.out.println("Chiudo");
dos.close();
dis.close();
stringIn.close();
} else {
dos.writeBytes("Invalid Id or Password");
System.out.println("Invalid Id or Password");
dos.close();
dis.close();
stringIn.close();
}
socketChannel.close();
} catch (Exception exc) {
exc.printStackTrace();
}
System.out.println("----------");
}
public void readFileFromSocket(String fileName) {
RandomAccessFile aFile = null;
try {
aFile = new RandomAccessFile(fileName, "rw");
FileChannel fileChannel = aFile.getChannel();
long fileLength = Long.parseLong(stringIn.readLine());
System.out.println("File length: " + fileLength);
fileChannel.transferFrom(socketChannel, 0, fileLength);
fileChannel.close();
Thread.sleep(1000);
fileChannel.close();
System.out.println("End of file reached, closing channel");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
客户:
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import javax.imageio.ImageIO;
public class Uploader {
private BufferedImage img;
private byte[] bytes;
private SocketChannel socketChannel;
private String link;
private String fileName;
DataOutputStream dos;
// Per gli screen parziali
public Uploader(Rectangle r, String ip, int port) throws IOException, AWTException {
SocketChannel socketChannel = createChannel(ip, port);
this.socketChannel = socketChannel;
Rectangle screenRect = new Rectangle(0, 0, 0, 0);
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
screenRect = screenRect.union(gd.getDefaultConfiguration().getBounds());
}
this.img = new Robot().createScreenCapture(screenRect).getSubimage(r.x, r.y, r.width, r.height);
ByteArrayOutputStream outputArray = new ByteArrayOutputStream();
ImageIO.write(img, "png", outputArray);
outputArray.flush();
this.bytes = outputArray.toByteArray();
outputArray.close();
}
// Per gli screen completi
public Uploader(BufferedImage bi, String ip, int port) throws IOException {
SocketChannel socketChannel = createChannel(ip, port);
this.socketChannel = socketChannel;
this.img = bi;
ByteArrayOutputStream outputArray = new ByteArrayOutputStream();
ImageIO.write(img, "png", outputArray);
outputArray.flush();
this.bytes = outputArray.toByteArray();
outputArray.close();
}
// Per i file
public Uploader(String fileName, String ip, int port) throws UnknownHostException, IOException {
SocketChannel socketChannel = createChannel(ip, port);
this.socketChannel = socketChannel;
// this.socket = new Socket(ip, port);
this.fileName = fileName;
}
public void send(String pass, String type) throws IOException {
dos = new DataOutputStream(socketChannel.socket().getOutputStream());
BufferedReader stringIn = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));
try {
socketChannel.socket().setSoTimeout(10000);
// send auth
System.out.println("Sending auth");
dos.writeBytes(pass + "\n");
System.out.println("Auth sent: " + pass);
this.link = stringIn.readLine();
// this.link = os.println();
System.out.println("Auth reply: " + link);
if (this.link.equals("OK")) {
System.out.println("Sending type: " + type);
dos.writeBytes(type + "\n");
// Controllo e aspetto che il server abbia ricevuto il type
// corretto
if (stringIn.readLine().equals(type)) {
System.out.println("Il server riceve un: " + type);
switch (type) {
// image transfer
case "img":
System.out.println("Uploading image...");
dos.writeInt(bytes.length);
dos.write(bytes, 0, bytes.length);
dos.flush();
break;
// file transfer
case "file":
sendFile(fileName);
break;
// default case, hmm
default:
break;
}
// return link
System.out.println("Waiting link...");
this.link = stringIn.readLine();
System.out.println("Returned link: " + link);
bytes = null;
} else {
System.out.println("The server had a bad interpretation of the fileType");
}
} else {
System.out.println("Closed");
}
dos.close();
stringIn.close();
socketChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public SocketChannel createChannel(String ip, int port) {
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
SocketAddress socketAddress = new InetSocketAddress(ip, port);
socketChannel.connect(socketAddress);
System.out.println("Connected, now sending the file...");
} catch (IOException e) {
e.printStackTrace();
}
return socketChannel;
}
public void sendFile(String fileName) {
RandomAccessFile aFile = null;
try {
File file = new File(fileName);
aFile = new RandomAccessFile(file, "r");
FileChannel inChannel = aFile.getChannel();
long bytesSent = 0, fileLength = file.length();
System.out.println("File length: " + fileLength);
dos.writeBytes(fileLength + "\n");
// send the file
while (bytesSent < fileLength) {
bytesSent += inChannel.transferTo(bytesSent, fileLength - bytesSent, socketChannel);
}
inChannel.close();
Thread.sleep(1000);
System.out.println("End of file reached..");
aFile.close();
System.out.println("File closed.");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getLink() {
return link;
}
}
您正在丢失 BufferedReader
中的数据。如果您查看已收到的文件,您会发现它的开头部分丢失了。
您不能在同一通道上混合使用缓冲和非缓冲输入。我建议你使用 DataInputStream
并使用 read/writeUTF()
传输文件名,并使用 read/writeLong()
传输长度。
我无法想象您为什么要使用不同的代码来传输图像和其他文件。都是字节。
我在使用 SocketChannels 进行文件传输时遇到问题:客户端结束传输文件,但服务器仍在等待来自客户端的更多字节。这会导致超时,文件会保存不到一小部分。服务器仍然卡在此处:"fileChannel.transferFrom(socketChannel, 0, fileLength);"。 之前发生的一切都正常工作。
服务器:
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import javax.imageio.ImageIO;
public class RequestHandler implements Runnable {
private SocketChannel socketChannel;
BufferedReader stringIn;
public RequestHandler(SocketChannel socketChannel) {
this.socketChannel = socketChannel;
// this.serverSocketChannel = socketChannel;
System.out.println("RequestHandler initialized");
}
public static int getLastPush(String dir) {
return new File("./" + dir).listFiles().length + 1;
}
public void run() {
LoadConfig config = null;
try {
config = new LoadConfig();
} catch (IOException e) {
e.printStackTrace();
}
String type = null;
try {
socketChannel.socket().setSoTimeout(10000);
MainServer.log("Client connected from: " + socketChannel);
// Prendere immagine
DataInputStream dis = new DataInputStream(socketChannel.socket().getInputStream());
// Leggo string
stringIn = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));
// Invio al client
DataOutputStream dos = new DataOutputStream(socketChannel.socket().getOutputStream());
// leggo in ricezione
MainServer.log("Attendo auth");
String auth = stringIn.readLine();
// check auth
MainServer.log("Auth ricevuto: " + auth);
String pass = config.getPass();
if (pass.equals(auth)) {
dos.writeBytes("OK\n");
System.out.println("Client Authenticated");
type = stringIn.readLine();
System.out.println("fileType: " + type);
dos.writeBytes(type + "\n");
Integer i = getLastPush(config.getFolder());
String fileName = i.toString();
System.out.println("fileName: " + fileName);
switch (type) {
case "img":
// transfer image
int len = dis.readInt();
System.out.println("Transfer started.");
byte[] data = new byte[len];
dis.readFully(data);
System.out.println("Transfer ended.");
File toWrite = new File(config.getFolder() + "/" + fileName + ".png");
ImageIO.write(ImageIO.read(new ByteArrayInputStream(data)), "png", toWrite);
dos.writeBytes("http://" + config.getDomain() + "/" + toWrite.getName());
break;
case "file":
// transfer file
System.out.println("Transfer started.");
readFileFromSocket(config.getFolder() + "/" + fileName + ".zip");
System.out.println("Transfer ended.");
System.out.println("Sending link...");
dos.writeBytes("http://" + config.getDomain() + "/" + fileName + ".zip");
break;
default:
}
i++;
System.out.println("Chiudo");
dos.close();
dis.close();
stringIn.close();
} else {
dos.writeBytes("Invalid Id or Password");
System.out.println("Invalid Id or Password");
dos.close();
dis.close();
stringIn.close();
}
socketChannel.close();
} catch (Exception exc) {
exc.printStackTrace();
}
System.out.println("----------");
}
public void readFileFromSocket(String fileName) {
RandomAccessFile aFile = null;
try {
aFile = new RandomAccessFile(fileName, "rw");
FileChannel fileChannel = aFile.getChannel();
long fileLength = Long.parseLong(stringIn.readLine());
System.out.println("File length: " + fileLength);
fileChannel.transferFrom(socketChannel, 0, fileLength);
fileChannel.close();
Thread.sleep(1000);
fileChannel.close();
System.out.println("End of file reached, closing channel");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
客户:
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import javax.imageio.ImageIO;
public class Uploader {
private BufferedImage img;
private byte[] bytes;
private SocketChannel socketChannel;
private String link;
private String fileName;
DataOutputStream dos;
// Per gli screen parziali
public Uploader(Rectangle r, String ip, int port) throws IOException, AWTException {
SocketChannel socketChannel = createChannel(ip, port);
this.socketChannel = socketChannel;
Rectangle screenRect = new Rectangle(0, 0, 0, 0);
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
screenRect = screenRect.union(gd.getDefaultConfiguration().getBounds());
}
this.img = new Robot().createScreenCapture(screenRect).getSubimage(r.x, r.y, r.width, r.height);
ByteArrayOutputStream outputArray = new ByteArrayOutputStream();
ImageIO.write(img, "png", outputArray);
outputArray.flush();
this.bytes = outputArray.toByteArray();
outputArray.close();
}
// Per gli screen completi
public Uploader(BufferedImage bi, String ip, int port) throws IOException {
SocketChannel socketChannel = createChannel(ip, port);
this.socketChannel = socketChannel;
this.img = bi;
ByteArrayOutputStream outputArray = new ByteArrayOutputStream();
ImageIO.write(img, "png", outputArray);
outputArray.flush();
this.bytes = outputArray.toByteArray();
outputArray.close();
}
// Per i file
public Uploader(String fileName, String ip, int port) throws UnknownHostException, IOException {
SocketChannel socketChannel = createChannel(ip, port);
this.socketChannel = socketChannel;
// this.socket = new Socket(ip, port);
this.fileName = fileName;
}
public void send(String pass, String type) throws IOException {
dos = new DataOutputStream(socketChannel.socket().getOutputStream());
BufferedReader stringIn = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));
try {
socketChannel.socket().setSoTimeout(10000);
// send auth
System.out.println("Sending auth");
dos.writeBytes(pass + "\n");
System.out.println("Auth sent: " + pass);
this.link = stringIn.readLine();
// this.link = os.println();
System.out.println("Auth reply: " + link);
if (this.link.equals("OK")) {
System.out.println("Sending type: " + type);
dos.writeBytes(type + "\n");
// Controllo e aspetto che il server abbia ricevuto il type
// corretto
if (stringIn.readLine().equals(type)) {
System.out.println("Il server riceve un: " + type);
switch (type) {
// image transfer
case "img":
System.out.println("Uploading image...");
dos.writeInt(bytes.length);
dos.write(bytes, 0, bytes.length);
dos.flush();
break;
// file transfer
case "file":
sendFile(fileName);
break;
// default case, hmm
default:
break;
}
// return link
System.out.println("Waiting link...");
this.link = stringIn.readLine();
System.out.println("Returned link: " + link);
bytes = null;
} else {
System.out.println("The server had a bad interpretation of the fileType");
}
} else {
System.out.println("Closed");
}
dos.close();
stringIn.close();
socketChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public SocketChannel createChannel(String ip, int port) {
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
SocketAddress socketAddress = new InetSocketAddress(ip, port);
socketChannel.connect(socketAddress);
System.out.println("Connected, now sending the file...");
} catch (IOException e) {
e.printStackTrace();
}
return socketChannel;
}
public void sendFile(String fileName) {
RandomAccessFile aFile = null;
try {
File file = new File(fileName);
aFile = new RandomAccessFile(file, "r");
FileChannel inChannel = aFile.getChannel();
long bytesSent = 0, fileLength = file.length();
System.out.println("File length: " + fileLength);
dos.writeBytes(fileLength + "\n");
// send the file
while (bytesSent < fileLength) {
bytesSent += inChannel.transferTo(bytesSent, fileLength - bytesSent, socketChannel);
}
inChannel.close();
Thread.sleep(1000);
System.out.println("End of file reached..");
aFile.close();
System.out.println("File closed.");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getLink() {
return link;
}
}
您正在丢失 BufferedReader
中的数据。如果您查看已收到的文件,您会发现它的开头部分丢失了。
您不能在同一通道上混合使用缓冲和非缓冲输入。我建议你使用 DataInputStream
并使用 read/writeUTF()
传输文件名,并使用 read/writeLong()
传输长度。
我无法想象您为什么要使用不同的代码来传输图像和其他文件。都是字节。