上个礼拜,测试一个东西,就是单线程和多线程客户端与一个socket服务器连接,传输数据,哪一个更快。然后我写了一个程序,如下:
服务器代码
public class SocketServer {
private int port;
private ServerSocket serverSocket;
private Date date;
private static long startDate = 0;
public SocketServer (int port){
this.port = port ;
startSend();
}
public static void receiveFile(Socket socket) throws IOException
{
byte[] inputByte = null;
int length = 0;
DataInputStream dis = null;
FileOutputStream fos = null;
DataOutputStream dos = null;
String filePath = "D:/temp/"+"SJ"+new
Random().nextInt(10000)+".docx";
try {
try {
dis = new DataInputStream(socket.getInputStream());
//dos = new DataOutputStream(socket.getOutputStream());
File f = new File("D:/temp");
if(!f.exists()){
f.mkdir();
}
fos = new FileOutputStream(new
File(filePath));
inputByte = new
byte[1024];
System.out.println("开始接收数据..."+(new Date().getTime() - startDate));
while ((length = dis.read(inputByte, 0, inputByte.length)) > 0)
{
fos.write(inputByte, 0, length);
fos.flush();
}
//发送回执
dos.write(inputByte);
System.out.println("完成接收:"+filePath);
} finally {
if (fos != null)
fos.close();
if (dis != null)
dis.close();
if (socket != null)
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void startSend(){
int mark =0;
try {
serverSocket = new ServerSocket(port);
System.out.println("服务器已启动,端口号为:"+port+".");
System.out.println("正在等待客户端连接......");
while (true) {
try
{
System.out.println("开始监听...");
Socket socket = serverSocket.accept();
System.out.println("有链接");
if(mark==0)
{
startDate = new Date().getTime();
mark =1;
}
receiveFile(socket);
long time = new Date().getTime() - startDate;
System.out.println("总时间:"+time);
} catch (Exception e) {
System.out.println("服务器异常");
e.printStackTrace();
}
}
//socketAccept.close();
} catch (Exception e)
{
}
}
}
客户端代码:
public void connectSocketFileTrans() throws
IOException{
int length = 0;
double sumL = 0 ;
int mark = 0;
byte[] sendBytes = null;
byte[] recvBytes = null;
Socket socket = null;
DataInputStream dis = null;
DataOutputStream dos = null;
FileInputStream fis = null;
boolean bool = false;
long startTime =0;
long endTime = 0;
long l = 1;
try {
File file = new File(fileName); //要传输的文件路径
l = file.length();
if(host.equals("localhost")||host.equals("127.0.0.1")){
socket = new Socket(InetAddress.getLocalHost(),port);
}else {
socket = new Socket(InetAddress.getByName(host),port);
}
System.out.println(fileName);
startTime = new Date().getTime();//开始设置管道,以及读写数据
dos = new DataOutputStream(socket.getOutputStream());
dis = new DataInputStream(socket.getInputStream());
fis = new
FileInputStream(file);
sendBytes = new byte[1024];
recvBytes = new byte[1024];
while ((length = fis.read(sendBytes, 0, sendBytes.length)) > 0)
{
dos.write(sendBytes, 0, length);
dos.flush();
//dis.read(recvBytes, 0, recvBytes.length);
//if(Arrays.equals(recvBytes, sendBytes))
{
sumL += length;
if(mark == 0)
{
System.out.println(fileName+"已传输:"+((sumL/l)*100)+"%");
mark = 1;
}
System.out.println(fileName+"已传输:"+((sumL/l)*100)+"%");
}
//else
{
//System.out.println("传输错误");
}
}
if(sumL==l){
bool =
true;
//结束管道的读写数据
endTime = new Date().getTime();
this.timeRun = endTime - startTime;
System.out.println(fileName+"已传输:"+((sumL/l)*100)+"%");
}
}catch (Exception e) {
System.out.println("客户端文件传输异常");
bool = false;
e.printStackTrace();
} finally{
if (dos != null)
dos.close();
if (fis != null)
{
fis.close();
dis.read(recvBytes, 0, recvBytes.length);
if(Arrays.equals(recvBytes, sendBytes))
{
sumL += length;
if(mark == 0)
{
System.out.println(fileName+"已传输:"+((sumL/l)*100)+"%");
mark = 1;
}
System.out.println(fileName+"已传输:"+((sumL/l)*100)+"%");
}
else
{
System.out.println("传输错误");
}
dis.close();
}
if (socket != null)
socket.close();
}
System.out.println(bool?"成功":"失败");
}
上面都是节选的下面所用到的一部分。
第一次测试的时候,使用最终回执,即只是在一个文件传送结束后使用回执。但是我的计算运行时间的函数写在了服务器端,当服务器开始接收一个文件的时候,记录开始时间,完全就接收到所有文件的时候,记录结束时间。这时候,在本地测试的时候,多线程竟然比单线程要快一些。
然后,我写了带有回执的第二次测试代码,并且在客户端记录时间,当开始执行第一次发送文件之前,记录开始时间,当所有线程或者所有类都发送结束后,记录结束时间。这时候,在本地测试的时候,单线程就比多线程快了。
后来,我做文件io测试的时候,在多次加载文件的情况下,多线程要比单线程快一些,我就误以为是第一个测试是因为文件io的原因,才使得多线程要比单线程快,但是后来我在调试的时候,发现情况并不只是这样。
在第一个测试背景下,当我服务器开启调试的时候,等待客户端连接,客户端多线程执行,向服务器发送连接请求,将文件写入数据输出管道,即当服务器接收到连接请求,并开始记录时间的时候,客户端已经完成很大一部分数据写入管道的操作了,大概每个管道可以写入的数据为190k左右,然后管道排满之后停止写入,也就阻塞在写方法上了,此时要等待所有数据发完之后才可以继续执行。而单线程的慢的原因在于客户端在服务器开始记录时间之前,只完成了第一个文件的预读,而没有做剩余文件的预读。所以,单线程要比多线程要慢,所以这个测试是错误的。
这个错误的原因来自于,我认为客户端的连接是阻塞在构造方法上,或者是写入数据方法上的,然而并不是这样子。实际的情况是这样,服务器开启后,阻塞在等待接收连接请求,而客户端发送连接请求,服务器接收到很多连接请求,对这些连接请求生成一个对列,然后处理第一个连接请求。而客户端方面,发送完连接请求之后,继续执行,将数据写入管道,因为没有回执,所以数据会一直写入管道,直到管道被填满,然后写方法进入阻塞状态。而此时,可能服务器端还没有计时。当服务器开始正式接收连接请求之后,客户端某线程进入连接状态,在管道中的数据会直接发送过去,然后该线程的管道写方法跳出阻塞状态开始执行,继续向管道中的写入剩余数据,直到所有数据传送完成,然后线程结束。服务器接收连接队列的下一条请求,然后重复执行上面步骤。所以第一次的计算时间的时候,多线程比单线程要少很多的文件io的时间。
所以,在服务器端是单线程的情况下,单线程客户端要比多线程快一些。