.
Java для чайников. Урок 33. Клиент серверное приложение.
Автор megabax   
07.01.2020 г.
В этой статье я постараюсь максимально просто и понятно изложить основы программирования на языке Delphi

Java для чайников. Урок 33. Клиент серверное приложение.

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

import java.io.*;

import java.net.*;

import java.util.*;

 

 

public class StockQuoteServer {

    private static final int SERVER_PORT=1701; //порт, который будет прослушиваться сервером

    private static final int MAX_CLIENTS=50; //максимальное количество подключенных клиентов

    private static final File STOCK_QUOTES_FILE=new File("stockquotes.txt"); //файл с данными

    private ServerSocket listenSocket; //сокет

    private String[] stockInfo;

    private Date stockInfoTime;

    private long stockFileMode;

    private boolean keepRunning=true; //флаг, сброс которого приводит к завершению работы сервера

   

    //запуск приложения

    public static void main(String args[]) {

        StockQuoteServer server = new StockQuoteServer();

        server.serverQuotes(); 

    }

   

    //Конструктор загружает данные об акциях, после чего сервер

    //ожидает запросы от клиентов

    public StockQuoteServer()

    {

        if(!loadQuotes()) System.exit(1); //загружает данные, если ошибка - завершает работу

        try

        {

            listenSocket=new ServerSocket(SERVER_PORT, MAX_CLIENTS); //создать сокет

        } 

        catch(IOException except)

        {

            System.err.println("Unable to listen on port "+SERVER_PORT);

            System.exit(1);

        }

    }

   

    //загрузка данных из файла

    protected boolean loadQuotes()

    {

        String fileLine;

        StringBuffer inputBuffer=new StringBuffer();

        int numSocks=0;

        try

        {

            //создать поток для чтения данных из файла

            DataInputStream stockInput=new DataInputStream(new FileInputStream(STOCK_QUOTES_FILE));

           

            //считаем каждую строку

            while((fileLine=stockInput.readLine())!=null)

            {

                inputBuffer.append(fileLine.toUpperCase()+"\n"); //поместить строку в буфер

                numSocks++; //увеличить счетчик

            }

            stockInput.close();

           

            //сохранить время последней модификации

            stockFileMode=STOCK_QUOTES_FILE.lastModified();

           

        } catch(FileNotFoundException except)

        {

            System.err.println("Unable to find file: "+except);

            return false;

        } catch (IOException except)

        {

            System.err.println("I/O exception: "+except);

            return false;          

        }

       

        stockInfo=new String[numSocks]; //создадим массив строк для чтения инфы из файла

        String inputString=inputBuffer.toString();

       

        //указатели для создания подстрок

        int stringStart=0; //начало строки

        int stringEnd=0;  //конец строки

               

        for(int index=0; index<numSocks; index++)

        {

            stringEnd=inputString.indexOf("\n",stringStart);

           

            //если \n больше не встречается, то весь остаток inputString

            if(stringEnd==-1)

            {

                stockInfo[index]=inputString.substring(stringStart);

            }

            else

            {

                stockInfo[index]=inputString.substring(stringStart, stringEnd);

            }

           

            stringStart=stringEnd+1;  //передвинем указатель на следующую строку

        }

       

        stockInfoTime=new Date(); //сохраним время загрузки

        return true;       

    }

   

    //Этот метод ожидает обращение от клиента

    public void serverQuotes()

    {

        Socket clientSocket=null;

        try

        {

            while(keepRunning)

            {

                clientSocket=listenSocket.accept(); //присоединить нового клиента

               

                //Если файл данных изменен, загрузить данные повторно

                if(stockFileMode!=STOCK_QUOTES_FILE.lastModified())

                {

                    loadQuotes();

                }

               

                //Создать новый обработчик

                StockQuoteHandler newHandler = new StockQuoteHandler(clientSocket, stockInfo, stockInfoTime);            

                Thread newHandlerThread=new Thread(newHandler);

                newHandlerThread.start();

               

            }

            listenSocket.close();

        } catch(IOException e)

        {

            System.err.println("Failed I/O "+e);

        }

    }

   

