java网络编程实例2.1——用阻塞+线程池实现的文件传输客户端

7 篇文章 0 订阅
2 篇文章 0 订阅

【任务】

写一个文件传输服务器和客户端。客户端用“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;
	}
}
未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值