Skip to content

Commit 5d2af18

Browse files
authored
what does that have to do with you and me?
Jesus said to her, "Woman, what does that have to do with you and me? My hour has not yet come." (John 2:4)
1 parent 6474377 commit 5d2af18

1 file changed

Lines changed: 286 additions & 0 deletions

File tree

  • Multithreading/task26/task3008/Chat-22/client
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
2+
//Jesus said to her, "Woman, what does that have to do with you and me? My hour has not yet come." (John 2:4)
3+
4+
package com.javarush.task.task30.task3008.client;
5+
6+
import java.io.IOException;
7+
import java.net.Socket;
8+
import com.javarush.task.task30.task3008.ConsoleHelper;
9+
import com.javarush.task.task30.task3008.Message;
10+
import com.javarush.task.task30.task3008.MessageType;
11+
import com.javarush.task.task30.task3008.Connection;
12+
13+
public class Client extends Thread {
14+
protected Connection connection;
15+
private volatile boolean clientConnected;
16+
17+
public static void main(String[] args) {
18+
Client client = new Client();
19+
client.run();
20+
}
21+
22+
public void run() {
23+
{
24+
SocketThread socketThread = getSocketThread();
25+
socketThread.setDaemon(true);
26+
socketThread.start();
27+
try {
28+
synchronized (this) {
29+
wait();
30+
}
31+
} catch (InterruptedException e) {
32+
ConsoleHelper.writeMessage("Thread error");
33+
System.exit(1);
34+
}
35+
if (clientConnected) {
36+
ConsoleHelper.writeMessage("Connection established. To quit, type 'exit'");
37+
while (clientConnected) {
38+
String message = ConsoleHelper.readString();
39+
if (message.equalsIgnoreCase("exit")) {
40+
break;
41+
} else {
42+
if (shouldSendTextFromConsole()) {
43+
sendTextMessage(message);
44+
}
45+
}
46+
}
47+
} else {
48+
ConsoleHelper.writeMessage("Error at client side");
49+
}
50+
}
51+
}
52+
53+
protected String getServerAddress() {
54+
String serverAddress;
55+
ConsoleHelper.writeMessage("Enter server address: ");
56+
serverAddress = ConsoleHelper.readString();
57+
return serverAddress;
58+
}
59+
60+
protected int getServerPort() {
61+
ConsoleHelper.writeMessage("Enter server port: ");
62+
return ConsoleHelper.readInt();
63+
}
64+
65+
protected String getUserName() {
66+
ConsoleHelper.writeMessage("Enter User name: ");
67+
return ConsoleHelper.readString();
68+
}
69+
70+
protected boolean shouldSendTextFromConsole() {
71+
return true;
72+
}
73+
74+
protected SocketThread getSocketThread() {
75+
return new SocketThread();
76+
}
77+
78+
protected void sendTextMessage(String text) {
79+
try {
80+
connection.send(new Message(MessageType.TEXT, text));
81+
} catch (IOException e) {
82+
ConsoleHelper.writeMessage("Error, can't send your message...");
83+
clientConnected = false;
84+
}
85+
}
86+
87+
public class SocketThread extends Thread {
88+
89+
public void run() {
90+
String serverAddress = getServerAddress();
91+
int serverPort = getServerPort();
92+
try {
93+
Socket socket = new Socket(serverAddress,serverPort);
94+
connection = new Connection(socket);
95+
clientHandshake();
96+
clientMainLoop();
97+
98+
} catch (IOException | ClassNotFoundException e) {
99+
notifyConnectionStatusChanged(false);
100+
}
101+
}
102+
103+
protected void processIncomingMessage(String message) {
104+
ConsoleHelper.writeMessage(message);
105+
}
106+
107+
protected void informAboutAddingNewUser(String userName) {
108+
ConsoleHelper.writeMessage("User " + userName + " joined to chat");
109+
}
110+
111+
protected void informAboutDeletingNewUser(String userName) {
112+
ConsoleHelper.writeMessage("User " + userName + " has left this chat");
113+
}
114+
115+
protected void notifyConnectionStatusChanged(boolean clientConnected) {
116+
synchronized (Client.this) {
117+
Client.this.clientConnected = clientConnected;
118+
Client.this.notify();
119+
}
120+
}
121+
122+
protected void clientHandshake() throws IOException, ClassNotFoundException {
123+
Message message;
124+
125+
while (!clientConnected) {
126+
try {
127+
message = connection.receive();
128+
} catch (ClassNotFoundException e) {
129+
throw new IOException("Unexpected MessageType");
130+
}
131+
if (message.getType() == MessageType.NAME_REQUEST) {
132+
connection.send(new Message(MessageType.USER_NAME, getUserName()));
133+
} else {
134+
if (message.getType() == MessageType.NAME_ACCEPTED) {notifyConnectionStatusChanged(true);}
135+
else throw new IOException("Unexpected MessageType");}
136+
137+
}
138+
}
139+
140+
protected void clientMainLoop() throws IOException, ClassNotFoundException {
141+
Message message;
142+
143+
while (true) {
144+
145+
try {
146+
message = connection.receive();
147+
} catch (Exception e) {
148+
break;
149+
}
150+
if (message.getType() == MessageType.TEXT) processIncomingMessage(message.getData());
151+
else {
152+
if (message.getType() == MessageType.USER_ADDED) informAboutAddingNewUser(message.getData());
153+
else {
154+
if (message.getType() == MessageType.USER_REMOVED) informAboutDeletingNewUser(message.getData());
155+
else break;
156+
}
157+
}
158+
159+
}
160+
throw new IOException("Unexpected MessageType");
161+
162+
}
163+
}
164+
}
165+
166+
/*
167+
Чат (22)
168+
Итак, подведем итог:
169+
• Ты написал сервер для обмена текстовыми сообщениями.
170+
• Ты написал консольный клиент, который умеет подключаться к серверу и
171+
обмениваться сообщениями с другими участниками.
172+
• Ты написал бот клиента, который может принимать запросы и отправлять данные о
173+
текущей дате и времени.
174+
• Ты написал клиента для чата с графическим интерфейсом.
175+
176+
Что можно добавить или улучшить:
177+
• Можно добавить поддержку приватных сообщений (когда сообщение отправляется не
178+
всем, а какому-то конкретному участнику).
179+
• Можно расширить возможности бота, попробовать научить его отвечать на
180+
простейшие вопросы или время от времени отправлять шутки.
181+
• Добавить возможность пересылки файлов между пользователями.
182+
• Добавить контекстное меню в графический клиент, например, для отправки
183+
приватного сообщения кому-то из списка участников.
184+
• Добавить раскраску сообщений в графическом клиенте в зависимости от отправителя.
185+
• Добавить блокировку сервером участников за что-либо, например, ненормативную
186+
лексику в сообщениях.
187+
• Добавить еще миллион фич и полезностей!
188+
189+
Ты научился:
190+
• Работать с сокетами.
191+
• Пользоваться сериализацией и десериализацией.
192+
• Создавать многопоточные приложения, синхронизировать их, применять модификатор
193+
volatile, пользоваться классами из библиотеки java.util.concurrent.
194+
• Применять паттерн MVC.
195+
• Использовать внутренние и вложенные классы.
196+
• Работать с библиотекой Swing.
197+
• Применять классы Calendar и SimpleDateFormat.
198+
199+
Так держать!
200+
201+
202+
Требования:
203+
1. Поздравляю, чат готов!
204+
Чат (21)
205+
У меня есть отличнейшая новость для тебя. Компонент представление (View) уже готов. Я добавил класс ClientGuiView. Он использует библиотеку javax.swing. Ты должен как следует разобраться в каждой строчке этого класса. Если тебе все понятно – это замечательно, если нет – обязательно найди ответы на свои вопросы с помощью дебага, документации или поиска в Интернет.
206+
207+
Осталось написать компонент контроллер (Controller):
208+
1. Создай класс ClientGuiController унаследованный от Client.
209+
2. Создай и инициализируй поле, отвечающее за модель ClientGuiModel model.
210+
3. Создай и инициализируй поле, отвечающее за представление ClientGuiView view. Подумай, что нужно передать в конструктор при инициализации объекта.
211+
4. Добавь внутренний класс GuiSocketThread унаследованный от SocketThread. Класс GuiSocketThread должен быть публичным. В нем переопредели следующие методы:
212+
а) void processIncomingMessage(String message) – должен устанавливать новое сообщение у модели и вызывать обновление вывода сообщений у представления.
213+
б) void informAboutAddingNewUser(String userName) – должен добавлять нового пользователя в модель и вызывать обновление вывода пользователей у отображения.
214+
в) void informAboutDeletingNewUser(String userName) – должен удалять пользователя из модели и вызывать обновление вывода пользователей у отображения.
215+
г) void notifyConnectionStatusChanged(boolean clientConnected) – должен вызывать аналогичный метод у представления.
216+
5. Переопредели методы в классе ClientGuiController:
217+
а) SocketThread getSocketThread() – должен создавать и возвращать объект типа GuiSocketThread.
218+
б) void run() – должен получать объект SocketThread через метод getSocketThread() и вызывать у него метод run(). Разберись, почему нет необходимости вызывать метод run в отдельном потоке, как мы это делали для консольного клиента.
219+
в) getServerAddress(), getServerPort(), getUserName(). Они должны вызывать одноименные методы из представления (view).
220+
6. Реализуй метод ClientGuiModel getModel(), который должен возвращать модель.
221+
7. Реализуй метод main(), который должен создавать новый объект ClientGuiController и вызывать у него метод run().
222+
Запусти клиента с графическим окном, нескольких консольных клиентов и убедись, что
223+
все работает корректно.
224+
225+
Чат (20)
226+
Консольный клиент мы уже реализовали, чат бота тоже сделали, почему бы не сделать клиента с графическим интерфейсом? Он будет так же работать с нашим сервером, но иметь графическое окно, кнопки и т.д.
227+
Итак, приступим. При написании графического клиента будет очень уместно воспользоваться паттерном MVC (Model-View-Controller). Ты уже должен был с ним сталкиваться, если необходимо, освежи свои знания про MVC с помощью Интернет. В нашей задаче самая простая реализация будет у класса, отвечающего за модель (Model).
228+
229+
Давай напишем его:
230+
1) Создай класс ClientGuiModel в пакете client. Все классы клиента должны быть созданы в этом пакете.
231+
2) Добавь в него множество(set) строк в качестве final поля allUserNames. В нем будет храниться список всех участников чата. Проинициализируй его.
232+
3) Добавь поле String newMessage, в котором будет храниться новое сообщение, которое получил клиент.
233+
4) Добавь геттер для allUserNames, запретив модифицировать возвращенное множество. Разберись, как это можно сделать с помощью метода класса Collections.
234+
5) Добавь сеттер и геттер для поля newMessage.
235+
6) Добавь метод void addUser(String newUserName), который должен добавлять имя участника во множество, хранящее всех участников.
236+
7) Добавь метод void deleteUser(String userName), который будет удалять имя участника из множества.
237+
238+
Чат (19)
239+
Сейчас будем реализовывать класс BotSocketThread, вернее переопределять некоторые его методы, весь основной функционал он уже унаследовал от SocketThread.
240+
241+
1. Переопредели метод clientMainLoop():
242+
а) С помощью метода sendTextMessage() отправь сообщение с текстом «Привет чатику. Я бот. Понимаю команды: дата, день, месяц, год, время, час, минуты, секунды.»
243+
б) Вызови реализацию clientMainLoop() родительского класса.
244+
2. Переопредели метод processIncomingMessage(String message). Он должен следующим образом обрабатывать входящие сообщения:
245+
а) Вывести в консоль текст полученного сообщения message.
246+
б) Получить из message имя отправителя и текст сообщения. Они разделены «: «.
247+
в) Отправить ответ в зависимости от текста принятого сообщения.
248+
Если текст сообщения:
249+
«дата» – отправить сообщение содержащее текущую дату в формате «d.MM.YYYY«;
250+
«день» – в формате «d«;
251+
«месяц» — «MMMM«;
252+
«год» — «YYYY«;
253+
«время» — «H:mm:ss«;
254+
«час» — «H«;
255+
«минуты» — «m«;
256+
«секунды» — «s«.
257+
Указанный выше формат используй для создания объекта SimpleDateFormat. Для получения текущей даты необходимо использовать класс Calendar и метод getTime().
258+
Ответ должен содержать имя клиента, который прислал запрос и ожидает ответ, например, если Боб отправил запрос «время«, мы должны отправить ответ «Информация для Боб: 12:30:47«.
259+
Наш бот готов. Запусти сервер, запусти бота, обычного клиента и убедись, что все работает правильно.
260+
Помни, что message бывают разных типов и не всегда содержат «:«
261+
262+
Чат (18)
263+
Иногда бывают моменты, что не находится достойного собеседника. Не общаться же с самим собой :). Давай напишем бота, который будет представлять собой клиента, который автоматически будет отвечать на некоторые команды. Проще всего реализовать бота, который сможет отправлять текущее время или дату, когда его кто-то об этом попросит.
264+
265+
С него и начнем:
266+
1) Создай новый класс BotClient в пакете client. Он должен быть унаследован от Client.
267+
2) В классе BotClient создай внутренний класс BotSocketThread унаследованный от SocketThread. Класс BotSocketThread должен быть публичным.
268+
3) Переопредели методы:
269+
а) getSocketThread(). Он должен создавать и возвращать объект класса BotSocketThread.
270+
б) shouldSendTextFromConsole(). Он должен всегда возвращать false. Мы не хотим, чтобы бот отправлял текст введенный в консоль.
271+
в) getUserName(), метод должен генерировать новое имя бота, например: date_bot_X, где X – любое число от 0 до 99. Для генерации X используй метод Math.random().
272+
4) Добавь метод main. Он должен создавать новый объект BotClient и вызывать у него метод run().
273+
274+
Чат (17)
275+
Последний, но самый главный метод класса SocketThread – это метод void run(). Добавь его. Его реализация с учетом уже созданных методов выглядит очень просто.
276+
277+
Давай напишем ее:
278+
1) Запроси адрес и порт сервера с помощью методов getServerAddress() и getServerPort().
279+
2) Создай новый объект класса java.net.Socket, используя данные, полученные в предыдущем пункте.
280+
3) Создай объект класса Connection, используя сокет из п.17.2.
281+
4) Вызови метод, реализующий «рукопожатие» клиента с сервером (clientHandshake()).
282+
5) Вызови метод, реализующий основной цикл обработки сообщений сервера.
283+
6) При возникновении исключений IOException или ClassNotFoundException сообщи главному потоку о проблеме, используя notifyConnectionStatusChanged и false в качестве параметра.
284+
285+
Клиент готов, можешь запустить сервер, несколько клиентов и проверить как все работает.
286+
*/

0 commit comments

Comments
 (0)