Как остановить сервер в многопоточности Client Server - PullRequest
0 голосов
/ 06 сентября 2018

Я реализую многопоточное клиент-серверное приложение на Java. Я хочу реализовать JDBC в этой программе и хочу, чтобы мой сервер извлекал данные из базы данных при каждом ее запуске. Я буду хранить эти данные в моих collection экземплярах, выполнять манипуляции с данными, и когда сервер завершит выполнение, мне нужно будет сохранить данные обратно в базу данных. Проблема в том, что сервер находится в бесконечном цикле ожидания клиентов, и я не могу понять, как заставить сервер остановиться.

Это моя серверная программа:

import java.io.*;
import java.text.*;
import java.util.*;
import java.net.*;

public class Server 
{
    public static void main(String[] args) throws IOException 
    {
        // server is listening on port 5056
        ServerSocket ss = new ServerSocket(5056);

        // running infinite loop for getting
        // client request
        while (true) 
        {
            Socket s = null;

            try {
                // socket object to receive incoming client requests
                s = ss.accept();

                System.out.println("A new client is connected : " + s);

                // obtaining input and out streams
                DataInputStream dis = new DataInputStream(s.getInputStream());
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());

                System.out.println("Assigning new thread for this client");

                // create a new thread object
                Thread t = new ClientHandler(s, dis, dos);

                // Invoking the start() method
                t.start();
            }
            catch (Exception e) {
                s.close();
                e.printStackTrace();
            }
        }
    }
}

// ClientHandler class
class ClientHandler extends Thread 
{
    DateFormat fordate = new SimpleDateFormat("yyyy/MM/dd");
    DateFormat fortime = new SimpleDateFormat("hh:mm:ss");
    final DataInputStream dis;
    final DataOutputStream dos;
    final Socket s;


    // Constructor
    public ClientHandler(Socket s, DataInputStream dis, DataOutputStream dos) 
    {
        this.s = s;
        this.dis = dis;
        this.dos = dos;
    }

