基于TCP的文件传输工具——计算机网络课程设计




计算机网络课程设计,基于TCP连接的文件传输,适用于局域网,转载请注明出处,

作者:一只想翻身的咸鱼

客户端:

package Client;

//GuI绘画包
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
//监听事件包
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
//流输入输出包
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
//网络包
import java.net.ServerSocket;
import java.net.Socket;

//GuI绘画包
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;

public class Client {
	//窗口对象
	private Frame frame;
	private TextField textField1;
	private TextField textField2;
	private JButton button2;
	private Label label1;
	private Label label2;
	private Label label3;
	private Panel panel1;
	private Panel panel2;
	private Panel panel3;
	private Panel panel4;
	//提示窗口对象
	private FileDialog fileDialog = new FileDialog(frame);
	/**
	 * 要上传的文件对象
	 * */
	File file;
	/**
	 * 套接字输出流对象,用于向服务器输出字节流
	 * */
	DataOutputStream out;
	/**
	 * 文件输入流对象,用于从文件读取字节流
	 * */
	FileInputStream fis;
	/**
	套接字变量,此套接字用于记录当前连接的服务器套接字。
	*/
	Socket Sockets;
	/**
	字节流输入变量,此变量用于接受来自套接字的流数据。
	*/
	InputStream is_s;
	/**
	字符输入缓冲区,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。。
	*/
	BufferedReader br_s;
	/**
	字节流输出变量,此变量用于从套接字发送流数据。
	*/
	OutputStream os_s;
	/**
	字符输出缓冲区,向文本输出流打印对象的格式化表示形式。。
	*/
	PrintWriter pw_s;
	//构造函数
	/**
	 * 调用初始化函数,并新建一个连接监听线程
	 * */
	public Client()
	{
		Init();
	}
	//初始化函数
	private void Init()
	{
		/**
		 * 以下都是对窗口界面的设计,包括组件,面板,窗体等GUI设计
		 * */
		frame = new Frame("Client");
		frame.setLayout(null);
		panel1 = new Panel();
		panel2 = new Panel();
		panel3 = new Panel();
		panel4 = new Panel();
		panel4.setBounds(0, 90, 600, 100);
		panel3.setBounds(0, 180, 600, 200);
		
		frame.setBounds(300,100,600,400);
		frame.setVisible(true);
		panel2.setVisible(true);
		panel3.setVisible(true);
		panel3.setLayout(null);
		frame.add(panel3);

		textField1 = new TextField(31);
		textField1.setBounds(160, 70, 300, 30);
		textField1.setFont(new Font("微软雅黑",Font.PLAIN,18));
		textField2 = new TextField(31);
		textField2.setBounds(160, 120, 300, 30);
		textField2.setFont(new Font("微软雅黑",Font.PLAIN,18));
		button2 = new JButton("上传");
		button2.setBounds(250, 0, 80, 40);
		label1 = new Label();
		label2 = new Label();
		label3 = new Label();
		label1.setText("服务器IP");
		label1.setFont(new Font("微软雅黑",Font.PLAIN,16));
		label1.setBounds(70, 60, 80, 50);
		label2.setText("选择文件");
		label2.setBounds(70, 110, 80, 50);
		label2.setFont(new Font("微软雅黑",Font.PLAIN,16));
		label3.setText("上传进度:0%");
		label3.setFont(new Font("微软雅黑",Font.PLAIN,22));
		label3.setBounds(210, 60, 300, 100);
		frame.add(label1);
		frame.add(textField1);
		frame.add(label2);
		frame.add(textField2);
		panel3.add(button2);
		panel3.add(label3);
		/**
		 * 调用事件函数,为各个组件添加监听事件。
		 * */
		myEvent();
	}
	/**
	 * 事件监听函数。
	 * */
	private void  myEvent()
	{
		/**
		 * 为按钮添加活跃监听事件。
		 * */
		button2.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				jButton2_actionPerformed();
			}
		});
		/**
		 * 为窗体添加关闭监听事件
		 * */
		frame.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);	
			}
		});
		/**
		 * 为文件选择输入框添加鼠标点击事件
		 * 当鼠标点击该输入框时,弹出文件选择窗口,并将选择的文件路径写入到文本输入框。
		 * */
		textField2.addMouseListener(new MouseAdapter() {	
			public void mouseClicked(MouseEvent e) {
				textField2.setText("");
				fileDialog.setVisible(true);
				textField2.setText(fileDialog.getDirectory()+fileDialog.getFile());
			}
		});
	}
	
	/**
	 * 这是一个多线程,用来接收server发送过来的信息。
	 * 该线程在点击测试按钮时创建,在测试完成后结束。
	 * */
	class ServerMessage implements Runnable
	{
		public ServerMessage()
		{
		}
		public void run()
		{
			try{
				/**
				 * 该变量用于存储服务器发来的字符串。
				 * */
				String str = "";
				while(true)
				{
					/**
					 * 接收服务器传来的消息
					 * 该循环在收到消息并处理结果后结束
					 * */
					str = br_s.readLine();//读取一个文本行。
					/**
					 * 对服务器传来的字符串进行判定,
					 * 1、若是success则表明服务器允许上传,开启上传线程。结束循环。
					 * 2、若是end则表明文件上传结束,关闭连接及字节流,结束循环。
					 * 3、若是其他字符串则表明返回的是错误信息,显示接收到的错误信息,结束循环。
					 *
					 * */
					if(str!=null&&str.equals("success")) {
						JOptionPane.showMessageDialog(frame,"开始上传");
						new Thread(new UploadFile()).start();
						break;
					}else if(str!=null&&str.equals("end")) {
						JOptionPane.showMessageDialog(frame,"上传成功");
						/**
						 * 上传成功关闭连接资源。
						 * */
						is_s.close();
						os_s.close();
						br_s.close();
						pw_s.close();
						Sockets.close();
						break;
					}else if(str!=null){
						/**
						 * 提示错误信息
						 * */
						JOptionPane.showMessageDialog(frame,str);
						break;
					}
				}
			}
			catch (Exception e)
			{
				throw new RuntimeException("接收失败");
			}
			
		}
	}
	
	/**
	 * 文件上传线程,该线程在文件测试通过后执行,上传完成后结束。
	 * */
	class UploadFile implements Runnable
	{
		/**
		 * 初始化文件上传环境
		 * */
		//该变量存储文件大小,单位是字节
		long filelength = file.length();
		//该变量用于存储已经上传的文件大小,单位为字节
		long uploadlength = 0;
		//开启消息接收线程,用于接收服务器发送的传输成功的消息。
		public UploadFile()
		{	
			new Thread(new ServerMessage()).start();
		}
		/**
		 * 上传文件
		 * */
		public void run()
		{
				//字节缓冲区,用于存储从文件读取流中读取的字节
				byte[] bate = new byte[1024];
				//用于记录读取的字节数。
				int len = 0;
				/**
				 * 发送文件,直到文件发送完成。
				 * 发送完成后结束线程。
				 * */
				try {
					//从文件读取字节,直到读取到文件结束标记-1
					while((len = fis.read(bate,0,bate.length))!=-1) {
						//将读取到的字节,写入套接字输出流。
						out.write(bate, 0, len);
						//刷新输出流,将写入的字节发送到服务器。
						out.flush();
						//记录已经发送的字节数
						uploadlength+=len;
						//动态更新上传进度
						label3.setText("上传进度:"+((uploadlength*100)/filelength)+"%");
					}
					//文件传输完毕后,向服务器发送结束信号。
					Sockets.shutdownOutput();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
	
	/**
	 *该函数用于连接服务器。建立套接字,用于向服务器发送数据。		
	 */
	public void connection()
	{
			//从文本框读取服务器IP地址。
			String ip = textField1.getText();
			//判断读取的IP是否为空。
			if(ip!=null&&ip.length()!=0) {
				try
				{
					//创建套接字,并获取套接字的输入流与输出流对象。
					Sockets = new Socket(ip,10001);
					is_s = Sockets.getInputStream();//返回此套接字的输入流
					os_s = Sockets.getOutputStream();//返回此套接字的输出流
					br_s = new BufferedReader(new InputStreamReader(is_s));//将字节流转变为字符流
					pw_s = new PrintWriter(os_s);//将字节流转变为字符流
				}
				catch (Exception e)
				{
					JOptionPane.showMessageDialog(frame,"err:"+e.toString());
				}
			 }else {
				 //若IP为空,提示用户输入服务器IP地址。
				 JOptionPane.showMessageDialog(frame,"请输入IP");
			 }
	}
	/**
	 *该函数用于对按钮2事件的处理,当鼠标点击按钮2时,执行此函数。
	 *连接服务器,将文件信息发送给服务器,并启动线程接收服务器返回的文件测试信息。
	 */
	public void jButton2_actionPerformed()
	{
		connection();//连接服务器
		String str = "";
		str = textField2.getText();//获取文件绝对路径
		if(str!=null&&str.length()!=0)
		{
			file = new File(str);//根据文件路径创建文件对象。
			if(file.isFile())
			{
				try {
					//创建文件读取流对象。
					 fis = new FileInputStream(file);
					 //创建套接字输出流对象。
					 out = new DataOutputStream(Sockets.getOutputStream());
					 // 向服务器发送文件名
					 out.writeUTF(file.getName());
					 //刷新流
		             out.flush();
		             //向服务器发送文件大小
		             out.writeLong(file.length());
		             out.flush();
		             //启动消息接收线程,接收服务器的允许信息。
		             new Thread(new ServerMessage()).start();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else
			{
				JOptionPane.showMessageDialog(frame,"您选择的不是一个正确的文件");
			}
		}
	}
	/**
	 * 程序入口
	 * */
	public static void main(String[] args) 
	{
		new Client();
	}
}

客户端运行结果:

服务器:

package Server;

import java.awt.*;//GuI绘画包
import java.awt.event.*;//监听事件包
import javax.swing.*;//GuI绘画包
import java.net.*;//网络包
import java.text.DecimalFormat;
import java.io.*;//流输入输出包
import java.math.RoundingMode;
import java.util.*;//集合框架包

public class Server 
{
	/**
	 * 各种GUI组件变量定义
	 * */
	private Frame frame;
	private TextField textField1;
	private JButton button1;
	private Label label1;
	private Label label2;
	private TextArea textArea1;
	private Panel panel3;
	/**
	 * 上传文件存放的文件夹
	 * 默认为d:/upload
	 * */
	private File uploadDir= new File("d:/upload/");
	/**
	 *用于格式化十进制数字格式,设置数字显示格式
	 * */
	private static DecimalFormat df = null;

	    static {
	        // 设置数字格式,保留一位有效小数
	        df = new DecimalFormat("#0.0");
	        /**
	         * 设置舍入方式
	         * RoundingMode.HALF_UP:向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。
	         * */
	        df.setRoundingMode(RoundingMode.HALF_UP);
	        //设置某个数的小数部分中所允许的最小数字位数。
	        df.setMinimumFractionDigits(1);
	        //设置某个数的小数部分中所允许的最大数字位数。
	        df.setMaximumFractionDigits(1);
	    }
	/**
	 * 构造函数,对象建立时执行
	 * */
	public Server()
	{
		/**
		 * 掉用初始化函数,初始化程序界面。
		 * */
		Init();
		/**
		 * 创建初始上传文件接收文件夹
		 * */
		uploadDir.mkdirs();
		/**
		 * 启动线程接收来自客户端的连接请求。
		 * */
		new Thread(new ServerStart()).start();
	}
	/**
	 * 初始化函数,初始化界面,添加监听机制。
	 * */
	private void Init()
	{
		frame = new Frame("Server");
		panel3 = new Panel();
		panel3.setBounds(2, 130, 580, 400);
		frame.setBounds(300,100,600,500);
		frame.setVisible(true);
		panel3.setVisible(true);
		panel3.setLayout(null);
		frame.setLayout(null);
		frame.add(panel3);

		label1 = new Label();
		label2 = new Label();
		label1.setText("文件存储目录");
		label1.setBounds(50, 65, 100, 50);
		label1.setFont(new Font("微软雅黑",Font.PLAIN,16));
		label2.setText("上传记录");
		label2.setFont(new Font("微软雅黑",Font.PLAIN,16));
		label2.setBounds(20, 0, 100, 40);
		textField1 = new TextField(31);
		textField1.setBounds(160, 70, 270, 35);
		textField1.setFont(new Font("微软雅黑",Font.PLAIN,18));
		button1 = new JButton("确定");
		button1.setBounds(450, 70, 70, 35);
		textArea1 = new TextArea();
		textArea1.setFont(new Font("微软雅黑",Font.PLAIN,14));
		textArea1.setBounds(10, 40, 590, 320);
		frame.add(label1);
		frame.add(textField1);
		frame.add(button1);
		panel3.add(label2);
		panel3.add(textArea1);
		/**
		 * 为各个组件添加事件监听机制。
		 * */
		myEvent();
	}
	/**
	 * 设置文件上传目录,获取文本框中的值,若为空则使用默认值。
	 * */
	private void setUploadDir() {
		/**
		 * 从文本输入框接收用户输入的接收文件夹路径。
		 * */
		String str = textField1.getText();
		/**
		 * 判断输入是否为空
		 * */
		if(str!=null&&str.length()!=0) {
			File dir = new File(str);
			/**
			 * 测试是否有文件目录,若无则根据文件上传目录创建一个新目录
			 * */
			if(!dir.exists()) {
				dir.mkdirs();
			}
			uploadDir = dir;
			JOptionPane.showMessageDialog(frame,"目录设置成功");
		}else {
			JOptionPane.showMessageDialog(frame,"请输入目录绝对路径");
		}
	}
	
	/**
	 * 为各个组件添加监听机制
	 * */
	private void  myEvent()
	{
		/**
		 * 为“确认”按钮添加活跃监听
		 * */
		button1.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				setUploadDir();
			}
		});
		/**
		 * 为窗体添加关闭事件监听
		 * */
		frame.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});
	}
	/**
	 * 该线程用于监听来自客户端的连接请求,并为每一个请求创建一个线程用于验证并接受上传文件
	 * 该线程在服务器启动时启动,服务器关闭时关闭。
	 * */
	class ServerStart implements Runnable
	{
		/**
		 * ServerSocket对象用于接收客户端连接,并获取客户端套接字
		 * */
		private ServerSocket SSocket1;
		public ServerStart()
		{
			/**
			 * 设置监听端口为10001
			 * */
			try
			{
				SSocket1 = new ServerSocket(10001);
			}
			catch (Exception e)
			{}
			
		}
		public void run()
		{
			try
			{
				while(true)
				{
					/**
					 * 获取客户端套接字。
					 * */
					Socket sk =new Socket();
					sk = SSocket1.accept();
					if(sk != null)
					{
						/**
						 * 启动一个线程用于接管该客户端文件传输的的所有事件
						 * 向该线程传递客户端套接字sk。
						 * */
						new Thread(new Task(sk)).start();
					}
					else
					{
						sk.close();
					}
				}
			}
			catch (Exception e)
			{
				throw new RuntimeException("接受失败");
			}
			
		}
	}
	/**
	 * 该线程用于接管一个客户端上传文件的所有流程
	 * 在客户端连接时创建,在客户端注销时结束。
	 * */
	class Task implements Runnable {
		//该线程的客户端套接字
        private Socket socket;
        //套接字输入流
        private DataInputStream dis;
        //文件输出流
        private FileOutputStream fos;
        //接收传递过来的客户端套接字
        public Task(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
            	//获取套接字的输入流对象
                dis = new DataInputStream(socket.getInputStream());
                
                // 接收文件名
                String fileName = dis.readUTF();
                //接收文件总大小
                long fileLength = dis.readLong();
                //文件已经接收的大小
                long uploadlength = 0;
                //根据设置的文件保存目录及传递过来的文件名创建文件对象
                File file = new File(uploadDir.getAbsolutePath() + File.separatorChar + fileName);
                //判断文件是否存在
                if(file.exists()) {
                	send(socket,"文件已存在\n");
                }else {
                	/**
                	 * 文件不存在,可以上传
                	 * */
                	//向客户端发送上传信号
                	send(socket,"success\n");
                	//创建字符串容器对象用于存储,该上传文件的路径、上传客户端的IP、已经上传的大小及上传进度
                	StringBuffer sb = new StringBuffer();
                	//获取上传客户端IP
            		sb.append(socket.getInetAddress().toString().substring(1)+":");
            		//获取上传文件路径
            		sb.append(file.getPath()+"已上传:");
                	// 获取文件输出流,用于向文件中写入数据
                	fos = new FileOutputStream(file);
                	//字节缓冲区,作为输入流与输出流之间的桥梁
                	byte[] bytes = new byte[1024];
                	//用于记录每次读取的字节数
                    int length = 0;
                    //循环读取套接字传递的数据
                    while((length = dis.read(bytes, 0, bytes.length)) != -1) {
                    	//向文件中写入服务器接收的数据
                        fos.write(bytes, 0, length);
                        /**
        				 * 显示上传进度
        				 * */
                        //记录已经上传的总字节数
                        uploadlength+=length;
                        //计算上传百分比
        				double plan = (uploadlength*100)/fileLength;
        				//获取显示区的文本内容
        				String text = textArea1.getText();
        				//判定显示区的内容是否为空,若为空则说明是服务器启动后的第一个上传文件
        				if(text.length()==0||text==null) {
        					//向文本显示区写入文件上传的信息
        					textArea1.setText(sb.toString()+"[Size:" + getFormatFileSize(uploadlength) + "]:"+plan+"%");
        				}else {
        					/**
        					 * 文本显示区不为空,则说明该文件是服务器启动后的第二及其以后的文件
        					 * */
        					//获取该文件信息在文本显示区的位置。
        					int index = text.indexOf(sb.toString());
        					//若位置为-1,则说明该文件信息第一次显示在文本显示区
        					if(index==-1) {
        						//直接写入文件信息
        						textArea1.setText(text+"\n"+sb.toString()+"[Size:" + getFormatFileSize(uploadlength) + "]:"+plan+"%");
        					}else {
        						/**
        						 * 不是第一次显示,获取原本文件信息,对其进行更新
        						 * */
        						//获取文件信息的末尾位置
        						int last =text.indexOf('%', index);
        						//截取旧的文件信息
        					    String oldstr = text.substring(index, last+1);
        					    //用新的文件信息,替换旧的文件信息
        					    text = text.replace(oldstr, sb.toString()+"[Size:" + getFormatFileSize(uploadlength) + "]:"+plan+"%");
        					    //将更新后的文本显示在文本显示区
        					    textArea1.setText(text);
        					}
        					
        				}
        				//刷新文件写出流
                        fos.flush();
                    }
                    //文件上传完毕,向客户端发送文件上传结束消息。
                    send(socket,"end\n");   
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            	/**
            	 * 关闭套接字及流缓冲区
            	 * */
               try {
                   if(fos != null)
                       fos.close();
                   if(dis != null)
                        dis.close();
                 		socket.close();
              } catch (Exception e) {}
           }
        }
    }

    /**
     * 格式化文件大小,将文件大小从字节数转换为其他单位的值
     */
    private String getFormatFileSize(long length) {
    	//获取有多少GB
        double size = ((double) length) / (1 << 30);
        //判断是否大于1GB
        if(size >= 1) {
            return df.format(size) + "GB";
        }
        //获取有多少MB
        size = ((double) length) / (1 << 20);
        //判断是否大于1MB
        if(size >= 1) {
            return df.format(size) + "MB";
        }
        //获取有多少KB
        size = ((double) length) / (1 << 10);
        //判断是否大于1KB
        if(size >= 1) {
            return df.format(size) + "KB";
        }
        return length + "B";
    }
    /**
     * 该方法用于向客户端发送应答信息
     * */
	public void send(Socket sk,String Str)
	{
		//套接字字节输出流
		OutputStream os_sd=null;
		//字符字节转换流,用于将字符转化为字节流
		PrintWriter pw_sd=null;
		try
		{
			/**
			 * 获取流对象
			 * */
			os_sd = sk.getOutputStream();
			pw_sd = new PrintWriter(os_sd);
		}
		catch (Exception e)
		{}
		if(sk!= null)//判断是否连接
		{
			try
			{
				//写入消息字符串
				pw_sd.write(Str);
				//刷新流内容,将内容发送出去
				pw_sd.flush();
			}
			catch (Exception e1)
			{
				JOptionPane.showMessageDialog(frame,"发送失败!");
			}
		}
	}
	/**
	 * 程序入口
	 * */
	public static void main(String[] args) 
	{
		new Server();
	}
}

服务器端运行结果:


猜你喜欢

转载自blog.csdn.net/qq_40756113/article/details/80500113