【任务】
写一个文件传输服务器和客户端。客户端用“GET 文件名”和“PUT 文件名”请求下载或上传文件。
这里要自定义文件传输协议。每传输一个文件都新建立一个连接,首先是客户端发送请求长度和请求内容,接着服务器根据自身情况向发送响应长度和响应内容,然后文件的发送方发送文件长度和文件内容,最后关闭连接。
为了能够练习尽量多的知识点,客户端用的是阻塞通信+线程池来实现的,服务端用的是非阻塞通信实现的。
现在的代码已经能正常行使功能,只是健壮性不强,所以还不完美。
【客户端代码】
package c_transfer_file;
import java.io.*;
public class App
{
public static void main(String[] args) throws IOException, InterruptedException
{new ClientTransferFile("localhost").work();}
}
package c_transfer_file;
import java.io.*;
import java.util.concurrent.*;
public class ClientTransferFile //文件传输客户端
{
String serverName; //服务器名
BufferedReader keyboardIn; //键盘输入流
ExecutorService threadPool; //线程池
public ClientTransferFile(String serverName)
{
this.serverName = serverName;
keyboardIn = new BufferedReader(new InputStreamReader(System.in)); //获得键盘输入流
threadPool = Executors.newCachedThreadPool(); //创建线程池
}
public void work() throws IOException, InterruptedException
{
/*********工作循环*********/
while(true)
{
/*********从键盘获取并解析用户要求*********/
String command = keyboardIn.readLine();
String command_up = command.toUpperCase();
if(command_up.equals("Q"))
break;
else if(command_up.startsWith("GET "))
{
String fileName = command.substring(4);
threadPool.submit(new TaskGetFile(serverName,fileName));
}
else if(command_up.startsWith("PUT "))
{
String fileName = command.substring(4);
threadPool.submit(new TaskPutFile(serverName,fileName));
}
else
System.out.println(command+"是未定义的命令");
/*********从键盘获取并解析用户要求*********/
}
/*********工作循环*********/
keyboardIn.close();
/*********关闭线程池*********/
threadPool.shutdownNow();
if(!threadPool.awaitTermination(30,TimeUnit.SECONDS))
{
threadPool.shutdownNow();
System.out.println("强制关闭了线程池");
}
/*********关闭线程池*********/
}
}
package c_transfer_file;
import java.util.concurrent.*;
import java.io.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
public class TaskGetFile implements Callable<Object> //线程任务:下载文件
{
String serverName; //服务器名
String fileName; //下载文件的名称
public TaskGetFile(String serverName, String fileName)
{
this.serverName = serverName;
this.fileName = fileName;
}
public Object call() throws IOException //线程任务的流程
{
/*********建立连接*********/
InetSocketAddress serverPort = new InetSocketAddress(serverName, 5299); //服务器端口地址
SocketChannel socketChannel;
try
{socketChannel = SocketChannel.open(serverPort);} //建立连接
catch(UnsupportedAddressTypeException e)
{
System.out.println(serverName+"是不支持的地址类型");
return null;
}
catch(UnresolvedAddressException e)
{
System.out.println("无法解析地址"+serverName);
return null;
}
catch(ConnectException e)
{
System.out.println(serverName+"拒绝了连接请求");
return null;
} //这里不要把SocketChannel关掉,因为SocketChannel还不存在
/*********建立连接*********/
/*********获得网络输入输出流*********/
Socket socket = socketChannel.socket();
DataInputStream socketIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream socketOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
/*********获得网络输入输出流*********/
/*********发送请求*********/
byte[] requestBytes = ("GET "+fileName).getBytes(Charset.forName("utf-8"));
socketOut.writeInt(requestBytes.length);
socketOut.write(requestBytes);
socketOut.flush();
/*********发送请求*********/
/*********接收响应信息*********/
int responseLen = socketIn.readInt(); //响应信息的长度
byte[] responseBytes = new byte[responseLen]; //用于接收响应信息的字节数组
socketIn.read(responseBytes);
String response = new String(responseBytes, Charset.forName("utf-8")); //响应信息
if(!response.equals("OK")) //如果响应信息表示没法下载
{
System.out.println(response);
socketChannel.close();
return null;
}
/*********接收响应信息*********/
/*********创建写文件流*********/
BufferedOutputStream writeFileStream;
try
{writeFileStream = new BufferedOutputStream(new FileOutputStream(fileName));}
catch(FileNotFoundException e)
{
System.out.println("无法创建文件"+fileName);
socketChannel.close();
return null;
}
/*********创建写文件流*********/
/*********下载和转存文件*********/
int fileLen = socketIn.readInt(); //文件长度
for(int i = 0; i < fileLen; i++)
writeFileStream.write(socketIn.read());
writeFileStream.flush();
writeFileStream.close();
/*********下载和转存文件*********/
System.out.println("文件"+fileName+"下载完成");
socketChannel.close(); //断开连接
return null;
}
}
package c_transfer_file;
import java.util.concurrent.*;
import java.io.*;
import java.nio.charset.*;
import java.net.*;
import java.nio.channels.*;
public class TaskPutFile implements Callable<Object> //线程任务:上传文件
{
String serverName; //服务器名
String fileName; //上传文件的名称
public TaskPutFile(String serverName, String fileName)
{
this.serverName = serverName;
this.fileName = fileName;
}
public Object call() throws IOException //线程任务的流程
{
/*********创建读文件流*********/
BufferedInputStream readFileStream;
try
{readFileStream = new BufferedInputStream(new FileInputStream(fileName));}
catch(FileNotFoundException e)
{
System.out.println("文件"+fileName+"不存在");
return null;
}
/*********创建读文件流*********/
/*********建立连接*********/
InetSocketAddress serverPort = new InetSocketAddress(serverName, 5299); //服务器端口地址
SocketChannel socketChannel;
try
{socketChannel = SocketChannel.open(serverPort);} //建立连接
catch(UnsupportedAddressTypeException e)
{
System.out.println(serverName+"是不支持的地址类型");
readFileStream.close();
return null;
}
catch(UnresolvedAddressException e)
{
System.out.println("无法解析地址"+serverName);
readFileStream.close();
return null;
}
catch(ConnectException e)
{
System.out.println(serverName+"拒绝了连接请求");
readFileStream.close();
return null;
} //这里不要把SocketChannel关掉,因为SocketChannel还不存在
/*********建立连接*********/
/*********获得网络输入输出流*********/
Socket socket = socketChannel.socket();
DataInputStream socketIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream socketOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
/*********获得网络输入输出流*********/
/*********发送请求*********/
byte[] requestBytes = ("PUT "+fileName).getBytes(Charset.forName("utf-8"));
socketOut.writeInt(requestBytes.length);
socketOut.write(requestBytes);
socketOut.flush();
/*********发送请求*********/
/*********接收响应信息*********/
int responseLen = socketIn.readInt(); //响应信息的长度
byte[] responseBytes = new byte[responseLen]; //用于接收响应信息的字节数组
socketIn.read(responseBytes);
String response = new String(responseBytes, Charset.forName("utf-8")); //响应信息
if(!response.equals("OK")) //如果响应信息表示没法上传
{
System.out.println(response);
socketChannel.close();
readFileStream.close();
return null;
}
/*********接收响应信息*********/
/*********读取和上传文件*********/
int fileLen = readFileStream.available(); //文件长度
socketOut.writeInt(fileLen);
for(int i = 0; i < fileLen; i++)
socketOut.write(readFileStream.read());
socketOut.flush();
readFileStream.close();
/*********读取和上传文件*********/
socketChannel.close(); //断开连接
System.out.println("文件"+fileName+"上传完成");
return null;
}
}
未完待续