ServerSocket/Datagram中的文件传输(转贴+原创)

一。socket文件传输

1。局域网文件传输和复制,含文件名(本节转自WW。CSDN。NET文章:(因为了分析比较))
 

实施局域网内文件复制,包括需要把文件名也拷贝过去。特点是文件名传输和文件传输动用了两个端口。进程完成这样的功能:每隔24小时进

行上传一次,保持两台机器的根目录下文件一致。

服务器端,也既待上传端。运行命令:java FileServer 3107
import java.io.*;
import java.net.*;

public class FileServer implements Runnable {
      
       private static int ipsocket = 0;
      
       public static void main(String[] args)throws Exception{
              ipsocket = Integer.parseInt(args[0]);
              //建议使用3107
              System.out.println("系统在端口:" + ipsocket + "等待发送");
              Thread t1 = new Thread(new FileServer());
              t1.start();
       }
      
       public void run(){
              while(true){
               try{
                String[] fileNames = seachFileName(1);
                if(fileNames == null || fileNames.length < 1) return;
                for(int i = 0; i < fileNames.length; i ++){
                      sendFileName(fileNames[i]);
                      sendFile(fileNames[i]);
                }       
                       Thread.sleep(24 * 60 * 60 * 1000); //24小时
        }catch(Exception e) {
         System.out.println(e);
        }
       }
       }
      
 /*
 listType = 0 返回含文件夹和文件
 listType = 1 只返回文件
 */
       public static String[] seachFileName(int listType) throws Exception{
              String directory = ".";
              File dir = new File(directory);
           File dirTemp;
           String[] filesTemp;
           String[] fileNames;
           String[] re;
           int length = 0;
           if (!dir.isDirectory()) 
             throw new IllegalArgumentException("FileList: no such directory");

           filesTemp = dir.list();        
              java.util.Arrays.sort(filesTemp);
    
              for(int i = 0; i < filesTemp.length; i++) {
                    dirTemp = new File(directory + "//" + filesTemp[i]);
                    if(!dirTemp.isDirectory()){
                            length++;
                    }
              }    
              fileNames = new String[length];  
              length = 0;           
              for(int i = 0; i < filesTemp.length; i++) {
                    dirTemp = new File(directory + "//" + filesTemp[i]);
                    if(!dirTemp.isDirectory()){
                         fileNames[length] = filesTemp[i];   
                    }
                    length++;//************这里我认为是应该放进if(){}中。***************
              }
              if(listType == 0) re = filesTemp;
            else re = fileNames;
            return re;

        }
       
        /*发文件名
 */
        private static void sendFileName(String fileNames) throws Exception{
              if(fileNames == null) return;
       //创建网络服务器接受客户请求
              ServerSocket ss=new ServerSocket(ipsocket);
              Socket client=ss.accept();
             
              //创建网络输出流并提供数据包装器
              OutputStream netOut=client.getOutputStream();
              OutputStream doc=new DataOutputStream(new BufferedOutputStream(netOut));
             
              //创建文件读取缓冲区
              byte[] buf=null;
              String fileName = fileNames;
              buf = fileName.getBytes();
              int num=buf.length;
              if(num > 0){//是否读完文件
                    doc.write(buf,0,num);//把文件数据写出网络缓冲区
                     doc.flush();//刷新缓冲区把数据写往客户端
              }
              doc.close();
              ss.close();
 }
       /*发文件本身
        */
        private static void sendFile(String fileName) throws Exception{
              if(fileName == null) return;
       //创建文件流用来读取文件中的数据
              File file=new File(fileName);
              FileInputStream fos=new FileInputStream(file);
             
              //创建网络服务器接受客户请求
              ServerSocket ss=new ServerSocket(ipsocket + 1);
              Socket client=ss.accept();
             
              //创建网络输出流并提供数据包装器
              OutputStream netOut=client.getOutputStream();
              OutputStream doc=new DataOutputStream(new BufferedOutputStream(netOut));
             
              //创建文件读取缓冲区
              byte[] buf=new byte[2048];
              int num=fos.read(buf);
              while(num!=(-1)){//是否读完文件
                     doc.write(buf,0,num);//把文件数据写出网络缓冲区
                     doc.flush();//刷新缓冲区把数据写往客户端
                     num=fos.read(buf);//继续从文件中读取数据
              }
              fos.close();
              doc.close();
              ss.close();
         
        }
}