    //метод для остановки сервера

    protected void stp()

    {

        if(keepRunning)

        {

            keepRunning=false;

        }      

    }

}

 

//Класс для обеспечения связи с отдельным клиентом

class StockQuoteHandler implements Runnable

{

    private Socket mySocket=null;

    private PrintStream clientSend=null;

    private DataInputStream clientReceive=null;

    private String[] stockInfo;

    private Date stockInfoTime;

   

    //конструктор

    public StockQuoteHandler(Socket newSocket, String[] info, Date time)

    {

        mySocket=newSocket;

        stockInfo=info;

        stockInfoTime=time;

    }

   

    //поток, релизующий обмен данными

    public void run()

    {

        String nextLine;

        String quoteID;

        String quoteResponse;

        try

        {

            clientSend=new PrintStream(mySocket.getOutputStream());

            clientReceive=new DataInputStream(mySocket.getInputStream());

            clientSend.println("+HELLO "+stockInfoTime);

            clientSend.flush();

           

            //получить строку от клиента и ответить

            while((nextLine=clientReceive.readLine())!=null)

            {

                nextLine=nextLine.toUpperCase();

               

                //команды выхода

                if(nextLine.indexOf("QUIT")==0) break;

               

                //команда STOCK

                else if(nextLine.indexOf("STOCK: ")==0)

                {                  

                    quoteID=nextLine.substring("STOCK: ".length());

                    quoteResponse=getQuote(quoteID);

                    clientSend.println(quoteResponse);

                    clientSend.flush();

                }

               

                //неизвестная команда

                else

                {

                    clientSend.println("-ERR UNKNOWN COMMAND");

                    clientSend.flush();

                }

            }

            clientSend.println("+BUY");

            clientSend.flush();

        } catch(IOException e)

        {

            System.err.println("Failed I/O "+e);

        } finally

        {

            //закроем потоки и сокет

            try

            {

                if(clientSend!=null) clientSend.close();

                if(clientReceive!=null) clientReceive.close();

                if(mySocket!=null) mySocket.close();

            } catch(IOException e)

            {

                System.err.println("Failed I/O "+e);

            }

        }

    }

   

    //Получить информацию по заданному идентификатору

    protected String getQuote(String quoteID)

    {

        for(int index=0; index<stockInfo.length; index++)

        {

            if(stockInfo[index].indexOf(quoteID)==0) return "+"+stockInfo[index];

        }

        return "-ERR UNKNOWN STOCK ID";

    }

}

 

Эта программа читает данные о котировках из текстового файла примерно такого вида:

IBM 123
Apple 333
General_Motors 12

 

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

Теперь создадим клиента:

import java.io.*;

import java.net.*;

 

 

public class StockQuoteClient {

 

    private static final int SERVER_PORT=1701; //сервер слушает этот порт

    private String serverName; //имя сервера

    private Socket quoteSocket=null; //сокет

    private DataInputStream quoteReceive; //поток очереди получения

    private PrintStream quoteSend; //поток очереди отправки

    private String[] stockIDs; //массив идентификаторов акций

    private String[] stockInfo; //массив возвращаемых значений

    private String currentAsOf=null; //Время обновления данных

   

    /*

    Запуск приложения: проверка параметров комнадной строки, создание нужных объектов и прочее

    */

    public static void main(String args[]) {

        if(args.length<2)

        {

            System.out.println("Usage: StockQuoteClient <server> <stock ids>");

            System.exit(1);

        }

        StockQuoteClient client=new StockQuoteClient(args);

        client.printQuotes(System.out);

        System.exit(0);

    }

   

    //конструктор класса

    public StockQuoteClient(String[] args)

