java多线程网络传输文件(非同步)-2008-08-17

利用 Socket 套接字进行面向连接通信的编程。客户端读取本地文件并发送;服务器接收文件并保存到本地文件系统中。
使用说明:请将TransferClient, TransferServer, TempFile三个类编译,他们的类包是FileServer.
客户端:
修改TransferClient: serPort, serIP, filePath, blockNum,的值来符合您机器的系统环境;或者用命令行
格式: java FileServer.TransferClient 文件路径+文件名 服务器IP地址 端口 线程数
C:\>java FileServer.TransferClient e:\GREbulletin.pdf 192.168.1.105 2008 5
服务端:
修改TransferServer: serPort, tempFolder的值来符合您机器的系统环境;或者用命令行
格式: java FileServer.TransferServer 端口 文件夹路径
C:\>java FileServer.TransferServer 2008 F:\tempFolder


文件传输服务端---------------------------------------------------------
package FileServer;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

/**
 * Description Socket文件传输服务端
 * 默认支持多线程传输,未支持断点续传,未支持内网UDF传输
 * Creation Date 07-08-2008 9:00
 * @author 卢俊宇
 * @version 1.0
 */
public class TransferServer {
 
 /**
  * @param args[0] 端口默认2008(例: 2008)
  * @param args[1] 接收文件存放路径,默认:和FileServer同一目录\ServerReceive
  *       (例:x:\example\example\)
  */
 public static void main(String[] args) {
  try {
   int serPort = 2008;//服务器默认端口2008
   String tempFolder = System.getProperty("user.dir")+"\\ServerReceive";//临时文件夹,用于存放接收文件
   ServerSocket ss = null;//设定Socket
   if (args.length<1 || null==args) {
    ss = new ServerSocket(serPort);
   }else {
    //检测输入端口是否正确
    if (args[0].replaceAll("\\D", "")!="") {
     serPort = Integer.parseInt(args[0].replaceAll("\\D", ""));
    }else {
     System.out.println("输入的端口有错误,使用系统默认端口");
    }
    ss = new ServerSocket(serPort);
    if (args[1].length()>0) {
     tempFolder = args[1];
    }
   }
   System.out.println("Welcome to TransferServer");
   System.out.println("服务器启动,监听端口" + ss.getLocalPort());
   System.out.println("服务器临时文件路径: "+tempFolder);
   while (true) {
    Socket s = ss.accept();
    new Thread(new ServerThread(s,tempFolder)).start();
   }

  } catch (IOException e) {
   e.getMessage();
   System.out.println("服务器端口被占用,或其他问题.");
  }
 }

}

/**
 * Description 服务器线程
 * @author 卢俊宇
 * @param tempFolder 接收文件存放路径,默认:TransferServer.class所在目录\ServerReceive
 *       (例:x:\example\example\)
 * @version 1.1
 */
class ServerThread implements Runnable {

 Socket s;// 实例化socket类
 private String tempFolder;//临时文件夹名
 public ServerThread(Socket s,String tempFolder) {
  this.s = s;
  this.tempFolder = tempFolder;
 }

