как настроить первые 2 байта в пакете дейтаграмм java UDP?

#java #udp #datagram

#java #udp #дейтаграмма

Вопрос:

У меня есть программа клиент-сервер, пытающаяся отправить файл jpg.

Я пытаюсь добавить байт ПОДТВЕРЖДЕНИЯ и байт номера блока ДАННЫХ в UDP-пакет. У меня возникли проблемы с этой частью кода (этот раздел кода представляет собой всего лишь код обработки блоков данных из клиентской программы):

 if(pType == TftpUtil.DATA){     //If byte 0 = 2 meaning it's a DATA block.

    private int blockNumber = 1;    

    byte[] bn = intToBytes(blockNumber);        
    
    ByteBuffer buff = ByteBuffer.allocate(8);   // For 2 ints, an int is 4 bytes long
    buff.put(TftpUtil.ACK);                     // This is an "ACKNOWLEDGE byte", at position zero.
    buff.put(bn);                               // This is supposed to be a byte containing the integer "blockNumber" meaning, what DATA block in the series it is.
    buff.rewind();                              // Not sure what this does. What does this do? Saw it in the code I copied. 
    DatagramPacket dataPacket = new DatagramPacket(buff.array(), buff.limit(), echoServerIP, 69);   //Create datagramPacket

    System.out.println("TftpClient sending a ACK on port "   clientSocket.getLocalPort());
    clientSocket.send(dataPacket);
    blockNumber  ;
    
}
  

Приведенный выше код не работает с сервером, который у меня есть, и сервер был создан Учителем. Я проверил код сервера, выглядит отлично.

По какой-то причине он зависает в той части, где предполагается отправить байт ПОДТВЕРЖДЕНИЯ, содержащий целое число, «3», что означает «Подтверждено». И предполагается, что он должен содержать этот байт ACK в нулевой позиции buff.array(). Но сервер говорит, что не получен подтверждающий байт.

TftpUtil начинается так:

 public class TftpUtil {
    
    //read request packet 
    public static final byte RRQ = 1;
    //data packet
    public static final byte DATA = 2;
    //ack packet
    public static final byte ACK = 3;
    //error packet
    public static final byte ERROR = 4;
    //the maximum number of resent
    public static final int MAX_RESEND = 5;     
    //data buffer size: 512
    public static int DATA_BUFFER_SIZE = 512;
    //packet buffer size: 2 bytes header   512 bytes file data;
    public static int PACKET_BUFFER_SIZE = DATA_BUFFER_SIZE   2;  
}
  

Сервер отправляет файл .jpg в блоках данных по 512 байт. Сервер обнаруживает, что установлено соединение, и пытается отправить файл. Он отправляет первый блок данных, который, кажется, проходит, потому что, когда я проверяю папку, появляется asd.jpg файл размером 512 байт. Но когда клиент отправляется, чтобы отправить байт подтверждения обратно, сервер сообщает, что он неверен, и истекает время ожидания.

Можете ли вы увидеть, что не так с кодом, который я вставил, или мне следует вставить больше кода?

Спасибо.

ОБНОВИТЬ полный код сервера:

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class server_v004 {
    private InetAddress serverAddress;
    private int serverPort;
    private DatagramSocket serverSocket; 
    private static final ExecutorService exec =  Executors.newFixedThreadPool(10); 
    
    public void startServer()
    {
        try {
            String serverIpAddress = "192.168.1.32";
            InetAddress echoServerIP = InetAddress.getByName(serverIpAddress);
            
            serverSocket = new DatagramSocket(serverPort, echoServerIP);
            System.out.println("TftpServer on port "   serverSocket.getLocalPort());
    
            while(true) {
                byte[] buf = new byte[1472];
                DatagramPacket p = new DatagramPacket(buf, 1472);
                System.out.println("waiting for connection ....");
                serverSocket.receive(p);            
                server_v004ServerWorker worker = new server_v004ServerWorker(p);
                exec.execute(worker);
            }
        }
        catch(Exception e) {
            System.err.println("Exception: "   e);
        }
        serverSocket.close();
        return;
    }
    
    
    public static void main(String[] args) {
        server_v004 tftpServer = new server_v004();
        //tftpServer.checkArgs(args);
        try{
            tftpServer.parseArgsAndInit(args);
            tftpServer.startServer();
        }
        catch(Exception e){
            e.printStackTrace();
            
        }
    }

    private void checkArgs(String[] args){
         if(args.length <0) {       //<1    //<2     
             System.out.println("Usage: TftpServer");       //server_port server_ip
             System.exit(0);
         }
    }
    
    private void parseArgsAndInit(String[] args) throws Exception{
        //serverAddress = InetAddress.getByName(args[0]);           
        serverPort = 12345;         //Integer.parseInt(args[1]);                    
   }    
}

