为了加强对socket及java IO流编程的回顾,本文作了一个文件传输的实例。针对java不同对IO流的不同实现,本文也分别基于BIO、NIO、AIO实现三种传输,同时本文采用了缓冲输入/输出流来包装输出流,再采用数据输入/输出输出流包装缓冲流,加快传输的速度。最后本文基于大文件对三种方式基本传输效率对比。对于每一种传输方式分别实现服务端和客户端,并通过服务端发送文件到客户端,(同理可以实现客户端到服务器端的文件发送)

1、基于BIO的文件发送实现

服务端:BIOServer.java

package zmx.socket.filethransfer.bio;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 本例实现基于BIO的Socket文件传输,客户端连接服务器后,服务器发送一个文件到客户端,
* 客户端接受文件,保存到本地。(同理可实现客户端向服务器的文件发送)
*
* 本类是服务端:实现接受连接和发送文件
* @author zhangwenchao
*/
public class BIOServer {
public static final int port=8821;
public static final int bufferSize = 8192;
public void serverStart(){
Socket s =null; //连接
try {
ServerSocket ss = new ServerSocket(port);//初始化ServerSocket
File f = new File("E:\\1.jpg");
System.out.println("要发送的文件为:"+f.getName()+" 文件大小:"+f.length());
while(true){//不断循环接受客户端请求
System.out.println("服务器正在监听端口:"+port+"...");
// public Socket accept() throws IOException
// 侦听并接受到此套接字的连接。此方法在进行连接之前一直阻塞。
s = ss.accept(); //建立连接
System.out.println("Socket连接已经建立成功!");
//Socket输入流: 读取客户端发送过来的数据
DataInputStream dis = new DataInputStream(new BufferedInputStream(s.getInputStream()));
byte message = dis.readByte();
System.out.println(message);
//文件输入流:读取文件
DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
//Socket输出流: 发送文件
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
//将文件名及长度传给客户端。这里要真正适用所有平台,例如中文名的处理,还需要加工,具体可以参见Think In Java 4th里有现成的代码。
dos.writeUTF(f.getName());
dos.flush();
dos.writeLong((long) f.length());
dos.flush();
byte[] buf = new byte[bufferSize];
int len = 0;
while((len=fis.read(buf))!=-1){
dos.write(buf, 0, len);
}
dos.flush();
// 注意关闭socket链接哦,不然客户端会等待server的数据过来,
// 直到socket超时,导致数据不完整。
fis.close();
s.close();
System.out.println("文件传输完成");
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("网络连接出现异常!");
}
}
public static void main(String arg[]) {
new BIOServer().serverStart();
}
}

客户端:BIOSocketUti.java+BIOClient.java

BIOSocketUtil.java
package zmx.socket.filethransfer.bio;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
/**
* Socket连接工具
* @author zhangwenchao
*
*/
public class BIOSocketUtil{
/**
* 创建socket连接
* @throws Exception
* exception
*/
public static Socket CreateConnection(String ip, int port) throws Exception {
try {
Socket socket = new Socket(ip, port);
return socket;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
}
}
/**
* 发送一条信息,通知服务器本机的操作系统类型
* @param sendMessage 0x1:windows 0x2:unix 0x3:Linux
* @throws Exception
*/
public static void sendMessage(Socket socket,String sendMessage) throws Exception {
DataOutputStream outputStream = null;
try {
outputStream = new DataOutputStream(socket.getOutputStream());
if (sendMessage.equals("Windows")) {
outputStream.writeByte(0x1);
outputStream.flush();
return;
}
if (sendMessage.equals("Unix")) {
outputStream.writeByte(0x2);
outputStream.flush();
return;
}
if (sendMessage.equals("Linux")) {
outputStream.writeByte(0x3);
outputStream.flush();
} else {
outputStream.writeUTF(sendMessage);
outputStream.flush();
}
} catch (Exception e) {
System.out.println("发送消息错误" + "\n");
e.printStackTrace();
if (outputStream != null)
outputStream.close();
throw e;
} finally {
}
}
/**
* 获取输入流
* @return
* @throws Exception
*/
public static DataInputStream getMessageStream(Socket socket) throws Exception {
try {
DataInputStream inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
return inputStream;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
}
}
/**
* 关闭连接
*/
public static void shutDownConnection(Socket socket) {
try {
if (socket != null){
socket.close();
}
} catch (Exception e) {
}
}
}
BIOClient.java
package zmx.socket.filethransfer.bio;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.net.Socket;
/**
* 客户端:发送一条消息 通知服务器本地操作系统类型,然后接受服务器发送的文件
* @author zhangwenchao
*
*/
public class BIOClient {
public static final int bufferSize = 8192;
private static void getFileMessage(Socket socket) {
if (socket == null)
return;
DataInputStream inputStream = null;
try {
inputStream = BIOSocketUtil.getMessageStream(socket); //根据socket获取输入流
} catch (Exception e) {
System.out.print("接收消息缓存错误\n");
return;
}
try {
//本地保存路径,文件名会自动从服务器端继承而来。
String savePath = "E:\\local\\";
byte[] buf = new byte[bufferSize];
int passedlen = 0; //统计收到的数据
//1、读取文件名
savePath += inputStream.readUTF();
//根据文件名新建一个文件输出流
DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(savePath)));
//2、读取文件长度
long fileLength = inputStream.readLong();
System.out.println("文件的长度为:" + fileLength + "\n");
System.out.println("开始接收文件..." + "\n");
//3、读取文件内容
int len = 0;
while((len=inputStream.read(buf))!=-1){
passedlen += len;
//下面进度条本为图形界面的prograssBar做的,这里如果是打文件,可能会重复打印出一些相同的百分比
System.out.println("文件接收了" + (passedlen * 100/ fileLength) + "%\n");
fileOut.write(buf, 0, len);
}
System.out.println("接收完成,文件存为" + savePath + "\n");
fileOut.close();
} catch (Exception e) {
System.out.println("接收消息错误" + "\n");
return;
}
}
public static void main(String arg[]) {
String ip = "localhost";// 设置成服务器IP
int port = 8821;
String sendMessage = "Windwos";
try {
Socket socket = BIOSocketUtil.CreateConnection(ip, port);
System.out.print("连接服务器成功!" + "\n");
BIOSocketUtil.sendMessage(socket, sendMessage); //发送数据
getFileMessage(socket); //获取数据
} catch (Exception e) {
System.out.print("连接服务器失败!" + "\n");
}
}
}

运行测试结果:

要发送的文件为:1.jpg 文件大小:917070 服务器正在监听端口:8821... Socket连接已经建立成功! 0 文件传输完成!