 @SuppressWarnings("deprecation")
 public void run() {
  try {

   InputStream ins = s.getInputStream();
   OutputStream outs = s.getOutputStream();

   DataInputStream dis = new DataInputStream(ins);
   DataOutputStream dos = new DataOutputStream(outs);

   // 取得线程ID
   String SerID = "SerID-" + Thread.currentThread().getId();

   while (true) {
    try {

     String inStr = dis.readUTF();

     // 接收到的文件包含头信息
     if (inStr != null) {
      // 对收到的socket包过滤出文件信息
      if (inStr.contains("ClientInfo")) {
       System.out.println(SerID + " get FileInfo! ");
       // 文件名字
       String fName = new String(inStr.replaceAll(
         "(.+<FileName>)|(<\\/FileName>\\S+)", "")
         .toString().getBytes("utf-8"), "utf-8");
       // 文件总长度
       String fSize = new String(inStr.replaceAll(
         "(.+<FileLength>)|<\\/FileLength>", "")
         .toString().getBytes("utf-8"), "utf-8");
       System.out.println("size: " + fSize);
       long Size = Long.parseLong(fSize);
       // 区块起始长度
       String fPs = new String(
         inStr
           .replaceAll(
             "(.+<FilePointerStart>)|(</FilePointerStart>\\S+)",
             "").toString().getBytes(
             "utf-8"), "utf-8");
       System.out.println("PS: " + fPs);
       // 区块结束长度
       String fPe = new String(
         inStr
           .replaceAll(
             "(.+<FilePointerEnd>)|(</FilePointerEnd>\\S+)",
             "").toString().getBytes(
             "utf-8"), "utf-8");
       System.out.println("PE: " + fPe);
       long PointS = Long.parseLong(fPs); // 分块头
       long PointE = Long.parseLong(fPe); // 分块尾
       System.out.println("SourceFile Name :" + fName);
       File tempF = new File(tempFolder, fName);
       if (!tempF.exists()) {
        //检测目标文件夹是否存在
        if (new File(tempFolder).isDirectory()) {
         TempFile.creat(tempF, Size);// 如果临时文件不存在就建立
        }else {
         boolean creat = new File(tempFolder).mkdirs();
         if (creat) {
          TempFile.creat(tempF, Size);// 如果临时文件不存在就建立
         }else {
          System.out.println("Error:System can not creat folder!");
          System.out.println(SerID+" exits");
          dis.close();// 关闭包类
          dos.close();// 关闭输出流
          s.close();// 关连接
          Thread.currentThread().stop();
         }
        }
        
       }
       // 返回服务器准备好了
       String SerReady = SerID + " ServerReady=1";
       dos.writeUTF(SerReady);// 返回服务器信息
       dos.flush();

       // 取得客户端发送标志"SendStart"
       if (dis.readUTF().equals("SendStart")) {

        // 随机读写文件(适应多线程方式)
        RandomAccessFile fos = new RandomAccessFile(
          tempF.getPath(), "rw");

        long curPoint = PointS;// 文件指针起点
        long endSet = PointE;// 文件指针终点
        long nowSize = 0;// 已读/写字节
        byte[] buffer = new byte[1024]; // 建立缓冲区

        while (curPoint < endSet) {
         int length = 0;
         try {

          if ((endSet - curPoint + 1) < 1024) {
           fos.seek(curPoint);// 寻找写入位置
           byte[] bufferM = new byte[(int) (endSet
             - curPoint + 1)];// 调整缓冲区大小
           length = ins.read(bufferM);// 读取bufferM缓冲socket流

           try {
            fos.write(bufferM);// 将取得的socket流写入文件
            dos.writeUTF("SerGotIt");// 返回服务器信息
            dos.flush();
            curPoint += length;// 文件指针增加
            nowSize += length;
            fos.close();
            // 向发送接收结束标志
            dos.writeUTF("ReciveEnd");
            dos.flush();
            System.out.println(SerID
              + " is receive ok.");
            break;

           } catch (IOException e) {
            e.printStackTrace();
           }
          } else {
           fos.seek(curPoint);
           length = ins.read(buffer);
          }
         } catch (IOException e) {
          e.printStackTrace();
          System.out.println(SerID + " is abnormally closed.");
          break;
         }

         if (length == buffer.length) {
          // 将缓冲区字节写入文件
          try {
           System.out
             .println(SerID
               + " receive "
               + buffer.length
               + " bytes.");
           fos.write(buffer);
           dos.writeUTF("SerGotIt");// 返回服务器信息
           dos.flush();
           curPoint += buffer.length;// 指针+1024
           nowSize += buffer.length;
           System.out.println(SerID + " 指针位置 "
             + curPoint);
           System.out.println(SerID
             + "线程size写入进度:" + nowSize);
          } catch (IOException e) {
           e.printStackTrace();
          }
         }
        }
       }

      }
     }
     // 接收到客户端结束标志,跳出循环
     if (dis.readUTF().equals("SendEnd")) {
      System.out.println("服务器接收完毕");
      break;
     }

    } catch (SocketException s) {
     // s.printStackTrace();
     // 客户端非正常退出处理
     System.out.println(SerID + " is abnormally closed.");
     break;
    }
   }
   dis.close();// 关闭包类
   dos.close();// 关闭输出流
   s.close();// 关连接
   System.out.println(SerID + " exited.");
  } catch (IOException e) {

   e.printStackTrace();
  }

 }
}


文件传输客户端
-----------------------------------------------------------
package FileServer;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Description Socket文件传输客户端(默认支持多线程传输,未支持断点续传,未支持内网UDF传输)
 * Creation Date 07-08-2008 9:00
 * @author 卢俊宇
 * @version 1.0
 */
public class TransferClient {

