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

来源:互联网 发布:西门子em235编程 编辑:程序博客网 时间:2024/06/05 04:50

初学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 {@Overridepublic void run() {// TODO Auto-generated method stubFile 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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;}@Overridepublic 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 stubScanner 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 0