Java Threads - для двух циклов while

Я новичок, и я просмотрел учебники для этого, но до сих пор не знаю, как именно это реализовать.

У меня есть два цикла while, один в методе main() и один в методе send(), оба должны выполняться одновременно, как мне это сделать.

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
    }
}

Цикл while в sendDATA работает, читая 512 байтов из файла, а затем отправляя их в клиентский класс. В то время как цикл в основном методе получает пакеты от клиента и обновляет переменную, если переменная истинна, тогда sendDATA считывает следующие 512 байтов и отправляет их и так далее, но я не могу работать в двух потоках.

Я сделал это с одним циклом 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;

        }
    }


}

person Sterling Duchess    schedule 11.09.2012    source источник
comment
какая желаемая скорость чтения/записи в сокет?   -  person huseyin tugrul buyukisik    schedule 11.09.2012
comment
Это можно сделать в одном потоке. Почему вы хотите сделать это в два потока? Это требование выполнять чтение с клиента и отправку клиенту в двух отдельных потоках?   -  person Niraj Nawanit    schedule 11.09.2012
comment
Если вам нужно запустить два независимых цикла while(true) одновременно, вы должны использовать два потока. Я не вижу никакого пути вокруг этого.   -  person Martin James    schedule 11.09.2012
comment
В яблочко. Пока(правда) не нужен. Сначала прочитайте данные из файла. Затем отправьте данные клиенту. Затем прочитайте данные с клиента. Затем установите переменную. И затем, если переменная верна, повторите предыдущие шаги еще раз.   -  person Niraj Nawanit    schedule 11.09.2012
comment
Я сделал это с одним циклом while, но я написал такой сложный код, что никогда не получал последний пакет, который я отлаживал, и безуспешно. Мне было указано, что было бы намного чище, даже если не нужно использовать 2 потока по одному для каждого цикла, но я действительно не знаю, как это сделать, даже после просмотра руководств.   -  person Sterling Duchess    schedule 11.09.2012


Ответы (2)


Вы должны использовать блокирующий канал для синхронизации связи между вашими процессами:

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

Дополнительная информация: http://docs.oracle.com/javase/1.4.2/docs/api/java/nio/channels/SocketChannel.html

Кроме того, сокеты предназначены для отправки данных между процессами, если это не так, ваш подход неверен, и вам следует изменить свой дизайн, т.е. вызывать каждый цикл в новом потоке, когда (или каждый раз) данные для него готовы (каждый поток будет обрабатывать его данные, а затем умирать) или просто выполнять оба блока инструкций последовательно внутри одного цикла, если вам не нужно параллельное выполнение. Диаграмма состояний вашей программы может помочь вам определить, какое решение является лучшим.

person NotGaeL    schedule 11.09.2012
comment
Это решение, но действительно ли в этом случае нужны два потока? - person Denys Séguret; 11.09.2012
comment
Я написал свой код в одном цикле, но я продолжал терять последний пакет после нескольких часов отладки, пытаясь заставить его работать, я попросил помощи, и мне предложили использовать потоки, даже если это не нужно, это упростило бы код, по крайней мере, для меня. - person Sterling Duchess; 11.09.2012

Итак, я искал простой «грубый» способ одновременного запуска двух методов одного и того же класса для, например, двух независимых циклов. В любом случае вот код:

    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
   ...
}
}
person Sterling Duchess    schedule 11.09.2012