 /**
  * Description 文件传输客户端
  *
  * @param args[0]
  *            文件路径+文件名(例:x:\example\example.class)
  * @param args[1]
  *            服务器IP地址(例: 192.168.1.1)
  * @param args[2]
  *            端口默认2008(例: 2008)
  * @param args[3]
  *            区块长度默认5 (例: 5)
  */
 public static void main(String[] args) {

  int serPort = 2008;// 默认连接端口2008;
  int blockNum = 5;// 文件区块数量(同时决定线程数)
  String serIP = "192.168.1.105";// 服务器IP
  String filePath = "e:\\1.rar";// 欲发送文件路径+文件名

  if (args.length<1 || null==args) {
   System.out.println("采用默认配置连接");
  } else {
   if (args[0].length()>0) {
    filePath = args[0];
   }
   
   if (args[1].length()>0) {
    serIP = args[1];
   }
   if (args[2].length()>0) {
    serPort = Integer.parseInt(args[2]);
   }
   if (args[3].length()>0) {
    blockNum = Integer.parseInt(args[3].replaceAll("\\D", ""));
   }
  }
  System.out.println("文件路径: "+filePath);
  System.out.println("连接IP: "+serIP);
  System.out.println("连接端口: "+serPort);
  System.out.println("线程数: "+blockNum);
  
  // 取得文件信息
  File source = new File(filePath);
  if (!source.exists()) {
   System.out.println("要传输的文件不存在!");
   System.exit(0);
  }

  long fSize = source.length(); // 文件长

  SimpleDateFormat sdf = new SimpleDateFormat("yyMMddhhmmss");
  String currentTime = sdf.format(new Date());

  String fName = currentTime + Math.round(100) + "-" + source.getName();// 按时间生成文件名
  System.out.println("源文件长度 " + fSize+" bytes.");

  // 区块信息
  long blockSize = fSize / blockNum; // 区块长度

  String[] fInfo = new String[blockNum];// 按区块生产文件信息组

  long offset = 0;// 区块起点位置
  long endset = 0;// 区块终点位置
  String sourceFilePath = source.getPath();// 取源文件路径

  // 打包区块信息
  for (int i = 0; i < blockNum; i++) {

   offset = ((long) i) * blockSize;// 设置文件指针起点
   endset = (((long) i + 1) * blockSize - 1 + (i == blockNum - 1
     ? fSize % blockNum
     : 0));// 设置文件指针终点

   fInfo[i] = "ClientInfo<FileName>" + fName + "</FileName>";
   fInfo[i] += "<FilePointerStart>" + offset + "</FilePointerStart>";
   fInfo[i] += "<FilePointerEnd>" + endset + "</FilePointerEnd>";
   fInfo[i] += "<FileLength>" + fSize + "</FileLength>";

   // 按区块开启线程
   new Thread(new FileTransThread(fInfo[i], sourceFilePath, offset, endset,
     serIP, serPort)).start();
  }

 }

}

/**
 * Description 文件传输线程
 *
 * @author 卢俊宇
 * @param info;
 *            初始化时打包的文件信息
 * @param sourceFilePath
 *            文件路径+文件名(例:x:\example\example.class)
 * @param offSet
 *            文件指针起点值地址(按文件区块数量划分)
 * @param endSet
 *            文件指针终点值地址(按文件区块数量划分)
 * @param serPort
 *            默认连接端口2008;
 * @param serIP
 *            连接IP
 * @version 1.1
 */
class FileTransThread implements Runnable {
 private String info;// 文件信息
 private String sourceFilePath;// 源文件路径

 private long offSet;// 这个线程写入文件起始值地址
 private long endSet;// 这个线程写入的文件长度

 private int serPort;// 默认连接端口2008;
 private String serIP;// 连接IP

 FileTransThread(String info, String sourceFilePath, long offSet, long endSet,
   String serIP, int serPort) {
  this.info = info;
  this.sourceFilePath = sourceFilePath;
  this.offSet = offSet;
  this.endSet = endSet;
  this.serIP = serIP;
  this.serPort = serPort;
 }

