初学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() + "---");
}
}
}
}
进一步改进可以使用非阻塞式方法,这样程序就不必通过断开连接保证数据完整,或者仍用阻塞式方法,文件传输后自动重新连接。还可以考虑传输文件夹,还可以可以侦听遍历进程终止,来允许传输文件的操作。