class server_v004ServerWorker implements Runnable
{
    //use to store RRQ request
    private DatagramPacket req;
    //client address who sent the RRQ request 
    private SocketAddress clientAddress;
    //socket used to send the file data packets to client
    private DatagramSocket sendfileSocket;
    //byte buffer to store the file data
    private byte[] dataBuffer = new byte[TftpUtil.DATA_BUFFER_SIZE];
    //the first block sequence
    private byte currentBlockSeq = 1;
    //use to retrieve the ack packet, only two bytes long
    private DatagramPacket ackDP = new DatagramPacket(new byte[2], 2);
    private int TIME_OUT = 1000; //1 second 
    
    
    public void run(){
        try{
            sendfileSocket = new DatagramSocket(69);
            System.out.println("TftpServer sending a file on port "   sendfileSocket.getLocalPort());
            byte pType = TftpUtil.checkPacketType(req);
            clientAddress = req.getSocketAddress();
            //checking if the first packet from client is a RRQ packet 
            if(pType == TftpUtil.RRQ){
                String filename = getFileName(req);
                System.out.println("Requested file name:"   filename);
                //if the file doesn't exist, send ERROR packet and close socket
                if(!(new File(filename)).exists()) {
                    DatagramPacket errorDP = TftpUtil.packErrorPacket(filename);
                    errorDP.setSocketAddress(clientAddress);
                    sendfileSocket.send(errorDP);
                }
                else{
                    //the file does exist, send file
                    sendfile(filename);
                }
            }// end if
        
        }catch(Exception e){        
                e.printStackTrace();            
        }
        sendfileSocket.close(); 
        return;
    }

     
    private void sendfile(String filename) throws Exception{
        FileInputStream fileInput = new FileInputStream(filename);                     
        while(true){
          int rec = fileInput.read(dataBuffer);
          //the file size is a multiple of 512, send empty packet 
          if(rec == -1){
                 sendDataPacket(new byte[0],0);
                 System.out.println("The last packet [0 byte in size]:#" currentBlockSeq);
                 break;
            }   
            //send a file data packet
            boolean successed = sendDataPacket(dataBuffer,rec);
           //tried five times
           if (!successed) {
                System.out.println("Tried five times, give up");
                System.exit(0);
           }
           // the last packet (the file size if not a multiple of 512)
           if (rec < 512 amp;amp; rec > 0 ) {
               System.out.println("The last packet [" rec " bytes in size]:#" currentBlockSeq);
                    break; 
           }
           currentBlockSeq  ;       
        }//while
        fileInput.close();
    }
    
     //
    private  boolean sendDataPacket(byte[] databuffer,int length) throws Exception{
        int resendCount = 0;
    
        DatagramPacket dataPacket = packFileDataPacket(databuffer,length);
        //try five times
        while(resendCount < TftpUtil.MAX_RESEND){
          try{
                  sendfileSocket.send(dataPacket); 
                  sendfileSocket.setSoTimeout(TIME_OUT);
              System.out.println("sent data block #" currentBlockSeq ", waiting for ack #"   currentBlockSeq);
              //ack arrives
              sendfileSocket.receive(ackDP);
              byte ackedBlockseq = TftpUtil.extractACKNumber(ackDP);
              System.out.println("received ack #"   ackedBlockseq);
             if(ackedBlockseq != currentBlockSeq) {
                  //the acked block seq is not the seq of block sent
                  //ignore this ack and resend                             
                       resendCount  ; 
                       continue;
                   } 
                //this data packet has been acked, return
                return true; 
        
             }//end of try
             catch(SocketTimeoutException ste){
                    resendCount  ;
                System.out.println("timeout #"   resendCount );
             }
         }//end of while
    
        return false;   
    }
      
    private DatagramPacket packFileDataPacket(byte[] dataBuffer, int length){
        int packetLength = 2   length;//type (1)   block seq (1)   data length
        ByteBuffer byteBuffer = ByteBuffer.allocate(packetLength); 
        byteBuffer.put(TftpUtil.DATA);//type
        byteBuffer.put(currentBlockSeq);//block seq
        byteBuffer.put(dataBuffer,0,length);//data   
        DatagramPacket dataPacket = new DatagramPacket(byteBuffer.array(), packetLength);
        dataPacket.setSocketAddress(clientAddress);
        return  dataPacket; 
    }
     
