按老师要求做了一个UDP传输的程序,但是传输后文件的数据丢包很严重,像exe,chm这类文件完全无法打开
请各位看下我的程序出了什么问题。
多谢!
显示客户端的接收文件:
 private class DownFile extends Thread {
        public void run() {
            JFileChooser chooser = new JFileChooser();
            chooser.setSelectedFile(new File(".", selected));
            int value = chooser.showSaveDialog(null);
            if (value == JFileChooser.APPROVE_OPTION) {
                try {
                    byte[] temp = selected.getBytes();
                    DatagramPacket packet = new DatagramPacket(temp, temp.length, InetAddress.getByName(host_ip), 3001);
                    client_socket = new DatagramSocket();
                    client_socket.send(packet);
                    File file = chooser.getSelectedFile();
                    FileOutputStream fileout = new FileOutputStream(file);
                    byte[] b = new byte[1024];
                    int i = 0;
                    packet = new DatagramPacket(b, b.length);
                    do {
                        client_socket.receive(packet);
                        fileout.write(b, 0, packet.getLength());
                        fileout.flush();
                    } while (packet.getLength() != 0);
                    client_socket.close();
                    fileout.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }下面是服务器的:
private void download(DatagramPacket packet) {
        try {
            String file_str = new String(packet.getData(), 0, packet.getLength());
            File file = new File(file_path, file_str);
            FileInputStream filein = new FileInputStream(file);
            byte[] b = new byte[1024];
            long i = 0;     //已传输的字节数
            String l = "";   //已K来计算已传输的数量
            String per = "";  //显示百分比
            int length = 0;
            table_frame.setSize(400, 400);
            table_frame.add(pane);
            Vector cell = new Vector();
            cell.add(packet.getAddress());
            cell.add(file_str);
            cell.add(l);
            cell.add(per);
            cell.add("传输中...");
            row.add(cell);
            tableModel.setDataVector(row, head);
            table.setModel(tableModel);
            table.setDefaultRenderer(
                    Class.forName("java.lang.Object"),
                    cellModel);
            table_frame.setVisible(true);
            InetAddress address = packet.getAddress();
            int port = packet.getPort();            while ((length = filein.read(b)) != -1) {
                packet = new DatagramPacket(b, length, address, port);
                try {
                    file_server.send(packet);
                } catch (IOException ex) {
                    Logger.getLogger(UDPServer.class.getName()).log(Level.SEVERE, null, ex);
                }
                i = i + length;
                l = i / 1024 + "K";
                per = (i * 100 / file.length()) + "%";
                cell.set(2, l);
                cell.set(3, (int) (i * 100 / file.length()));
                cell.set(4, per);
                cell.set(5, "传输中...");
            }
            filein.close();
            cell.set(5, "传输完毕");
        } catch (Exception e) {
            System.out.println("Error handling a client: " + e);
        }
    }
请各位帮忙分析一下,谢谢。

解决方案 »

  1.   

    具体的程序请看这个网址:
    http://blog.csdn.net/thdgytyi/archive/2009/01/03/3694065.aspx
      

  2.   

    你要搞清楚,UDP就是为了提高速度,不会验证包的发送是否成功的
    所以你客户端收到的包可能会丢包、可能会顺序错误等除非你网络非常差,否则我觉得其实只是你收到的包顺序有问题罢了。解决办法就是使用TCP,又或者在UDP协议之上,你再定义发送接收协议保证数据的完整性和正确性
      

  3.   

    老师要求只能用UDP传输,TCP的之前做过了一个。
    这个程序就是在TCP的那个程序的基础上改写的.......
    请问如何能使传输的顺序能正确呢?
    现在传输的文件数据丢失相当的严重
      

  4.   

    如果你必须使用UDP,那么你可以在发送的第一个包先说明要发送的文件信息(名称、长度)及后续发送包的个数,
    然后客户端必须返回一个包说明已经接收到然后在服务器发送的后续包的前两个字节声明该包的序号,后面的字节才是真正的内容,最后几个字节为该包的摘要(MD5之类)保证包的数据没有错误
    客户端在接收到包,先验证包是否有错误,如果有错误则发信息要求重新发一次;
    然后判断序号,按序号正确的写入文件相应位置;如果发现序号次序乱了并可能已经丢包了(次序乱了不意味包已经丢失,可能只是1、2、3变成了1、3、2)则发信息要求重发上述说明中,文件信息包、确认包、要求重发包也可能丢失,所以需要在没有收到后续时,需要重发
      

  5.   

    似乎......虽然明白意思,但是应该怎么做呢?
    我敢刚接触java.......
    能介绍些相关的文章或简单的例子吗?
    谢谢。
      

  6.   

    客户端在接收到包,先验证包是否有错误,如果有错误则发信息要求重新发一次; 
    然后判断序号,按序号正确的写入文件相应位置;如果发现序号次序乱了并可能已经丢包了(次序乱了不意味包已经丢失,可能只是1、2、3变成了1、3、2)则发信息要求重发
    请问这有什么资料可以看下吗?
    接触JAVA不到两个月,虽然能理解意思,但不清楚怎么下手啊。
      

  7.   

    这个与Java本身没有什么关系,纯粹是一个技术上的流程罢了。你应该首先解决序号问题,这个其实并不复杂啊,你收到的包并不是直接写入OutputStream中,而是写入RandomAccessFile就可以啦判断丢包可以大概这样判断:如果在未收到序号为N的包时,已经收到了N+10的包或者结束了,则发一个UDP包到服务器上要求重发
    服务器接收客户端的请求的,按指定的序号重新发送就可以了
      

  8.   

    private class DownFile extends Thread {
            public void run() {
                JFileChooser chooser = new JFileChooser();
                chooser.setSelectedFile(new File(".", selected));
                int value = chooser.showSaveDialog(null);
                if (value == JFileChooser.APPROVE_OPTION) {
                    try {
                        byte[] temp = selected.getBytes();
                        DatagramPacket packet = new DatagramPacket(temp, temp.length, InetAddress.getByName(host_ip), 3001);
                        client_socket = new DatagramSocket();
                        client_socket.send(packet);
                        File file = chooser.getSelectedFile();
                        FileOutputStream fileout = new FileOutputStream(file);
                        byte[] b = new byte[2048];
                        int i = 0;
                        packet = new DatagramPacket(b, b.length);
                       DatagramSocket socket = new DatagramSocket();
                        do {
                            client_socket.receive(packet);
                            String str = new String(b, 0, packet.getLength(),"UTF-8");
                            String[] strs = str.split("~!~");
                            boolean flag = true;
                            while (flag) {
                                System.out.println(Integer.parseInt(strs[0]) + "/" + strs[1].getBytes().length);
                                if (Integer.parseInt(strs[0]) > strs[1].getBytes("UTF-8").length) {
                                    byte[] t = new byte[256];
                                    t = "error".getBytes();
                                    DatagramPacket p = new DatagramPacket(t, t.length, InetAddress.getByName(host_ip), 3002);
                                    socket.send(p);
                                } else {
                                    byte[] t = new byte[256];
                                    t = "error".getBytes();
                                    DatagramPacket p = new DatagramPacket("success".getBytes(), "success".getBytes().length, InetAddress.getByName(host_ip), 3002);
                                    socket.send(p);
                                    fileout.write(strs[1].getBytes(), 0, strs[1].getBytes().length);
                                    fileout.flush();
                                    flag = false;
                                }
                            }
                        } while (packet.getLength() != 0);
                        client_socket.close();
                        fileout.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }
    这个似乎还是不行,传了一个800K的chm,还是丢数据。
      

  9.   

    这是服务器传输部分的改后的程序:
    private void download(DatagramPacket packet) {
    try {
    temp_server = new DatagramSocket(3002);
    String file_str = new String(packet.getData(), 0, packet
    .getLength());
    File file = new File(file_path, file_str);
    FileInputStream filein = new FileInputStream(file);
    byte[] b = new byte[1024];
    long i = 0; // 已传输的字节数
    String l = ""; // 已K来计算已传输的数量
    String per = ""; // 显示百分比
    int length = 0;
    table_frame.setSize(400, 400);
    table_frame.add(pane);
    Vector cell = new Vector();
    cell.add(packet.getAddress());
    cell.add(file_str);
    cell.add(l);
    cell.add(per);
    cell.add("传输中...");
    row.add(cell);
    tableModel.setDataVector(row, head);
    table.setModel(tableModel);
    table.setDefaultRenderer(Class.forName("java.lang.Object"),
    cellModel);
    table_frame.setVisible(true);
    InetAddress address = packet.getAddress();
    int port = packet.getPort();
    while ((length = filein.read(b)) != -1) {
    boolean flag = true;
    String str = "~!~"; // 分隔符,在头上添加长度信息
    String temp = new String(b, 0, length,"UTF-8");
    str = length + str + temp;
    byte[] t = str.getBytes("UTF-8");
    System.out.println(t.length);
    packet = new DatagramPacket(t, t.length, address, port);
    try {
    file_server.send(packet);
    } catch (IOException ex) {
    Logger.getLogger(UDPServer.class.getName()).log(
    Level.SEVERE, null, ex);
    }
    while (flag) {
    byte buffer[] = new byte[256];
    DatagramPacket p = new DatagramPacket(buffer, buffer.length);
    temp_server.receive(p);
    String s = new String(buffer, 0, p.getLength());
    System.out.println("***" + s.trim());
    if (s.trim().equals("error")) {
    try {
    file_server.send(packet);
    } catch (IOException ex) {
    Logger.getLogger(UDPServer.class.getName()).log(
    Level.SEVERE, null, ex);
    } } else if (s.trim().equals("success")) {
    flag = false;
    }
    }
    i = i + length;
    l = i / 1024 + "K";
    per = (i * 100 / file.length()) + "%";
    cell.set(2, l);
    cell.set(3, (int) (i * 100 / file.length()));
    cell.set(4, per);
    cell.set(5, "传输中...");
    }
    filein.close();
    cell.set(5, "传输完毕");
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    但是这样不但速度慢的无法忍受,而且最终的结果时还是丢失数据.......
      

  10.   

    String str = new String 是不可行的。因为CHM中包含了不可见字符,所以你只应将byte[]数组进行处理,而不可以变成String
      

  11.   

    服务器:
        private void download(DatagramPacket packet) {
            try {
                String file_str = new String(packet.getData(), 0, packet.getLength());
                File file = new File(file_path, file_str.split("_____")[0]);
                FileInputStream filein = new FileInputStream(file);
                byte[] b = new byte[1024];
                int i = 0;     //已传输的字节数
                String l = "";   //已K来计算已传输的数量
                String per = "";  //显示百分比
                int length = 0;
                table_frame.setSize(400, 400);
                table_frame.add(pane);
                Vector cell = new Vector();
                cell.add(packet.getAddress());
                cell.add(file_str);
                cell.add(l);
                cell.add(per);
                cell.add("传输中...");
                row.add(cell);
                tableModel.setDataVector(row, head);
                table.setModel(tableModel);
                table.setDefaultRenderer(
                        Class.forName("java.lang.Object"),
                        cellModel);
                table_frame.setVisible(true);
                //取得连接的地址和端口
                InetAddress address = packet.getAddress();
                int port = packet.getPort();
                while ((length = filein.read(b,8,1016)) != -1) {
                 //first 4 byte for offset, next 4 byte for len
                 byte[] tmpb = null;            
                 tmpb = int2bytes(i);
                 for(int k=0;k<4;k++)
                 b[k]=tmpb[k];
                 tmpb = int2bytes(length);
                 for(int k=0;k<4;k++)
                 b[k+4]=tmpb[k];
                
                    packet = new DatagramPacket(b, length+8, address, port);
                    try {              
                        file_server.send(packet);
                       // file_server.getSendBufferSize();
                        Thread.currentThread().sleep(100);                  
                        System.err.println("send:"+i);
                    } catch (IOException ex) {
                        Logger.getLogger(UDPServer.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    i = i + length;
                    l = i / 1024 + "K";
                    per = (i * 100 / file.length()) + "%";
                    cell.set(2, l);
                    cell.set(3, (int) (i * 100 / file.length()));
                    cell.set(4, per);
                    cell.set(5, "传输中...");
                }
                filein.close();
                cell.set(5, "传输完毕");
            } catch (Exception e) {
                System.out.println("Error handling a client: " + e);
            }
        }
        //线程,刷新table
        private class TableThread extends Thread {
            public void run() {
                while (true) {
                    table.repaint();
                }
            }
        }
        public void stopServer() {
            flag = false;
        }
        static int bytes2int(byte[] b,int off)
        {
                 //byte[] b=new byte[]{1,2,3,4}; 
                 int mask=0xff;
                 int temp=0;
                int res=0;
                for(int i=0;i<4;i++){
                    res<<=8;
                    temp=b[i+off]&mask;
                    res|=temp;
                }
               return res;
        }     static byte[] int2bytes(int num)
        {
               byte[] b=new byte[4];
               int mask=0xff;
               for(int i=0;i<4;i++){
                    b[i]=(byte)(num>>>(24-i*8));
               }
              return b;
        }
    客户端
     private class DownFile extends Thread {
            public void run() {
             try{       
                JFileChooser chooser = new JFileChooser();
                
                //设置默认保存的文件名
                String[] tmp = selected.split("_____");
                if(tmp==null||tmp.length!=2)
                 return;
                chooser.setSelectedFile(new File(".", tmp[0]));
                RandomAccessFile fileout = null;
                int value = chooser.showSaveDialog(null);
                if (value == JFileChooser.APPROVE_OPTION) {
                    try {
                        byte[] temp = selected.getBytes();
                        DatagramPacket packet = new DatagramPacket(temp, temp.length, 
                         InetAddress.getByName("localhost"), 3001);
                        client_socket = new DatagramSocket();
                        client_socket.send(packet);
                        //取得选取的文件
                        File file = chooser.getSelectedFile();
                        //准备写入
                       // FileOutputStream fileout = new FileOutputStream(file);
                        fileout = new RandomAccessFile(file,"rw");
                        for(int k=0;k<(Integer.parseInt(tmp[1]));k++)
                         fileout.writeByte(0);
                        //取得服务器端的流
                        //下面为传输
                        byte[] b = new byte[1024];
                        int i = 0,wri_len=0,total=0;                   
                        
                        do {
                         packet = new DatagramPacket(b, b.length);                   
                            client_socket.receive(packet);    
                            fileout.seek(bytes2int(b,0));
                            wri_len = bytes2int(b,4);
                            total+=wri_len;
                            System.err.println("recv:"+total);
                            fileout.write(b, 8, wri_len);
                            if(Integer.parseInt(tmp[1])<=total)
                             break;                      //   fileout.flush();
                        } while (packet.getLength() >0);
                        //关闭流
                        client_socket.close();
                       
                    } catch (IOException e1) {
                        e1.printStackTrace();
                      
                    }finally{
                       fileout.close();
                    }
                }
            
            }catch(Exception e){
        
         }
        }
        }
         
        static int bytes2int(byte[] b,int off)
        {
                 //byte[] b=new byte[]{1,2,3,4}; 
                 int mask=0xff;
                 int temp=0;
                int res=0;
                for(int i=0;i<4;i++){
                    res<<=8;
                    temp=b[i+off]&mask;
                    res|=temp;
                }
               return res;
        }     static byte[] int2bytes(int num)
        {
               byte[] b=new byte[4];
               int mask=0xff;
               for(int i=0;i<4;i++){
                    b[i]=(byte)(num>>>(24-i*8));
               }
              return b;
        } 
    目前文件不丢失数据了,但是文件还是打不开
    例如飞鸽,虽然大小没变,但传输后的文件无法打开而且连图标的图案都没显示出来。