    {

        String serverInfo;

        serverName=args[0]; //первый параметр - имя сервера

       

        //размер массива на 1 меньше, чем args

        stockIDs = new String[args.length-1];

        stockInfo = new String[args.length-1];

       

        //скопируем аргументы в массив stocksIDs

        for(int index=1; index<args.length; index++)

        {

            stockIDs[index-1]=args[index];

        }

       

        serverInfo=contactServer(); //связаться с сервером и возвратить сообщение приветсвия

       

        //распарсить строку с временем

        if(serverInfo!=null)

        {

            currentAsOf=serverInfo.substring(serverInfo.indexOf(" ")+1);

        }

        qetQuotes(); //считать информацию

        quitServer(); //закрыть соединение

    }

   

    //соединиться с ервером

    protected String contactServer()

    {

        String serverWelcome=null; //приглашение сервера

        try

        {

            quoteSocket =  new Socket(serverName, SERVER_PORT); //открыть сокет на сервер

            quoteReceive = new DataInputStream(quoteSocket.getInputStream());

            quoteSend=new PrintStream(quoteSocket.getOutputStream());

            serverWelcome=quoteReceive.readLine(); //считать приглашение сервера

        } catch (IOException except)

        {

            System.err.println("Unknown host "+serverName+": "+except);

        }

        return serverWelcome; //вернуть приглашение сервера

    }

   

    //считать информацию об акциях

    protected void qetQuotes()

    {

        String response; //ответ на запрос

       

        //если соединение в порядке

        if(connectOK())

        {

            try

            {

                //цикл по всем акциям

                for(int index=0; index<stockIDs.length; index++)

                {

                    if(stockIDs[index].indexOf("+")==0)

                    {

                        quoteSend.println(stockIDs[index].substring(1,stockIDs[index].length())); //послать запрос

                    }

                    else

                    {

                        quoteSend.println("STOCK: "+stockIDs[index]); //послать запрос

                    }

                    response=quoteReceive.readLine(); //получить ответ

                    stockInfo[index]=response.substring(response.indexOf(" ")+1); //распарсить полученные данные

                   

                }

            } catch (IOException except)

            {

                System.err.println("Failed I/O to"+serverName+": "+except);

            }

        }

    }

   

    //отключение от сервера

    protected String quitServer()

    {

        String serverBuy=null; //сообщение отключения

        try

        {

            //если соединение в порядке

            if(connectOK())

            {  

                quoteSend.println("QUIT");

                serverBuy=quoteReceive.readLine();

            }

           

            //если ссылки не равны NULL, то надо закрыть потоки и сокет

            if(quoteSend!=null)  quoteSend.close();

            if(quoteReceive!=null) quoteReceive.close();

            if(quoteSocket!=null) quoteSocket.close();

           

        } catch (IOException except)

        {

            System.err.println("Failed I/O to "+serverName+": "+except);

        }

       

        return serverBuy;

    }

   

   

    protected void printQuotes(PrintStream sendOutput)

    {

        //если уже было сообщение приветствия

        if(currentAsOf!=null)

        {

            sendOutput.print("INFORMATION ON REQUESTED QUOTES\nCurrent As Of: "+currentAsOf+"\n");

               

            //цикл по всем акциям

            for(int index=0 ;index < stockIDs.length; index++)

            {

                sendOutput.println(stockIDs[index]+":");

                if(stockInfo[index]!=null) sendOutput.println(" "+stockInfo[index]); else sendOutput.println();

            }

        }

    }

   

    //проверить, что с соединением все в порядке

    protected boolean connectOK()

    {

        return (quoteSend!=null && quoteReceive!=null && quoteSocket!=null);

    }

}

 

Итак, компилируем сервер, компилируем клиента. Запускаем сервер. Запускаем клиента, указывая в командной строке сначала хотс, а потом команды серверу, например, вот так:

"C:\Program Files (x86)\Java\jdk1.7.0_45\bin\java.exe" StockQuoteClient localhost IBM Apple +QUIT

 

В ответ вы увидите экран программы:

 

Java для чайников. Урок 33. Клиент серверное приложение.

 

 

(С) Шуравин Александр

 

 

Последнее обновление ( 07.01.2020 г. )