|
Прохожу курс "Построение распределенных систем на Java" в третьей лекции где описывается TCPServer вылетает эта ошибка
"Connection cannot be resolved to a type" Java version 1.7.0_05 |
Пример использования API java.net
Второй пример
Рассмотренный в предыдущем примере способ взаимодействия имеет очень существенный недостаток, который происходит от выбранного нами протокола взаимодействия между клиентом и сервером (что еще раз подчеркивает важность правильного выбора протокола). Этот недостаток состоит в том, что наш протокол полностью не защищен от ошибок приложения. Предположим, например, что наш клиент при передаче операции заведения новой карты вместо того, чтобы передать три параметра (код операции, имя клиента и номер карты), случайно передал четыре - код операции, имя клиента, фамилия клиента и номер карты (такое возможно в случае ошибки программиста, реализующего приложение-клиент, или в случае появления новой, расширенной версии приложения). Поскольку сервер ничего об этом не знает, он считает из потока три параметра, а четвертый (номер карты) будет интерпретирован как код следующей операции. Таким образом, использование явной передачи параметров методов может приводить к трудноуловимым ошибкам.
Попробуем модифицировать наше приложение, чтобы избавиться от этого недостатка. Идея состоит в том, чтобы группировать передаваемые данные некоторым образом так, чтобы даже в случае изменения их структуры не происходило их перемешивания.
Мы могли бы модифицировать протокол из предыдущего примера, введя в него разделители команд или предусмотрев первоначальный обмен клиента и сервера метаданными, описывающими передаваемые объекты - существует масса способов достичь желаемого результата. Однако, поскольку приложение написано на языке Java, разумно воспользоваться средствами, уже встроенными в эту платформу. Речь идет о сериализации объектов.
В самом деле, рассмотрим еще раз нашу задачу. Все, что необходимо, - реализовать механизм обмена структурами данных (или объектами) между клиентом и сервером. И встроенный механизм сериализации в этом случае нам идеально подходит. Мы будем передавать по сети не отдельные "поля", а целые объекты.
Первое, что необходимо сделать, - выделить эти "транспортные" объекты. В нашем случае таких объектов будет два - объект "карта" и объект "операция над картой". Поскольку в нашем примере рассматривается система, выполняющая очень небольшое число действий, все операции системы можно свести к двум типам - операции, в которых аргументом является карта целиком, и операции, в которых в качестве аргумента выступает начисление/списание средств с карты. К первому типу относятся операции заведения новой карты и операция получения баланса карты (баланс мы будем считать ее атрибутом), ко второму - операции пополнения баланса и расчета.
Класс CardOperation
Первый класс, который мы реализуем, - класс "операция над картой" (пример 4.4).
1 package com.asw.net.ex2;
2 import java.util.*;
3 import java.io.*;
4
5
6 public class CardOperation implements Serializable {
7 public CardOperation(String card,double amount,Date operationDate){
8 this.card = card;
9 this.amount = amount;
10 this.operationDate = operationDate;
11 }
12 public String card;
13 public double amount;
14 public Date operationDate;
15 }
Листинг
4.4.
Транспортный класс CardOperation
Первое, что стоит отметить: класс CardOperation объявлен реализующим интерфейс Serializable.Этот интерфейс является "тэгирующим" - он не содержит полей или методов и служит только для того, чтобы сообщить о том, что данный класс сериализуем.
Класс содержит три поля, которые и составляют его полезное наполнение - номер карты, сумма начисляемых/списываемых средств и дата операции.
Класс Card
Второй транспортный класс представляет собой представление карты и выглядит следующим образом (пример 4.5).
1 package com.asw.net.ex2;
2 import java.io.Serializable;
3 import java.util.*;
4
5 public class Card implements Serializable{
6 public Card(String person, Date createDate, String cardNumber,double balance){
7 this.person = person;
8 this.createDate = createDate;
9 this.cardNumber = cardNumber;
10 this.balance = balance;
11 }
12 public String person;
13 public transient Date createDate;
14 public String cardNumber;
15 public double balance;
16 public String toString(){
17 return "Card: cardNumber="+cardNumber+"\tBalance="+balance+"\tPerson="+person+"\tCreateDate="+createDate+"";
18 }
19 }
Листинг
4.5.
Транспортный класс Card
Поскольку его тоже предстоит передавать по сети, он также объявлен как сериализуемый и содержит следующие поля: ФИО клиента, дату заведения карты, номер карты и текущий баланс. Для удобства вывода на экран перекрыт метод toString(), который распечатывает значения всех полей класса.
Перейдем теперь к серверным классам.
Класс BillingService
Первый из серверных классов - BillingService (пример 4.6).
1 package com.asw.net.ex2;
2 import java.net.*;
3 import java.util.Hashtable;
4 import java.io.*;
5
6 public class BillingService extends Thread{
7 private int serverPort = 7896;
8 private ServerSocket ss;
9 private Hashtable hash;
10
11 public static void main(String[] args) {
12 BillingService bs = new BillingService();
13 bs.start();
14 }
15
16 public BillingService(){
17 hash = new Hashtable();
18 }
19
20 public void run(){
21 try {
22 ss = new ServerSocket(serverPort);
23 System.out.println("Server started");
24 while(true){
25 System.out.println("new client waiting...");
26 Socket s = ss.accept();
27 System.out.println("Client accepted");
28 BillingClientService bcs = new BillingClientService(this,s);
29 System.out.println("bcs created");
30 bcs.start();
31 }
32 } catch (IOException e) {
33 e.printStackTrace();
34 }
35
36 }
37
38 public void addNewCard(Card card) {
39 hash.put(card.cardNumber, card);
40 }
41 public void addMoney(String card, double money) {
42 Card c = (Card)hash.get(card);
43 if (c==null) {
44 System.out.println("Bad Card number\n");
45 return;
46 };
47 c.balance+=money;
48 hash.put(card,c);
49 }
50 public Card getCard(String card){
51 return (Card)hash.get(card);
52 }
53 }
Листинг
4.6.
Серверный класс BillingService
Несложно заметить, что он очень похож на BillingService из предыдущего примера. И действительно, он точно так же хранит хэш-таблицу карт, имеет методы для обработки соответствующих событий - добавления новой карты, запроса баланса карты и обработки операций изменения баланса. Основной цикл run (строки 21-32) тоже точно такой же - сначала создается серверный сокет, затем ожидаются соединения клиентов. При появлении нового клиента создается экземпляр класса BillingClientService (строка 28), который и занимается всем дальнейшим обслуживанием клиента.
Однако есть и отличие - теперь в качестве элемента хранения в хэш-таблице используются экземпляры класса Card (ключом по-прежнему выступает ее номер), а метод getCard,заменивший метод getCardBalance,возвращает не скалярное значение, а экземпляр класса Card.