    private  String getFileName(DatagramPacket dataDP){  
        byte[] data = dataDP.getData();
        int dataLength = dataDP.getLength();
        ByteBuffer byteBuffer = ByteBuffer.allocate(dataLength-1); 
        //remove the packet type (RRQ)
        byteBuffer.put(data,1,dataLength-1);
        return new String(byteBuffer.array());
    }

    public server_v004ServerWorker(DatagramPacket req)
    {
        this.req = req;
    }
}
  

и полный код TftpUtil:

 import java.net.DatagramPacket;
import java.nio.ByteBuffer;

public class TftpUtil {
    
    //read request packet 
    public static final byte RRQ = 1;
    //data packet
    public static final byte DATA = 2;
    //ack packet
    public static final byte ACK = 3;
    //error packet
    public static final byte ERROR = 4;
    //the maximum number of resent
    public static final int MAX_RESEND = 5;     
    //data buffer size: 512
    public static int DATA_BUFFER_SIZE = 512;
    //packet buffer size: 2 bytes header   512 bytes file data;
    public static int PACKET_BUFFER_SIZE = DATA_BUFFER_SIZE   2;  
    
    //return the type (RRQ, DATA, ACK or ERROR) of a packet  
    public static byte checkPacketType(DatagramPacket dataDP){
            byte[] payload = dataDP.getData();      
            return payload[0];
      }
     
     //return a RRQ packet 
    public static DatagramPacket packRRQDatagramPacket(byte[] filename) throws Exception{
        return packDatagramPacket(RRQ, filename);   
    }   
    
    //return a "file not found" error packet
    public static DatagramPacket packErrorPacket(String filename) throws Exception{
        String errorMessage = filename   " not found";
        return packDatagramPacket(ERROR, errorMessage.getBytes());
    }
    
    /*
     * utility method that wrap a packet type, data into a DatagramPacket
     */
    private static DatagramPacket packDatagramPacket(byte type, byte[] payload) throws Exception{
        int dataLength = 1   payload.length;
        ByteBuffer byteBuffer = ByteBuffer.allocate(dataLength); 
        byteBuffer.put(type);
        byteBuffer.put(payload);    
        return new DatagramPacket(byteBuffer.array(), dataLength);      
     }  
    
    //return the ack number of a ACK packet
    public static  byte extractACKNumber(DatagramPacket ackDP){
            byte[] payload = ackDP.getData();
            return payload[1];
    }
    
    //print the string content of a ERROR packet 
    public static void printErrorString(DatagramPacket p){
           byte[] data = p.getData();
           int dataLength = p.getLength();
           ByteBuffer byteBuffer = ByteBuffer.allocate(dataLength-1);
           //ignore the packet type
           byteBuffer.put(data,1, dataLength-1);
          System.out.print(new String(byteBuffer.array()));
        }
  
    //return the block sequence of a data packet
    public static byte extractBlockSeq(DatagramPacket dataDP){
        byte[] payload = dataDP.getData();
        if(payload.length <=1) return -1; //-1: no block sequence in data
        int type = payload[0];
        if(type == DATA){
            return payload[1];
        }
        return -1; //-1: not a data packet
       
   }
    
}
  

Комментарии:

1. Вероятно, мы могли бы использовать этот серверный код, просто чтобы посмотреть, что на самом деле ожидается. Мы также могли бы использовать сообщение об ошибке, которое выдается. Чтобы отладить это, настройте и клиент, и сервер на своей собственной машине, чтобы вы могли наблюдать, что делают оба конца. Вам нужно увидеть оба, или вы просто догадываетесь, в чем проблема.

2. Я могу видеть оба и запускать их локально. Должен ли я использовать wireshark, это было бы полезно?

3. Если это работает локально, вам следует поговорить со своим преподавателем. Проблема может быть на его или ее стороне. Можете ли вы найти пути ошибок, которые может использовать сервер? Какие сообщения об ошибках может выдавать сервер? Где вы можете найти эти ошибки?

4. О, говоря о локальном и удаленном: это UDP. Вы уверены, что пакет достигает сервера? Запуск Wireshark на стороне сервера может быть хорошей идеей, если есть вероятность, что эти пакеты просто отбрасываются.

5. Они оба находятся на одном и том же IP-адресе, передавая пакеты между сокетом 69 (сервер) и другим случайным сокетом (клиент).