2:客户端,也既文件接受端。运行命令:java FileClient 127.0.0.1 3107。注意跟服务器端口要一致。
import java.io.*;
import java.net.*;
 
public class FileClient implements Runnable {
      
       private static String ip = "";
       private static int ipsocket = 0;
      
       public static void main(String[] args)throws Exception{
              ip = args[0];
              ipsocket = Integer.parseInt(args[1]);
              //建议使用3107
              System.out.println("系统在地址@" + ip + ":" + ipsocket + "倾听");
              Thread t1 = new Thread(new FileClient());
              t1.start();
       }
             
       public void run(){
              while(true){
               try{
                String strTemp = getFileName();
                getFile(strTemp);
               }catch(Exception e){}
               try{
                Thread.sleep(5 * 1000); //5秒               //****就是每隔5S轮询一次是否有文件要上传咯,这里有二个作用:1。作为服务器某一时间段里围轮询  2。作为经24H后的一次轮询*********过
                                                                       
        }catch(Exception e) {
         System.out.println(e);
        }       
       }
       }
      
       private static String getFileName() throws Exception{
              // 通过Socket连接文件服务器
              Socket server=new Socket(ip,ipsocket);             

              //创建网络接受流接受服务器文件数据
              InputStream netIn=server.getInputStream();
              InputStream in=new DataInputStream(new BufferedInputStream(netIn));             

              //创建缓冲区缓冲网络数据
              byte[] buf=new byte[2048];
              int num=in.read(buf);
             
              while(num!=(-1)){//是否读完所有数据                      /********这里觉得不是很恰当,可以不用WHILE来读一个文件名吧,本身服务器也是发了一次数据,而且你这样不是每次都覆盖了上次的内容吗?开始看时我也觉得好像自己被蒙蔽了。。。******************
                     num=in.read(buf);//继续从网络中读取文件
              }
              String fileName = new String(buf);
              fileName = fileName.trim();
              in.close();
              server.close();
              return fileName;             
       }
      
       private static void getFile(String strTemp) throws Exception{
              //使用本地文件系统接受网络数据并存为新文件
              File file=new File(strTemp);
             
              //如果文件已经存在,先删除
              if(file.exists()) file.delete();
              for(int i = 0 ; i <  10000; i++) {}
              file.createNewFile();
              RandomAccessFile raf=new RandomAccessFile(file,"rw");             

              // 通过Socket连接文件服务器
              Socket server=new Socket(ip,(ipsocket + 1) );             

              //创建网络接受流接受服务器文件数据
              InputStream netIn=server.getInputStream();
              InputStream in=new DataInputStream(new BufferedInputStream(netIn));             

              //创建缓冲区缓冲网络数据
              byte[] buf=new byte[2048];
              int num=in.read(buf);
             
              while(num!=(-1)){//是否读完所有数据
                     raf.write(buf,0,num);//将数据写往文件
                     raf.skipBytes(num);//顺序写文件字节//***********这里应该是错的,raf本身可以自动移动指针,为什么要SKIP?***********
                     num=in.read(buf);//继续从网络中读取文件
              }
              in.close();
              raf.close();             
              server.close();
       }
}

注:带///********xxxxxxxxxxxxxxxx*******是本人注释。

评论:该文使用了两个端口来实现文件名与文件的传送,方法不错,我以前用DatagramSocket作的文件传输是用一个port的,当然这样要先送一些约定信息,详细见下文。

2。自创。在网上找了很久也没相关的datagramsocket传输文件的文章,有的多是普通传输普通信息,于是我就自己做了一个传输文件的,当然这个例子中有些BUG,有等重构

import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.SocketException;
import java.net.InetAddress;
import java.io.File;
import java.io.*;
import java.util.HashMap;
import java.util.Collection;
import java.util.Arrays;

//为了简化,假定这个文件为2-9K之间,如果大于此可能会有异常,但可以修改代码改善
public class Server {
 private static DatagramSocket socket ;
 private static byte[] buff = new byte[1024];
 private static int packetNum;
 private static int packetTotal;
 private static final int INPORT = 1700;
 
 private static HashMap packets = new HashMap();//use for repackage.
 private static Integer packetOrder;           
 private static RandomAccessFile bufferFile;
 private static File resultFile;
 private static InetAddress add;
 private static DatagramPacket recePacket;
 private static DatagramPacket sendPacket;
 public static final String path = "d://server//dest";
 
