import java.io.*; // Импорт используемых пакетов import java.net.*; import java.util.*; /** * Это приложение реализует протокол передачи информации об акциях, * выдавая эту информацию клиентам. * Sauthor David W. Baker * Автор Дэвид Бейкер * 0version 1.1 * Версия 1.1 */ 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 = null; private String[] stockInfo; private Date stockInfoTime; private long stockFileMod; // Флаг, сброс которого приводит // к завершению работы сервера. private boolean keepRunning = true; /** * Запуск приложения. * Эрагага args Ignored command line arguments. * args - параметры командной строки - игнорируются */ public static void main(String[] args) { StockQuoteServer server = new StockQuoteServer(); server.serveQuotes(); } /** * Конструктор загружает данные об акциях, * после чего сервер ожидает * запросов от клиентов. */ public StockQuoteServer() { // Загружает данные и при ошибке завершает работу. if (!loadQuotes()) System.exit(1); try { // Создать сокет. listenSocket = new ServerSocket(SERVER_PORT,MAX_CLIENTS); } catch(IOException excpt) { System.err.println("Onable to listen on port " + // "Невозможно слушать порт ..." SERVER_PORT + ": " + excpt); System.exit(1); } } /** * Загрузка данных из файла. */ protected boolean loadQuotes() { String fileLine; StringBuffer inputBuffer = new StringBuffer(); int numStocks = 0; try { // Создать поток для чтения файла данных. DataInputStream stockInput = new DataInputStream( new FileInputStream(STOCK_QUOTES_FILE)); // Считать каадую строку. while ((fileLine = stockInput.readLine()) != null) { // Поместить строку в буфер. inputBuffer.append(fileLine + "\n"); numStocks++; // увеличить счетчик. } stockInput.close(); // Сохранить время последней модификации. stockFileMod = STOCK_QUOTES_FILE.lastModified(); } catch(FileNotFoundException excpt) { System.err.println("Unable to find file: " + excpt); // "Не найден файл" return false; } catch(IOException excpt) { System.err.println("Failed I/O: " + excpt); // "Ошибка ввода/вывода" return false; } // Создать массив строк для чтения информации из файла. stockInfo = new String[numStocks]; String inputString = inputBuffer.toString(); // Указатели для создания подстрок.. int stringStart = 0, // начало строки stringEnd = 0; // конец строки for (int index = 0; index < numStocks; index ++) { // Найти конец строки. stringEnd = inputString.indexOf("\n",stringStart); // Если символ \n больше не встретился, // взять весь остаток inputString. if (stringEnd == -1) { stockInfo[index] = inputString.substring(stringStart); // иначе взять подстроку до символа \n } else { stockInfo[index] = inputString.substring(stringStart,stringEnd); } // передвинуть указатель начала подстроки. stringStart = stringEnd + 1; } stockInfoTime = new Date(); // сохранить время загрузки, return true; } /** * Этот метод ожидает обращений от клиентов */ public void serveQuotes() { Socket clientSocket = null; try { while(keepRunning) { // Присоединить нового клиента. clientSocket = listenSocket.accept(); // Если файл данных изменен, // загрузить данные повторно. if (stockFileMod != STOCK_QUOTES_FILE.lastModified()) { loadQuotes(); } // Создать новый обработчик. StockQuoteHandler newHandler = new StockQuoteHandler(clientSocket,stockInfo, stockInfoTime); Thread newHandlerThread = new Thread(newHandler); newHandlerThread.start(); } listenSocket.close(); } catch(IOException excpt) { System.err.println("Failed I/O: "+ excpt); // "Ошибка ввода/вывода" } } /** * Метод для остановки сервера. */ protected void stop() { 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; /** * Конструктор инициализирует переменные экземпляра * @param newSocket Socket to the incoming client. * newSocket - сокет для связи с клиентом * 8param info The stock data. * info - данные об акциях * 8param time The time when the data was loaded. * time - время загрузки информации */ 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() ; // команда QUIT. if (nextLine.indexOf("QOIT") == 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("+BYE"); clientSend.flush(); } catch(IOException excpt) { System.err.println("Failed I/O: " + excpt); // "Ошибка ввода/вывода" // Наконец, закрыть потоки и сокет. } finally { try { if (clientSend != null) clientSend.close(); if (clientReceive != null) clientReceive.close(); if (mySocket != null) mySocket.close(); } catch(IOException excpt) { System.err.println("Failed I/O: " + excpt); // "Ошибка ввода/вывода" } } } /** * Метод для нахождения информации * по заданному идентификатору акции. * Эрагат quoteID The stock ID to look up. * quoteID - идентификатор акции * Sreturn The releveant data. * возвращаемое значение - данные по акции */ 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"; } }