2

私は初心者で、これについてのチュートリアルを調べましたが、これを正確に実装する方法がまだわかりません。

main() メソッドに 1 つと send() メソッドに 1 つの 2 つの while ループがあり、両方を同時に実行する必要があります。

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

    // Listen for incoming packets
    while(true) {
        // Do things
    }
}

private static void sendDATA() {
    while(true) {
        // Do things
    }
}

sendDATA の while ループは、ファイルから 512 バイトを読み取り、それらをクライアント クラスに送信することによって機能します。メインメソッドのループがクライアントからパケットを受信し、変数がtrueの場合に変数を更新している間、sendDATAは次の512バイトを読み取って送信しますが、2つのスレッドで作業できません.

これを 1 つの while ループで実行すると、プログラムが動作し、最後のパケット以外のすべてのパケットが転送されます。クライアントは最後のパケットを取得しません。

サーバ:

    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() {

    try {
        ByteBuffer sDATA = ByteBuffer.allocate(514);

        byte[] tmp = new byte[512];
        DatagramPacket data = new DatagramPacket(sDATA.array(), sDATA.array().length, InetAddress.getByName(clientip), clientport);
        InputStream fis = new FileInputStream(new File(FILE));

        int a;
        int block = 1; 

        while((a = fis.read(tmp,0,512)) != -1)
        {
            data.setLength(a);
            sDATA.put((byte)3);
            sDATA.put((byte)block);
            System.out.println(sDATA.array().length);
            sDATA.put(tmp);
            System.out.println(tmp.length);
            socket.send(data); 

            socket.setSoTimeout(60000);

            while(true) {
                DatagramPacket getack = new DatagramPacket(incoming, incoming.length);
                try {
                    socket.receive(getack);
                    if(incoming[0] == 4 && incoming[1] == block) {  
                        break;
                    } else if(incoming[0] == 4 && incoming[1] == block && tmp.length < 511) {
                        fis.close();
                        break;
                    }
                } catch (SocketTimeoutException e) {
                   socket.send(data);
                   continue;
                }

            }
            block++;
        }       
    } catch (Exception e) {
        e.printStackTrace();
    }

}

クライアント:

public static void main(String[] args) throws Exception
{
    clientSocket = new DatagramSocket(8571);

    // WRQ || RRQ
    Scanner input = new Scanner(System.in);
    int opcode = input.nextInt(); input.close();

    // Pripravi paketek
    outgoing = makeRequestPacket(opcode,"filename.txt","US-ASCII");

    // Odposlje pakete
    sendPacket(outgoing);

    // Streznik vrne ACK - opcode 4 ali ERROR - opcode 5
    // Pri ACK zacnemo posiljat DATA opcode 3 drugace prekinemo povezavo ob ERROR - opcode 5
    while(true) {
        DatagramPacket receiveResponse =  new DatagramPacket(incoming, incoming.length);
        clientSocket.receive(receiveResponse);

        // opcode 5 - ERROR
        if(incoming[0] == 5) {
            getError(incoming);
        } 
        else if(incoming[0] == 4 && incoming[1] == 0) { // opcode 4 - Prvi ACK
            System.out.print("opcode: (" + incoming[0] +") ACK received operation confirmed.");
            continue;
        } 
        else if(incoming[0] == 3) {
            System.out.println("Ah got a data packet.");
            File initfile = new File("filename2.txt");
            if(!initfile.exists()) {
                initfile.createNewFile();
            } 

            int block;
            FileOutputStream fio = new FileOutputStream(initfile);

            if(incoming.length > 511) {
                block = incoming[1];
                System.out.println("Will start to write.");

                for(int i = 2; i < incoming.length; i++) {
                    fio.write(incoming[i]);
                }
                ByteBuffer recack = ByteBuffer.allocate(514);
                recack.put((byte)4);
                recack.put((byte)block);
                System.out.println("If i came here and nothing happened something went horribly wrong.");

                DatagramPacket replyACK = new DatagramPacket(recack.array(), recack.array().length, InetAddress.getByName("localhost"),13373);
                clientSocket.send(replyACK);
            } else if (incoming.length < 511) {
                System.out.println("Last chunk.");
                block = incoming[1];
                for(int j = 2; j < incoming.length; j++) {
                    if(incoming[j] != 0) {
                        fio.write(incoming[j]);
                    } else {
                        break;
                    }
                }
                ByteBuffer recack = ByteBuffer.allocate(514);
                recack.put((byte)4);
                recack.put((byte)block);


                DatagramPacket replyACK = new DatagramPacket(recack.array(), recack.array().length, InetAddress.getByName("localhost"),13373);
                clientSocket.send(replyACK);
                fio.close();
                clientSocket.close();
                break;
            }
            continue;

        }
    }


}
4

2 に答える 2

2

プロセス間の通信を同期するには、ブロッキング チャネルを使用する必要があります。

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);

詳細: http://docs.oracle.com/javase/1.4.2/docs/api/java/nio/channels/SocketChannel.html

また、ソケットはプロセス間でデータを送信するように設計されています。そうでない場合は、アプローチが間違っていて、設計を変更する必要があります。つまり、データの準備ができたとき (または毎回) 新しいスレッドで各ループを呼び出します (各スレッドはデータを処理してから終了する)、または同時実行が必要ない場合は、1 つのループ内で両方の命令ブロックを順番に実行するだけです。プログラムの状態図は、最適なソリューションを判断するのに役立ちます。

于 2012-09-11T08:45:13.853 に答える
0

したがって、私が探していたのは、同じクラスの 2 つのメソッドを同時に実行して、2 つの独立したループを実行する単純な「大雑把な」方法でした。とにかくここにコードがあります:

    class MyClass extends Thread {

public void run() {     // start new thread
    otherMethod();      // will calls another method
}
void otherMethod() {
     ....  executed in another Thread
}

public static void main(String[] args) {
   MyClass mc = new MyClass();
   mc.start();                 // start other Thread
   ... continue in main() with actual thread
   ...
}
}
于 2012-09-11T17:28:29.320 に答える