 private static File tempFile;
 private static File tempDir;
 
 private static long startApp;
 private static long interval = 5000;//by default
 private static int resendTime = 0 ;
 private static boolean starting = true;
 
 public Server(){
  tempDir = new File("d://server//temp");
 }
 
 //create a new file only**
 public void createFile(String fileName){
  if(fileName == null){
   System.out.println("Error: the file name send by client is unvalidity!");
   return ;
  }
  resultFile = new File(path,fileName);  
  try{
   //if the file has existed,delete it first
   if(!resultFile.createNewFile()){
    resultFile.delete();//delete the original file.
   }
   resultFile.createNewFile();
   System.out.println("now a new file send by client is being created on the D://server//dest//"+resultFile.getName()+"...");   
  }
  catch(IOException ioe){
   System.out.println("Oops!receive file failed.");
  }   
 }
 
 //为了简化,假定只有一个包未如期接收
 private static int checkPacket(){
  Collection coll = packets.values();
  File[] result = (File[])coll.toArray(new File[0]);
  Arrays.sort(result);
  
  for(int i=101;i<=packetTotal;i++){
   String index = result[i].getName();
   index = index.substring(0,3);
   int whichOne = Integer.parseInt(index);
   if(i != whichOne)
    return i; //MUST be i instead of index!     
  }  
        //0 will be return if normall send(but here will not,here is as a request of method)
  return 0;
 }
 
 public static void main(String[] args) {
  // TODO Auto-generated method stu
  System.out.println("server has started");
  Server server = new Server();
  //initialize the scheme.
  try{
   server.socket = new DatagramSocket(INPORT);  
  }
  catch(SocketException se){
   se.printStackTrace();
  }
  server.recePacket = new DatagramPacket(server.buff,server.buff.length);  
  //now receive the indentify infomation(file name,the amount of packet...etc)
        //filename contains bytes and the amout of the file is denoted by client.
  //***the file and packet VIP preformatted information.
  //------------new file data format.
  //buff[0]= 'n' means a new file else means file's data; ****
  //buff[1] = filename lenght;starts buff[2] (if any) is the filename data.
  //buff[100] = number of the packet;
  //------------packet data format.
  //buff[0] means the packet index or the 'r'(error) or 'o'(over) flag .
  int index = 100;
  //here use a label(like the goto statement in C)***********
  String fname = null;
  resend:
  while(true){
   try{
    server.socket.receive(server.recePacket);
    if(starting){
     startApp = System.currentTimeMillis();
     System.out.println(startApp);
     starting = false;//only use one time
    }
    
    byte[] data = new byte[1024];
    data = server.recePacket.getData();
               
    //为了简化,默认为当发送过来的第一个字节为‘n'时,就是一个新文件。
    //(对应client端,当Server发送一个'r'时,要求重发送指定包。
    if(data[0] == 'n'){     
     fname = new String(data,2,buff[1]);
     server.createFile(fname);
     packetTotal = data[100];//假定文件个数为(基数100+实际文件数目) 
     System.out.println("packet total: "+packetTotal);
    }    
    else{
     //默认从101开始编号临时文件
     index++;//count for the amout of the packets.
     //只要有发送过来都是默认为正常要接收的个包
     //以下是作为一个文件的建立,关闭操作。     
     tempFile = new File(tempDir,index+".tmp");
     tempFile.createNewFile();
     packetOrder = new Integer(index);
     packets.put(packetOrder,tempFile);
     bufferFile = new RandomAccessFile(tempFile,"rw");
     bufferFile.write(data);     
     bufferFile.close();
     //为了简化,只要发送次数等于包数就默认为完毕(可以正常组装)
     if(index == packetTotal){
      Collection values = packets.values();
      //int cnt = values.size();      
      File[] valInt = (File[])(values.toArray(new File[0]));
      Arrays.sort(valInt);
      
      bufferFile = new RandomAccessFile(resultFile,"rw");
      for(int i=0;i<valInt.length;i++){
       tempFile = valInt[i];
       RandomAccessFile transFile = new RandomAccessFile(tempFile,"rw");
       buff = new byte[1024];//clear data privor to save
       transFile.read(buff);
       bufferFile.write(buff);
                            //需要删除刚才建立的临时文件
       if(tempFile.delete()){        
        System.out.println("temperory file: "+tempFile+" has deleted!");
        System.out.println(tempFile.isFile());
       }
       else
        System.out.println("can not delete temperory file: "+tempFile);
       transFile.close();       
      }
      packets.clear();
      bufferFile.close();
      System.out.println("d://server//dest//"+fname+" file successfully received!");
      //回传一个'o'(over)标志完毕
      server.resend((byte)'o');
      break;
     }
     //假定5s里可以将文件传送完毕;
     //如果5s内未完全传送完毕,则要求每隔2s发送一个重发包命令,连续五次,
     //如果包还是未到达,那么接收文件失败并删除它
     else{ 
      long period = System.currentTimeMillis() - startApp;
      System.out.println(period);
      if( period > interval ){
       System.out.println("resend time");
       if(resendTime < 5 ){
        
        resendTime++;
        startApp = System.currentTimeMillis();
        interval = 2000;//convert to use resend
        byte miss = (byte)checkPacket();
        //回传要重发的包(注意0值不可能到达,因为上面if(index == packetTotal)会优先检查
        //所以这时没有必要做0的情况
        if(miss != 0){
         server.resend(miss);
         System.out.println("miss a package: waiting for it resended by client...");
         continue resend;//retry
        }
       }
       else{
        System.out.println("Oops!receive file failed.");
        server.resend((byte)'r');//r means error
        break;
       }
      }    
     }     
    }
   }
   catch(SocketException se){
    System.out.println("connection counld not be opened!");
    se.printStackTrace();
    System.exit(1);
   }
   catch(IOException ioe){
          System.out.println("the client send a file but you can't receive by any problems.");
          ioe.printStackTrace();
          System.exit(1);
   }
  }
 }
 
