package com.javachat.client;
import com.javachat.shared.Command;
import com.javachat.shared.Logger;
import com.javachat.shared.MessageHolder;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
* Java chat клиент
*
* @author Dmitry Levykin
*/
public class ChatClient {
// Кодировка сообщений
public static final String CHARSET_NAME = "UTF-8";
// Максимальный размер одного сообщения 1 Кб
private static final int MESSAGE_LIMIT = 1024;
// Буфер приема 1 Кб
private final ByteBuffer buffer = ByteBuffer.allocate(1024);
// Буфер для хранения частей сообщений
private ByteBuffer endBuffer = null;
// Накопитель отдельного сообщения
private MessageHolder holder;
// Флаг запуска и подключения
private boolean start = false;
// Канал связи
private SocketChannel channel;
// Поток ввода текста
private InputStream inputStream;
//
private BufferedReader bufferedReader;
// Имя клиента
private String userName;
// Порт по-умолчанию
private Integer port = 3000;
// Хост по-умолчанию
private String host = "localhost";
/**
* Java chat клиент
*
* @param userName Имя пользователя, может быть null
* @param inputStream Поток ввода
*/
public ChatClient(String userName, String host, Integer port, InputStream inputStream) {
if (inputStream == null) {
throw new IllegalArgumentException("InputStream is not set!");
}
this.userName = userName;
this.host = host == null ? this.host : host;
this.port = port == null ? this.port : port;
this.inputStream = inputStream;
}
public static void main(String[] args) throws InterruptedException {
Logger.info("Java chat client.");
// DEBUG OFF
Logger.setDebugMode(false);
String userName = null;
String host = null;
Integer port = null;
// Параметры приложения
if (args != null && args.length != 0 && args.length % 2 == 0) {
for (int i = 0; i < args.length / 2; i++) {
String key = args[i * 2];
String value = args[i * 2 + 1];
switch (key) {
case "-n":
userName = value;
Logger.debug("Parameter user name is %s.", userName);
break;
case "-h":
host = value;
Logger.debug("Parameter host is %s.", host);
break;
case "-p":
port = Integer.valueOf(value);
Logger.debug("Parameter port is %d.", port);
break;
}
}
} else {
Logger.info("Optional parameters: \n" +
"-n User\n" +
"-h Host\n" +
"-p Port\n");
}
// Запуск клиента с вводом из консоли
new ChatClient(userName, host, port, System.in).start();
}
/**
* Отключение и остановка потока-слушателя сокета
*/
void shutdown() {
if (!start) {
return;
}
start = false;
try {
if (channel != null) {
channel.close();
}
} catch (IOException e) {
// Ignore
}
}
/**
* Создание канала связи
*/
void createChannel() throws IOException {
channel = SocketChannel.open();
channel.connect(new InetSocketAddress(host, port));
channel.configureBlocking(false);
}
/**
* Отправка сообщения на сервер
*/
void sendMessage(String message) throws IOException {
sendMessage(message.getBytes(CHARSET_NAME));
}
/**
* Отправка массива байт сообщения на сервер
*/
void sendMessage(byte[] messageBytes) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(messageBytes.length + 4);
buffer.putInt(messageBytes.length);
buffer.put(messageBytes);
buffer.flip();
channel.write(buffer);
}
/**
* Установка ввода
*/
void setInputStream(InputStream inputStream) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
}
/**
* Запуск основного цикла клиента
*/
void start() throws InterruptedException {
if (start) {
return;
}
Logger.debug("Try connect to %s:%d.", host, port);
try {
createChannel();
start = true;
Logger.debug("Connect success.");
setInputStream(inputStream);
// Авторизация, если параметр задан
if (userName != null) {
sendMessage(Command.LOGIN.getText() + userName);
}
while (start && !channel.socket().isClosed()) {
// Неблокирующий ввод из inputStream
if (bufferedReader.ready()) {
String message = bufferedReader.readLine();
sendMessage(message);
// Обработка команды выхода
if (message.trim().equals(Command.QUIT.getText())) {
start = false;
}
}
// Чтение сообщений с сервера
if (onRead() == null) {
Thread.sleep(200);
}
}
} catch (IOException e) {
Logger.info("Client start error: " + e.getMessage() + ".");
// Остановка
shutdown();
}
}
/**
* Получение и обработка сообщений с севера
*/
String onRead() throws IOException {
if (holder == null) {
holder = new MessageHolder(MESSAGE_LIMIT, CHARSET_NAME);
}
if (endBuffer != null && endBuffer.hasRemaining()) {
buffer.put(endBuffer);
}
if (channel.read(buffer) == -1) {
throw new IOException("End of server stream!");
}
buffer.flip();
String message = holder.readFromBuffer(buffer);
if (message != null) {
Logger.info(message);
holder = null;
}
if (buffer.hasRemaining()) {
endBuffer = ByteBuffer.allocate(buffer.remaining());
endBuffer.put(buffer);
endBuffer.flip();
}
buffer.clear();
return message;
}
}