    @Override
    public void run() 
    {
        String received;
        String toreturn;
        while (true) {
            try {
                // Ask user what he wants
                dos.writeUTF("What do you want?[Date | Time]..\n"+
                        "Type Exit to terminate connection.");

                // receive the answer from client
                received = dis.readUTF();

                if(received.equals("Exit"))
                { 
                    System.out.println("Client " + this.s + " sends exit...");
                    System.out.println("Closing this connection.");
                    this.s.close();
                    System.out.println("Connection closed");
                    break;
                }

                // creating Date object
                Date date = new Date();

                // write on output stream based on the
                // answer from the client
                switch (received) {         
                    case "Date" :
                        toreturn = fordate.format(date);
                        dos.writeUTF(toreturn);
                        break;

                    case "Time" :
                        toreturn = fortime.format(date);
                        dos.writeUTF(toreturn);
                        break;

                    default:
                        dos.writeUTF("Invalid input");
                        break;
                }
            } 
            catch (IOException e) {
                e.printStackTrace();
            }
        }

        try
        {
            // closing resources
            this.dis.close();
            this.dos.close();            
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

Вот моя клиентская программа:

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

// Client class
public class Client 
{
    public static void main(String[] args) throws IOException 
    {
        try
        {
            Scanner scn = new Scanner(System.in);

            // getting localhost ip
            InetAddress ip = InetAddress.getByName("localhost");

            // establish the connection with server port 5056
            Socket s = new Socket(ip, 5056);

            // obtaining input and out streams
            DataInputStream dis = new DataInputStream(s.getInputStream());
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());

            // the following loop performs the exchange of
            // information between client and client handler
            while (true) 
            {
                System.out.println(dis.readUTF());
                String tosend = scn.nextLine();
                dos.writeUTF(tosend);

                // If client sends exit,close this connection 
                // and then break from the while loop
                if(tosend.equals("Exit"))
                {
                    System.out.println("Closing this connection : " + s);
                    s.close();
                    System.out.println("Connection closed");
                    break;
                }

                // printing date or time as requested by client
                String received = dis.readUTF();
                System.out.println(received);
            }

            // closing resources
            scn.close();
            dis.close();
            dos.close();
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Обзор

Отличный вопрос! Чтобы повторить то, что было сказано в вышеприведенных комментариях, вы ищете завершение работы на стороне сервера. Есть некоторый способ справиться с этой ситуацией, и я могу объяснить это на кратком примере.

ExecutorServer

Я рассмотрю модифицированный пример, основанный на этом примере. Ниже найдите реализацию сервера.

class NetworkService implements Runnable {
    private final ServerSocket serverSocket;
    private final ExecutorService pool;
    private final AtomicBoolean shouldExit;

    public NetworkService(int port, int poolSize) throws IOException {
        serverSocket = new ServerSocket(port);
        pool = Executors.newFixedThreadPool(poolSize);
        shouldExit = new AtomicBoolean(false); // Thread-safe boolean
    }

    public void run() { // run the service
        try {

           // While we should not exit
           while(!shouldExit.get()) {
             try {
                 pool.execute(new ClientHandler(serverSocket.accept()));
             } catch (SocketException e) {
                 if(shouldExit.get()) break; // Poison pill has been delivered, lets stop
                 // Error handling
             }
           }
        } catch (IOException ex) {
           pool.shutdown();
        }

       // Clean up the thread pool
       shutdownAndAwaitTermination();
    }
}

class ClientHandler implements Runnable {
    private final Socket socket;
    ClientHandler (Socket socket) { this.socket = socket; }
    public void run() {
        ...
    }
    ...
 }

Здесь вы измените свой текущий код Сервера, чтобы запугать эту структуру. У вас сейчас похожий макияж, но мы добавили ExecutorService.

Исполнитель, который предоставляет методы для управления завершением и методы, которые могут создать будущее для отслеживания хода выполнения одной или нескольких асинхронных задач.

Отправляя свой ClientHandler на ExecutorService, вы используете ThreadPool. Хотя это дает множество преимуществ, наиболее значительными из них являются то, что вы имеете больший контроль над многопоточным сервисом, ThreadPool будет управлять использованием потоков, а эффективность приложения значительно возрастет.

Ниже показано, как вы пытаетесь завершить работу всех завершающих потоков:

void shutdownAndAwaitTermination(ExecutorService pool) {
    pool.shutdown(); // Disable new tasks from being submitted
    try {
        // Wait a while for existing tasks to terminate
        if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
            pool.shutdownNow(); // Cancel currently executing tasks
            // Wait a while for tasks to respond to being cancelled
        if (!pool.awaitTermination(60, TimeUnit.SECONDS))
            System.err.println("Pool did not terminate");
        }
    } catch (InterruptedException ie) {
        // (Re-)Cancel if current thread also interrupted
        pool.shutdownNow();
        // Preserve interrupt status
        Thread.currentThread().interrupt();
    }
}

Теперь остается вопрос, как отключить сервер? Приведенный выше код показывает улучшенную структуру, но все еще имеет проблему блокировки на serverSocket.accept()!

Решение

Есть две идеи, которые приходят на ум, когда думаешь об этом сценарии; CLI или графический интерфейс. Оба имеют одинаковую семантику, и решение в конечном итоге остается за вами. Для целей объяснения я буду ссылаться на подход CLI.

Таблетка отравления

Если вы реализуете new Thread(), который обрабатывает все входящие команды из CLI, этот поток будет действовать как ядовитая таблетка . Идея состоит в том, чтобы доставить таблетку с ядом к цели так, чтобы она могла проснуться / выполнить и умереть. Поток изменит логическое значение shouldExit на true и создаст new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()).close(); для подключения к ServerSocket и немедленно закроет его. В приведенном выше коде приложение больше не будет блокировать serverSocket.accept(). Вместо этого он войдет в пробный улов для SocketExceptions и проверит, использовалась ли ядовитая таблетка; Если это было так, давайте очистим, если нет - разрешим ошибку.

Таймаут

Вы также можете установить тайм-аут на ServerSocket так, что он будет выдавать исключение каждый раз, когда не может установить соединение в этом интервале времени с myServer.setSoTimeout(2000);. Это выдаст InterruptedIOException и может быть обработано аналогично таблетке с ядом, где флаг изменяется с помощью команды CLI, и он проверяет, должен ли он выйти в блоке catch. Если это должно завершиться, давайте очистим, если нет - обработаем ошибку.

0 голосов
/ 06 сентября 2018

Вы можете использовать шаблон флага с логической переменной volatile, и вы должны поместить его в 'while' - когда обработка будет завершена, установите значение false, и сервер остановится.

Другой способ - использовать пулы потоков и дождаться их завершения в главном потоке вашего сервера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...