 public void resend(byte flag){
  byte[] buf = new byte[10];//reset the data,because the client use only the first byte to resolve so 10B is enough
  buf[0] = flag;
  sendPacket = new DatagramPacket(buf,buf.length,recePacket.getAddress(),recePacket.getPort());
  try{
   socket.send(sendPacket);
  }
  catch(IOException ioe){
   ioe.printStackTrace();
  }
 }
}

import java.net.*;
import java.io.*;
 
public class Client {
 private static DatagramSocket socket ;
 private static DatagramPacket recePacket;
 private static DatagramPacket sendPacket;
 private static final int INPORT = 2100;//==how it do if this is the same as the Server's ?
 private static RandomAccessFile raf;
 private static InetAddress add ;
 private static byte[] buff = new byte[1024];
 private static int packetTotal;
 private static File tempDir;
 private static boolean canSend = true;
 private static int baseNum = 100;
 private static final String tempFileExtend = ".tmp";
 
 //data format:---------------------------------
 /the first transform
 //data[100] the amount of the files will be sent

 /the later transform
 //data[0] = 'n':a new file will be sent;then data[1] = length of filename(in bytes)
 //          'r':a error and terminated this transform;
 //          'o':exit normally;
 //          101 to 109 the resend number.
 
 public Client(){
  recePacket = new DatagramPacket(new byte[1024],1024);
  try{
   add = InetAddress.getByName("59.42.110.15");//==this will be changed if in intranet****
   tempDir = new File("d://client//temp");
  }
  catch(UnknownHostException uhe){
   uhe.printStackTrace();
  }  
 }
 
 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Client c = new Client();
  try{
   socket = new DatagramSocket(INPORT);//2100 is the port of local host
  }
  catch(SocketException se){
   System.out.println("Can't open the connection!");
   se.printStackTrace();
  }
  //the file's length
  long bytes = 0;
  try{
   raf = new RandomAccessFile("d://client//dest//data.txt","rw");
   bytes = raf.length();
   packetTotal = (int)bytes / 1024;
   if(bytes % 1024 != 0)
    packetTotal++;//if file's length isn't interger multiple,increase 1
   //now it is the real value use for transforming and creating temperory files
   packetTotal += baseNum;
   //create the tempfile of these packets use for sending or resending and write to a temp file
   for(int i= baseNum+1;i<=packetTotal;i++){
    File tempFile = new File(tempDir,i+tempFileExtend);
    tempFile.createNewFile();
    //==tempFile.deleteOnExit();
    byte[] buff = new byte[1024];
    raf.read(buff);
    RandomAccessFile stream = new RandomAccessFile(tempFile,"rw");
    stream.write(buff);
   }   
  }
  catch(FileNotFoundException fnfe){
   fnfe.printStackTrace();
  }
  catch(IOException ioe){
   ioe.printStackTrace();
  }
  