 public void run() {
  try {
   String CliID = "ClientID-" + Thread.currentThread().getId();
   System.out.println(CliID + " connect " + serIP);
   Socket s = new Socket(InetAddress.getByName(serIP), serPort);
   System.out.println(CliID + " successfully connected.");

   InputStream clientIn = s.getInputStream();
   OutputStream clientOut = s.getOutputStream();

   DataInputStream dis = new DataInputStream(clientIn);

   DataOutputStream dos = new DataOutputStream(clientOut);

   dos.writeUTF(info);// 发送文件信息到服务器
   dos.flush();
   System.out.println(CliID + " send FileInfo!");

   while (true) {

    String inStr = dis.readUTF();
    // 判断服务器是否准备接收
    if (inStr.contains("ServerReady=1")) {
     System.out.println(CliID + " report: " + inStr);
     // 开始准备文件传输

     dos.writeUTF("SendStart");// 发送接收标志
     dos.flush();

     RandomAccessFile fos = new RandomAccessFile(sourceFilePath,
       "r");

     long curPoint = offSet;

     long nowSize = 0;
     byte[] buffer = new byte[1024];

     while (curPoint < endSet) { // 建立缓冲区
      int length = 0;
      try {

       if ((endSet - curPoint + 1) < 1024) {
        fos.seek(curPoint);
        byte[] bufferM = new byte[(int) (endSet
          - curPoint + 1)];// 调整缓冲区大小
        length = fos.read(bufferM);

        try {
         clientOut.write(bufferM);
         clientOut.flush();
         //等待服务器确认
         for(;;){
          if (dis.readUTF().equals("SerGotIt")) {
           break;
          }
         }
         curPoint += length;
         nowSize += length;
         fos.close();
         System.out.println(CliID + " is send ok.");
         break;

        } catch (IOException e) {
         e.printStackTrace();
         System.out.println(CliID + " is abnormally closed.");
         break;

        }
       } else {
        fos.seek(curPoint);
        length = fos.read(buffer);
       }
      } catch (IOException e) {
       e.printStackTrace();

      }

      if (length == buffer.length) {
       // 将缓冲区字节写入文件
       try {
        System.out.println(CliID + " send "
          + buffer.length + " bytes.");
        clientOut.write(buffer);
        clientOut.flush();
        //等待服务器确认
        for(;;){
         if (dis.readUTF().equals("SerGotIt")) {
          break;
         }
        }
        curPoint += buffer.length;// 指针+1024
        nowSize += buffer.length;
        System.out.println(CliID + " 指针位置 " + curPoint);
        System.out.println(CliID + "线程size读入进度:"
          + nowSize);
       } catch (IOException e) {
        e.printStackTrace();
        System.out.println(CliID + " is abnormally closed.");
        break;

       }
      }
     }
    }
    // 向服务端发送结束标志,跳出循环
    if (dis.readUTF().equals("ReciveEnd")) {
     dos.writeUTF("SendEnd");
     dos.flush();
     System.out.println("传输完毕");
     break;
    }
   }
   dis.close();// 关闭包类
   dos.close();// 关闭输出流
   s.close();// 关连接
  } catch (UnknownHostException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

 


生成临时文件
package FileServer;

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

/**
 * Description 生成临时文件
 * Creation Date 07-08-2008 9:00
 * @author 卢俊宇
 * @version 1.0
 */
public class TempFile {
 /**
  * 创建文件
  * @param targetFile 文件对象
  * @param fileLength 文件字节长度
  */
 public static void creat(File targetFile, long fileLength) {
  long now = System.currentTimeMillis();
  long length = fileLength;//指定写入文件文件大小
  byte[] buffer = new byte[1024];// 缓冲区1024 bytes
  FileOutputStream fos;
  try {
   fos = new FileOutputStream(targetFile);
   while (true) { // 建立缓冲区
    if (length > 1024) {
     // 将缓冲区字节写入文件
     try {
      fos.write(buffer);// 写入缓冲
      length = length - 1024;
     } catch (IOException e) {
      e.printStackTrace();
     }
    } else {
     byte[] buf = new byte[(int) length];
     System.arraycopy(buffer, 0, buf, 0, (int) length);
     try {
      fos.write(buf);
      fos.close();
     } catch (IOException e) {
      e.printStackTrace();
     }
     break;
    }
   }
   System.out.println("写入临时文件" + targetFile.getName() + ":"
     + targetFile.length() + " bytes");

  } catch (FileNotFoundException e1) {
   e1.printStackTrace();
  }
  long end = System.currentTimeMillis();
  System.out.println("临时文件" + targetFile.getName() + "写入耗时:"
    + (end - now) + " ms");
 }
}


-文件传输包含3个类---------------------------------------------------
效果:我已经测试过该程序了,能成功运行!可以多线程


    FileServer.rar (5.9 KB)
    描述: java源文件
    下载链接: http://dl.iteye.com/topics/download/f28c97a6-91c2-391a-ad95-8fba65a2e088

你可能感兴趣的:(java,多线程,socket)