使用Java的Socket套接字实现echo和大文件传输

初学Java的socket,简单地实现了echo功能,用socket实现局域网内部大文件的快速传输,鉴于socket的简单性,没有用nio。服务器和客户端连接成功后,echo功能将服务器接收到的数据行原样输出到客户端。大文件传输前,程序现在服务器上遍历所有文件,生成一个dir.txt的目录文档,经过客户端的请求将它传输到客户端,由客户端查找需要的文件后输入文件名下载,每次文件传输完毕后,客户端会断开连接,保证数据接收的完整性。

实现过程中遇到了很多问题,归纳如下:

(1)用字符流BufferedReader读非文本文件,导致传输数据丢失,参考资料:http://wenku.baidu.com/view/a9f61c09a417866fb94a8e52

(2)socket自带阻塞,文件传输完毕后,客户端的BufferedInputStream一直处在读的状态,参考资料:http://www.blogjava.net/sterning/archive/2007/10/13/152508.html

源代码分为四块,EchoServer是服务器主类,TraveralDir是服务器遍历文件时运行的进程,UserService是服务器监听到客户端请求后响应产生的进程,EchoClient是客户端主类。


package com.vince.xu;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {

	public static void main(String[] args) {
		try {
			// 启动服务器
			ServerSocket ss= new ServerSocket(8000);
			System.out.println("服务已经启动");
			// 启动线程,遍历服务器
			new Thread(new TraversalDir()).start();

			boolean flag = true;
			while(flag){
				System.out.println("等待连接中..........");
				Socket s = ss.accept(); // 阻塞
				// 检测到新连接,启动新线程
				new Thread(new UserService(s)).start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

package com.vince.xu;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public class TraversalDir implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		File file = new File("dir.txt");
		try {
			if (file.exists()){
				return;
			}else{
				PrintWriter pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
				File[] f = {new File("D:\\"),new File("E:\\"),new File("F:\\"),new File("G:\\"),new File("H:\\")};
				for (File file2 : f) {
					if (file2.exists()){
						traversalDir(file2, pw);
						System.out.println("-"+file2.getAbsolutePath()+"遍历结束-");
					}
				}
				System.out.println("-遍历完成-");
				pw.close();
			} 
		}catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 遍历服务器资源
	public static void traversalDir(File f, PrintWriter pw) throws IOException {
		if (f.isDirectory()) {
			File[] f1 = f.listFiles();
			if (f1 != null) {
				for (File file : f1) {
					traversalDir(file, pw);
				}
			}
		} else {
			// 遍历0.5GB大文件
			if(f.length()>=512*1024*1024){
				pw.println((f.getAbsolutePath()));
			}
			// System.out.println(f.getAbsolutePath());
		}
	}
}

package com.vince.xu;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

class UserService implements Runnable {
	// 客户端套接字
	private Socket s;
	
	public UserService(Socket s) {
		this.s = s;
	}

	@Override
	public void run() {
		System.out.println(s.getInetAddress().getHostAddress() + "已连接");
		try {
			// 输入字节流
			InputStream is = s.getInputStream();
			//BufferedInputStream bis = new BufferedInputStream(is);
			// 输入字符流
			BufferedReader br = new BufferedReader(new InputStreamReader(is));

			// 输出字节流
			OutputStream os = s.getOutputStream();
			BufferedOutputStream bos = new BufferedOutputStream(os);
			// 输出字节流
			PrintStream ps = new PrintStream(new BufferedOutputStream(os));
			
			// bool为false时,进程结束
			boolean bool = true;
			while (bool) {
				ps.println("请选择操作: 传输文件(T),Echo(E),结束连接(bye)");
				ps.flush();
				String info = br.readLine();
				if ("".equals(info) || "bye".equals(info)) {
					bool = false;
				} else if ("E".equals(info) || "e".equals(info)) {
					echo(br, ps, s);
				} else if ("T".equals(info) || "t".equals(info)) {
					transferFile(br, ps, bos, s);
				}
			}
			ps.close();
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static void transferFile(BufferedReader br, PrintStream ps,
			BufferedOutputStream bos, Socket s) throws IOException {
		// 检测客户端的dir文件
		DetectDir(br,  ps, bos, s);
		ps.println("请输入文件名: [返回上级(Q)]");
		ps.flush();
		// 从客户端读取一个文件名
		String string = br.readLine(); 
		if ("Q".equals(string) || "q".equals(string)) {
			return;
		} else {
			// 从dir.txt中读取文件所在位置
			BufferedReader bis1 = new BufferedReader(new FileReader(new File(
					"dir.txt")));// 文件输入流
			String filePath = null;
			String fileName = null;
			List<String> filePathFound = new ArrayList<String>();
			while ((filePath = bis1.readLine()) != null) {
				fileName = filePath.substring(filePath.lastIndexOf(File.separatorChar) + 1,filePath.length());
				if (string.equals(fileName)) {
					filePathFound.add(filePath);
				}
			}
			bis1.close(); // 关闭dir.txt的输入流

			ps.println(filePathFound.size());
			ps.flush();
			if (!filePathFound.isEmpty()) {
				// 开始文件传输
				BufferedInputStream brFile = null;
				File srcFile = null;
				String string2 = filePathFound.get(0);
				// Y为文件传输握手信号
				if ("Y".equals(br.readLine()) || "y".equals(br.readLine())) {
					srcFile = new File(string2);
					brFile = new BufferedInputStream(new FileInputStream(srcFile));
					byte[] bytes = new byte[1024 * 1024];
					int len = -1;
					while ((len = brFile.read(bytes, 0, bytes.length)) > 0) {
						bos.write(bytes, 0, len);
						bos.flush();
					}
					brFile.close();
				}
				// 单个文件传输结束
			} else {
				ps.println("没有找到文件");
				ps.flush();
			}
			s.close();
		}
	}

	private static void DetectDir(BufferedReader br, PrintStream ps,
			BufferedOutputStream bos, Socket s) throws IOException{
		ps.println(InetAddress.getLocalHost().getHostAddress()+"_"+InetAddress.getLocalHost().getHostName()+"_dir.txt");
		ps.flush();
		Boolean bool = Boolean.parseBoolean(br.readLine());
		// bool为true,客户端有dir文件
		if(bool){
			return;
		}else{
			BufferedInputStream brFile = null;
			File srcFile = null;
			if ("Y".equals(br.readLine()) || "y".equals(br.readLine())){
				srcFile = new File("dir.txt");
				brFile = new BufferedInputStream(new FileInputStream(srcFile));
				byte[] bytes = new byte[1024 * 1024];
				int len = -1;
				while ((len = brFile.read(bytes, 0, bytes.length)) > 0) {
					bos.write(bytes, 0, len);
					bos.flush();
				}
				brFile.close();
			}
			s.close();
		}
	}
	
	private static void echo(BufferedReader br, PrintStream ps,Socket s)
			throws IOException {
		boolean bool = true;
		ps.println("已进入echo,请输入:[返回上级(Q)]");
		ps.flush();
		while (bool) {
			String info = br.readLine();
			if ("Q".equals(info) || "q".equals(info)) {
				bool = false;
			} else {
				System.out.println("来自"+s.getInetAddress().getHostName()+"_"+s.getInetAddress().getHostAddress()+":"+info);
				ps.println("echo:" + info);
				ps.flush();
			}
		}
	}

}

package com.vince.xu;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class EchoClient {

	public static void main(String[] args) throws UnknownHostException,
			IOException {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		System.out.println("请输入服务器IP地址:");
		String serverIp = input.next();
		//System.out.println("请输入服务器端口号:"); // 由服务器指定
		// int port = input.nextInt();
		//Socket s = new Socket(serverIp, port);
		Socket s = new Socket(serverIp, 8000);
		System.out.println("与服务器连接成功");
		boolean flag = true;

		// 输入字节流
		InputStream is = s.getInputStream();
		BufferedInputStream bis = new BufferedInputStream(is);
		// 输入字符流
		BufferedReader br = new BufferedReader(new InputStreamReader(is));

		// 输出字节流
		OutputStream os = s.getOutputStream();
		//BufferedOutputStream bos = new BufferedOutputStream(os);
		// 输出字节流
		PrintStream ps = new PrintStream(new BufferedOutputStream(os));

		while (flag) {
			System.out.println("---" + br.readLine() + "---");
			String info = input.next();
			ps.println(info);
			ps.flush();
			if ("bye".equals(info)) {
				flag = false;
			} else if ("E".equals(info) || "e".equals(info)) {
				echo(br, ps);
			} else if ("T".equals(info) || "t".equals(info)) {
				transferFile(br, ps, bis,s);
			}
		}
		br.close();
		ps.close();
	}

	@SuppressWarnings("deprecation")
	private static void transferFile(BufferedReader br, PrintStream ps,
			BufferedInputStream bis,Socket s ) throws IOException {
		Scanner input = new Scanner(System.in);
		DetectDir(br, ps,bis, s);
			System.out.println("---" + br.readLine() + "---");
			String string = input.next();
			ps.println(string);
			ps.flush();
			if ("Q".equals(string) || "q".equals(string)) {
				return;
			} else {
				int size = Integer.parseInt(br.readLine());
				if (size != 0) {
					System.out.println("服务器端有" + size + "个文件名为" + string);
					BufferedOutputStream boFile = null;
					
						boFile = new BufferedOutputStream(new FileOutputStream(
								new File(string)));
						byte[] bytes = new byte[1024 * 1024];
						int len = -1;
						ps.println("Y");
						ps.flush();  // 发送握手信号,已经准备好了
						// 之前
						// while((len=br.read(bytes))!=-1){
						// boFile.write(bytes);
						// boFile.flush();
						// }
//						for (int j = 0; j < num; j++) {
//							len = bis.read(bytes, 0, bytes.length);
//							boFile.write(bytes, 0, len);
//							System.out.println("mark_-");
//							boFile.flush();
//						}
						// 后来
						while ((len = bis.read(bytes, 0, bytes.length)) > 0) {
							boFile.write(bytes, 0, len);
							//System.out.println("mark_-");
							boFile.flush();
						}
						
						boFile.close();
						System.out.println("第"+1+"个"+string+"传输完毕,共"+size+"个");
					}
			 else {
					System.out.println("---" + br.readLine() + "---");
				}
				s.close();
				Thread.currentThread().stop();
			}
	}
	
	@SuppressWarnings("deprecation")
	private static void DetectDir(BufferedReader br, PrintStream ps,
			BufferedInputStream bis, Socket s) throws IOException{
		String dirName = br.readLine();
		File dirFile = new File(dirName);
		ps.println(dirFile.exists());
		ps.flush();
		if(dirFile.exists()){
			return;
		}else{
			BufferedOutputStream boFile = new BufferedOutputStream(new FileOutputStream(
					dirFile));
			byte[] bytes = new byte[1024 * 1024];
			int len = -1;
			ps.println("Y");
			ps.flush();
			while ((len = bis.read(bytes, 0, bytes.length)) > 0) {
				boFile.write(bytes, 0, len);
				System.out.println("mark_-");
				boFile.flush();
			}
			
			boFile.close();
		}
		s.close();
		System.out.println("dir文件传输完毕,请重新连接");
		Thread.currentThread().stop();
	}


	private static void echo(BufferedReader br, PrintStream ps)
			throws IOException {
		boolean bool = true;
		Scanner input = new Scanner(System.in);
		System.out.println(br.readLine());
		while (bool) {
			String info = input.next();
			ps.println(info);
			ps.flush();
			if ("Q".equals(info) || "q".equals(info)) {
				bool = false;
			} else {
				System.out.println("---" + br.readLine() + "---");
			}
		}
	}
}

进一步改进可以使用非阻塞式方法,这样程序就不必通过断开连接保证数据完整,或者仍用阻塞式方法,文件传输后自动重新连接。还可以考虑传输文件夹,还可以可以侦听遍历进程终止,来允许传输文件的操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值