  //now start send files or receive any info
  try{
   buff[0] = 'n';
   String sendFile = "data.txt";
   int fileNameLength = sendFile.length();
   buff[1] = (byte)fileNameLength;
   buff[100] = (byte)packetTotal;
   //Oops!this is a deprecated method!***********
   sendFile.getBytes(0,buff[1],buff,2);
   sendPacket = new DatagramPacket(buff,buff.length,add,1700);
   
   socket.send(sendPacket);
   int count = 101;
   //first,send the indentify data
   //this method must NOT block becasue it has additional IP of remote machine  
   
   while(true){
    if(count <=packetTotal){
     setFileData(count); 
     count++;         
    }
    //this means count is > packetTotal(that is sending is over),now listening any info from Server
    //open receive port
    else {
     System.out.println("receiving....");
     socket.receive(recePacket);
     byte[] data = recePacket.getData();
     if(data[0] == 'o'){
      System.out.println("OK!the file has been sent to the Server successfully.");
      break;
     }
     else if(data[0] == 'r'){
      System.out.println("Sorry!there are any problems so the file can't been sent.");
      break;
     }
     else {
      //read the resend index of the packet      
      byte resendNO = data[0];   
      System.out.println("resend package: "+resendNO);
      setFileData(resendNO);
      count = 101;//reset
     }
    }
   }   
  }
  catch(IOException ioe){
   ioe.printStackTrace();
  }
  finally{
   socket.close();
   try{
    raf.close();
   }
   catch(IOException ioe){
    
   }
  }
 }
 
 public static void setFileData(int tempFileNO){
  try{
   File tempFile = new File(tempDir,tempFileNO+tempFileExtend);
   tempFile.createNewFile();   
   byte[] buff = new byte[1024];
   raf = new RandomAccessFile(tempFile,"rw");
   raf.read(buff);
   sendPacket.setData(buff);
   socket.send(sendPacket);
  }
  catch(FileNotFoundException fnfe){
   fnfe.printStackTrace();
  }
  catch(IOException ioe){
   ioe.printStackTrace();
  }
 }
}
评论:本文采取的分包技术,觉得不是很好,就是将一个文件拆分成几个文件,然后逐个送发;客户端就始终接到送过来的包,如果已经是正常接收完毕,就开始重新组装包;如果是有漏包,就根据包号发送服务器要求重发,服务器则将包号对应 的文件回发,一切完毕后就可以删除所有服务器/客户端的文件了(但这里不知道怎么的老是删除不成功,你知道吗?怎么不告诉我一声哦??),当然例子中涉及受限条件,如果是真正应用的话还是需要调整代码的,因条件而异咯:)

3.自创文章

import java.net.*;
import java.io.*;

public class SocketServer{
 public static void main(String[] args) throws Exception{
  ServerSocket server = new ServerSocket(3107);
  Socket client = server.accept();
  OutputStream dos = client.getOutputStream();
  
  for(int i=0;i<10;i++){//会先主动断开
   dos.write((byte)i);
   dos.flush();
   /*try{
    Thread.sleep(500);
   }
   catch(Exception e){
   }*/
  }
  
  dos.close();
  client.close();
  server.close();
 }
}

import java.net.*;
import java.io.*;

class SocketClient {
 public static void main(String[] args) throws Exception{
  Socket server = new Socket("127.0.0.1",3107);
  InputStream dis = server.getInputStream();
  int val = 0;
  while((val=dis.read()) != -1){
   System.out.println(val);
   try{
    Thread.sleep(500);
   }
   catch(Exception e){
   }
  }
  
  dis.close();
  server.close();
 }
}

这例子说明,socket其实是"不负责的",它一旦建立连接,只是将自己的任务发出去就算了,我认为流信息始终是先发到端口(它是内存一部分吧),然后客户就读取,其实我觉得这也是客户端能即时读取的原因(我在内部网测试过了,确定是当服务器发送完数据并自动断开连接后,客户端依然可以读取数据),错了吗?好,评论一下吧,我也是刚好的哦!),但客户端却始终准确地读取的,不像DATAGRAM一样检测.

总结:socket是可靠传送信息的方法,但其实是有代码的,包括建立连接,数据校验等;datagram是比较轻型的,它好像是一个智能的子弹,自动跟踪目标;比如就是:SOCKET是打电话,DATAGRAM是信件,不保证你的情信会飞到对方...:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值