0

1) 質問: 両方の while ループが同時に実行されていますか? 私が知りたいのは、sendDATA の while ループが実行されている間、main() メソッドの while ループが実行されていることです。

2) そうでない場合、どうすれば同時に送受信できますか? TFTP を実装する必要があるため、パケットごとに 512 バイトを読み取って送信する必要があります。512 バイトを読み取って送信し、ACK パケットがクライアントから到着するのを待ちます。したがって、着信[0] = 3および着信[1] = intブロック #

着信のブロック番号が送信したものと一致する場合、次のパケットを送信できます。そうでない場合は、前のパケットを送信する必要があります。これを1つのループで実装する方法がわかりません。

私のコード:

package source;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.Scanner;

public class Server
{


    static String[] errorcodes = {"Not defined", "File not found", "Access violation", "Disk full or allocation exceeded", "Illegal TFTP Operation",
                              "Unknown transfer ID", "File already exists", "No such user"}; 

    static String FILE = "";
    static String MODE = "";

    static byte[] outgoing = new byte[512];
    static byte[] incoming = new byte[512];

    static DatagramSocket socket;

    static String clientip;
    static int clientport;


    public static void main(String[] args) throws Exception
    {   
        socket = new DatagramSocket(13373); // 69 Reserved for TFTP

        // Listen for incoming packets
        while(true) {
            DatagramPacket packet = new DatagramPacket(incoming, incoming.length);
            socket.receive(packet);

            clientip = packet.getAddress().toString().replace("/", "");
            clientport = packet.getPort();

            System.out.println(clientport);

            if(incoming[0] == 1 || incoming[0] == 2) {
                handleRequest(incoming);
            }

        }
    }

    // sends DATA opcode = 3 : | opcode | block # | data |
    private static void sendDATA() {
        outgoing = new byte[512]; // Empty array
        try {
            ByteBuffer sDATA = ByteBuffer.allocate(516);
            // 512 - 2 byte opcode, 2 byte block #, 512 data

            DatagramPacket data = new DatagramPacket(outgoing, outgoing.length, InetAddress.getByName(clientip), clientport);
            InputStream fis = new FileInputStream(new File(FILE));

            int a;
            int block = 1;



            while((a = fis.read(outgoing,0,512)) != -1)
            {
                data.setLength(a);
                sDATA.put((byte)3);
                sDATA.put((byte)block);
                sDATA.put(outgoing);
                socket.send(data);

                if(incoming[0] == 3 && incoming[1] == block) 
                {

                }

            }


        } catch (Exception e) {

        }

    }


    // Request packet: | opcode | filename | 0 | mode | 0 |
    // Upravlja le z WRQ / RRQ opcode
    private static void handleRequest(byte[] packet) 
    {

        int opcode = packet[0];
        String filename = "";
        int offset = 0;
        String mode = "";

        // Get filename
        for(int i = 1; i < packet.length; i++) {
            if(packet[i] != 0) {
                filename += (char)packet[i];
            } else {
                offset = i;
                break;
            }
        }
        // Get mode
        for(int j =  offset; j < packet.length; j++) {
            if(packet[j] != 0) {
                mode += (char)packet[j];
            }
        }
        FILE = filename;
        MODE = mode;
        if(opcode == 1) {
            // Do RRQ
            File test = new File(filename);
            if(test.exists()) {
                sendACK(); 
            } else {
                sendERROR(1);
            }
        } else {
            // Do WRQ
            File test2 = new File(filename);
            if(test2.exists()) {
                sendERROR(6);
            } else {
                sendACK();
            }
        }
    }


    // send ACK opcode = 4 : | opcode | block # |
    // block 0 -> prvi ACK, odgovor na RRQ/WRQ 
    // block 1 -> stevilo poslanega bloka, stetje blokov se zacne
    // pri 1.
    private static void sendACK() {
        outgoing = new byte[512];
        outgoing[0] = (byte)4;
        outgoing[1] = (byte)0;

        try {
            DatagramPacket ackpacket = new DatagramPacket(outgoing, outgoing.length, InetAddress.getByName(clientip), clientport);
            socket.send(ackpacket);
        } catch(Exception e) {

        }
    }   

    // send ERROR packet opcode =  5 : | opcode | errorcode | msg | 0 |
    // General errorcodes: 2
    private static void sendERROR(int errorcode) {
        ByteBuffer errpack = ByteBuffer.allocate(512);
        errpack.put((byte)5); // opcode
        errpack.put((byte)errorcode); // errorcode
        try {
            errpack.put(errorcodes[errorcode].getBytes("US-ASCII")); // msg
            errpack.put((byte)0);

            outgoing = errpack.array(); // Pack ERROR Packet

            DatagramPacket errpacket = new DatagramPacket(outgoing, outgoing.length, InetAddress.getByName(clientip), clientport);
            socket.send(errpacket);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

}
4

1 に答える 1

0

質問: 両方の while ループが同時に実行されていますか? 私が知りたいのは、sendDATA の while ループが実行されている間、main() メソッドの while ループが実行されていることです。

いいえ。ただし、handleRequest を呼び出している間、着信 UDP パケットは (一定の制限まで) バッファリングされます。handleRequest が返されたときに、利用可能な新しい UDP パケットがあれば、socket.receive() への後続の呼び出しがすぐに返されるようにします。これは、「handleRequest」呼び出しに長時間費やした場合にパケットが失われないことを保証するものではありません。ただし、妥当な負荷と小さなファイルでは機能する可能性があります。

そうでない場合、どうすれば同時に送受信できますか。

各セッションの状態を保持するデータ構造を保持する必要があります。(例えば、どのファイルが転送されているか、これまでに転送されたバイト数、ack 状態など...) 各セッションをコレクション/リストのどこかに保存します。コード ロジックは次のようになります。

// this is not java, just pseudo code
while (true)
{
    packet = receive packet from network
    session = list_of_sessions.lookup(packet.fromaddress)
    if (session == null)
    {
        session = new Session(packet)
        list_of_sessions.add(new Session(), packet.fromaddress)
    }
    handleRequest(packet, session); // sends a response based on state or packet received
    if (session.isFinished)
    {
        list_of_sessions.remove(session)    
    }
}

上記のシングル スレッド アプローチがおそらく最適です。しかし、ワイルドになりたい場合は、スレッドに投資できます。まだ準備ができているかどうかわかりません。

于 2012-09-12T15:29:41.220 に答える