Chat Socket Baseado em Eventos
Tempos atrás encontrei um exercício sobre chat com sockets que me chamou a atenção. Achei interessante porque parece aqueles sistemas antigos, antes das aplicações com interface gráfica, que eram baseados apenas em comandos no modo console e quando consegui um tempinho livre resolvi tentar implementá-lo. Acabei não pensando muito qual seria a melhor maneira de fazer, apenas fui programando pouco a pouco, então com certeza depois que você conferir o código, terá novas idéias, tanto em relação a qualidade de código quanto a novas funções ou mesmo melhorando as já implementadas.
1. Exercício proposto
Faça uma aplicação Socket cliente-servidor baseada em eventos. Desta forma, clientes podem se subscrever a eventos de outros clientes, bem como publicar eventos que são divulgados a outros clientes que se subscreveram a este. Desta forma, a aplicação deverá atender aos seguintes requisitos:
- O cliente, ao iniciar sua aplicação, deverá se conectar no servidor. Para isso ele deverá digitar, por exemplo,
/connect 192.168.1.1
, sendo este o IP do servidor. - Caso o cliente ainda não tenha se registrado, ele deverá se registrar digitando o comando
/register
. O servidor deverá verificar se já não existe outro cliente com o mesmo login, e caso não, deverá armazenar de alguma forma as informações do cliente (com arquivos, por exemplo) e retornar uma mensagem indicando sucesso no registro. - Caso o cliente já possua registro, ele poderá se logar com
/login
. Por sua vez, o servidor deverá confirmar se o login ocorreu corretamente ou não. Se o login ocorreu de maneira correta, os eventos abaixo poderão ser utilizados, e caso contrário, não poderão. - Após logar, o cliente poderá publicar eventos, que no caso desta aplicação, tais eventos serão somente mensagens de texto. Para publicar um evento, o cliente deverá digitar
/public
. Após publicar tal evento, outros clientes que haviam se subscrito nos eventos deste cliente deverão receber uma notificação indicando o acontecimento de tal evento. - Além disso, o cliente também poderá se inscrever em eventos de outros clientes. Para isso, ele poderá utilizar o comando
/list
para listar todos os clientes cadastrados e também o comando/subscreve
, em que é o login do cliente no qual este deseja se subscrever para receber notificações de seus eventos. - Deverá ser implementado um comando
/logout
para deslogar. - Os comandos citados acima deverão existir obrigatoriamente no sistema, mas nada impede que sejam adaptados para um melhor funcionamento.
Para você entender o funcionamento deste chat, assista o vídeo a seguir, onde demonstro como utilizar os comandos, testar algumas regras de lógica implementadas no sistema como não poder logar se já estiver logado, ou não permitir um novo registro de quem já está logado ou não permitir o cadastro de 2 usuários ou mais com o mesmo login, entre outras coisas.
2. Classes de comandos
Como o sistema é baseado em comandos de eventos, resolvi criar duas classes que estarão presentes tanto na aplicação cliente quanto na aplicação servidor. A classe CommandRequest
– Listagem 1 – contem os comandos que serão enviados do cliente ao servidor, e a classe CommandResponse
– Listagem 2 – contem os comandos de resposta do servidor para o cliente. As classes possuem variáveis estáticas que representam os comandos que serão digitados pelo usuário, como também comandos de resposta do servidor. Além disso, a classe CommandRequest
possui três variáveis de instancia que serão úteis para o seguinte propósito. Quando a aplicação cliente enviar uma mensagem ao servidor, essa mensagem será enviada através de um objeto CommandRequest
, onde a variável cmd1
conterá um comando, a cmd2
conterá ou o login do usuário ou o titulo da mensagem publicada e a cmd3
conterá ou a senha do usuário ou o corpo da mensagem de uma mensagem publicada.
package com.wp.mb.evt.command;
import java.io.Serializable;
/**
* http://www.mballem.com/
*/
public class CommandRequest implements Serializable {
//variaveis estaticas que armazenam os possiveis comandos do cliente.
public static final String CONNECT = "/connect";
public static final String LOGIN = "/login";
public static final String REG = "/register";
public static final String PUB = "/public";
public static final String SUBS = "/subscreve";
public static final String LIST = "/list";
public static final String LOGOUT = "/logout";
public static final String HELP = "/help";
private String cmd1;
private String cmd2;
private String cmd3;
// gere os métodos get/set das variaveis cmd1, cmd2, cmd3
public static void help() {
System.out.println("/connect - Efetua a conexao.");
System.out.println("/register - Registro de novo cliente.");
System.out.println("/login - Executa o login no servidor.");
System.out.println("/public - Publica mensagens para seus " +
"subscritos.");
System.out.println("/subscreve - Realiza a subscricao em um " +
"determinado cliente.");
System.out.println("/list - Lista todos os clientes ativos no " +
"servidor.");
System.out.println("/logout - Encerra a conexao com o servidor.");
}
}
package com.wp.mb.evt.command;
import java.io.Serializable;
/**
* http://www.mballem.com/
*/
public class CommandResponse implements Serializable {
//variaveis estaticas que armazenam as possiveis respostas do servidor.
public static final String SUBS_YES = "SUBS_YES";
public static final String LOGIN_YES = "LOGIN_YES";
public static final String LOGIN_NO = "LOGIN_NO";
public static final String LOGIN_LOGGED = "LOGIN_LOGGED";
public static final String EVT_YES = "EVT_YES";
public static final String REG_YES = "REG_YES";
public static final String REG_NO = "REG_NO";
public static final String REG_LOGGED = "REG_LOGGED";
public static final String ON_LINE = "ON_LINE";
}
3. App Server
Na aplicação servidor foram implementas algumas classes a mais do que será no cliente, isso porque, temos o elemento arquivo que deverá ser criado pelo servidor para gravar quais usuários estão subscritos nos tópicos de outros usuários. Para isso, primeiro implementei uma classe User, conforme Listagem 3. Esta classe foi baseada em mapeamento JABX2 para manipulação de XML. O XML será o tipo de arquivo utilizado pelo servidor para gravar as informações dos usuários. Se você não tem conhecimento sobre JAXB2, confira mais na seção Referências.
package com.wp.mb.evt.entity;
import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* http://www.mballem.com/
*/
//indica o elemento raiz do arquivo XML
@XmlRootElement(name = "User")
//indica que vamos trabalhar com os metodos get/set
@XmlAccessorType(XmlAccessType.FIELD)
public class User { //Classe User, também contem as instrucões para montar os arquivos XML
//instrui a criacao da tag login no arquivo xml
@XmlElement(name = "login", required = true)
private String login;
//instrui a criacao da tag password no arquivo xml
@XmlElement(name = "password", required = true)
private String password;
//instrui a criacao da tag subscritos no arquivo xml (sera uma lista)
@XmlElementWrapper(name = "subscritos")
//instrui a criacao das tags filhas de subscritos no arquivo xml (elementos da lista)
@XmlElement(name = "subscrito")
private List subscritos = new ArrayList();
// gere os métodos get/set
@Override
public String toString() {
return "User{" +
"login='" + login + ''' +
", password='" + password + ''' +
", subscritos=" + subscritos +
'}';
}
}
Para manipular os arquivos, vamos usar o padrão DAO. Para isso, foram criadas a interface IUserDao
– Listagem 4 – e a classe UserDao
– Listagem 5 – que implementa os métodos da interface. O método marshalToFile()
converte o objeto User
em uma estrutura XML e o método unmarshalFromFile()
executa a tarefa contrária.
package com.wp.mb.evt.dao;
/**
* http://www.mballem.com/
*/
public interface IUserDao {
void marshalToFile(User user, String fileName);
User unmarshalFromFile(String fileXml);
}
package com.wp.mb.evt.dao;
import com.wp.mb.evt.entity.User;
import javax.xml.bind.*;
import java.io.*;
/**
* http://www.mballem.com/
*/
public class UserDao implements IUserDao {
private Marshaller marshaller;
private Unmarshaller unmarshaller;
/**
* Converte o objeto user em uma estrutura XML.
* @param user objeto a ser convertido em XML.
* @param fileName nome do arquivo XML a ser gerado.
*/
public void marshalToFile(User user, String fileName) {
JAXBContext context;
Writer writer = null;
try {
context = JAXBContext.newInstance(user.getClass());
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
writer = new FileWriter(fileName);
marshaller.marshal(user, writer);
} catch (PropertyException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close(); //fecha o arquivo
}
} catch (Exception e) {
e.getMessage();
}
}
}
/**
* Realiza a conversao (unmarshal) de um arquivo XML em um objeto do seu tipo.
* @param fileXml nome do arquivo XML a ser convertido em objeto.
* @return novo objeto.
*/
public User unmarshalFromFile(String fileXml) {
JAXBContext context;
try {
context = JAXBContext.newInstance(User.class);
unmarshaller = context.createUnmarshaller();
return (User) unmarshaller.unmarshal(
new FileInputStream(fileXml)
);
} catch (JAXBException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
Vamos agora passar para a criação do Socket
no servidor, que se encontra na classe SocketService
da Listagem 6. Já na Listagem 7 temos a classe EventService
com os eventos de resposta do servidor. As duas classes estão bem comentadas, portanto, leia os comentários para ter uma idéia geral da função de cada método. Assim, encerramos o lado servidor. Veja na figura 1 a estrutura do projeto servidor:
Figura 1 – Estrutura da App Servidor
package com.wp.mb.evt.service;
import com.wp.mb.evt.command.CommandRequest;
import com.wp.mb.evt.entity.User;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import static com.wp.mb.evt.command.CommandRequest.*;
import static com.wp.mb.evt.command.CommandRequest.LIST;
/**
* http://www.mballem.com/
*/
public class SocketService {
private static String MSG_ERROR_PUB =
"You must be logged in to post!";
private static String MSG_ERROR_SUBS =
"You must be logged in to realize subscription!";
private static String MSG_ERROR_LIST =
"You must be logged in to visualize the list of connected users!";
private Socket socket;
private ServerSocket serverSocket;
//cria uma lista que guarda os Clientes conectados.
static Map<string, dataoutputstream=""> MAP_USERS = new HashMap<string, dataoutputstream="">();
//no construtor inicializamos o servidor
public SocketService() {
try {
serverSocket = new ServerSocket(5555);
System.out.println("Server On!");
//socket ouvinte
while (true) {
//espera aqui ate que um cliente novo se conecte
socket = serverSocket.accept();
System.out.println("Cliente conectado!");
//inicia a classe thread ListenerCliente passando o socket
// atual como parametro
new Thread(new ListenerSocket(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Classe interna, thread ouvinte do servidor.
*/
private class ListenerSocket implements Runnable {
//objeto para receber os dados entrada
private ObjectInputStream input;
//objeto para enviar os dados de saida
private DataOutputStream output;
//objeto que vai guardar os dados do usuario atual da thread
private User user;
//objeto para executar os metodos de eventos
private EventService eventService;
//objeto de manipulacao dos arquivos xml
public ListenerSocket(Socket socket) {
//variavel input recebe os dados do socket atual
try {
input = new ObjectInputStream(socket.getInputStream());
output = new DataOutputStream(socket.getOutputStream());
eventService = new EventService();
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void run() {
CommandRequest message;
try {
while ((message = (CommandRequest) input.readObject()) != null) {
//Teste que verifica o tipo de comando recebido.
//Conforme tipo de comando, sera chamado o
// metodo referente para executar o processo.
if (message.getCmd1().equals(REG)) {
eventService.registrar(message, user, output);
} else if (message.getCmd1().equals(LOGIN)) {
//quando o usuario tentar logar retorna um objeto user
user = eventService.logon(message, output);
//Se o objeto retornado for != de null entao e porque o
// usuario existe, caso contrario sera null e o usuario
// ainda na existe e entao nao sera armazenado na lista
// de clientes.
if (user != null) {
//adiciona na lista de clientes os usuarios que efetuarem
// o login com sucesso.
//a lista e um Map, a chave sera o login e o valor sera o
// objeto de saida de dados do socket
//do usuario da thread atual
MAP_USERS.put(user.getLogin(), new DataOutputStream(output));
}
} else if (message.getCmd1().equals(PUB)) {
//apenas usuários logados, existentes na lista,
// podem publicar eventos
if (user != null) {
eventService.publicar(message, user);
} else {
eventService.send(output, MSG_ERROR_PUB);
}
} else if (message.getCmd1().equals(SUBS)) {
//apenas usuários logados, existentes na lista,
// podem requere uma subscricao
if (user != null) {
eventService.subscrever(message, output, user.getLogin());
} else {
eventService.send(output, MSG_ERROR_SUBS);
}
} else if (message.getCmd1().equals(LIST)) {
//apenas usuários logados, existentes na lista,
// podem listar os demais clientes
if (user != null) {
eventService.listarClientesConectados(output, user);
} else {
eventService.send(output, MSG_ERROR_LIST);
}
}
}
} catch (IOException e) {
//quando o cliente efetuar o logout retiramos ele da
// lista de clientes ativos
MAP_USERS.remove(user.getLogin());
System.out.println("User [" + user.getLogin() + "] disconnected!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new SocketService();
}
}
package com.wp.mb.evt.service;
import com.wp.mb.evt.command.CommandRequest;
import com.wp.mb.evt.dao.IUserDao;
import com.wp.mb.evt.dao.UserDao;
import com.wp.mb.evt.entity.User;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import static com.wp.mb.evt.command.CommandResponse.*;
import static com.wp.mb.evt.service.SocketService.*;
/**
* http://www.mballem.com/
*/
public class EventService {
private static String XML = ".xml";
private IUserDao dao = new UserDao();
/**
* metodo resposavel por enviar os eventos de um cliente para os
* demais clientes cadastrados a ele.
* @param message string com a mensagem a ser enviada para o grupo de
* clientes cadastrados ao user atual
* @param user o proprio usuario que criou a mensagem que sera publicada
*/
private void sendAll(String message, User user) {
//fazemos um for na lista de clientes ativos
for (Map.Entry<string, dataoutputstream=""> map : MAP_USERS.entrySet()) {
try {
//Testamos se o usuario atual da thread possui
// o cliente na posicao do loop em sua lista
// de subscritos, se possuir, envia o envento para ele.
if (user.getSubscritos().contains(map.getKey())) {
map.getValue().writeUTF(String.valueOf(message));
map.getValue().flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* metodo responsavel por enviar mensagem apenas para o proprio usuario.
* util para todas requisoes que nao sao de publicacao de eventos.
*
* @param outputStream cliente atual
* @param message mensagem a ser enviada ao cliente
*/
public void send(DataOutputStream outputStream, String message) {
try {
outputStream.writeUTF(message);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* metodo que lista todos os usuários ativos no servidor
*
* @param outputStream cliente atual da thread,
* o que fez o pedido da lista
* @param user o usuario atual da thread
*/
public void listarClientesConectados(DataOutputStream outputStream, User user) {
String lista = ON_LINE;
//faz o loop na lista de clientes
for (Map.Entry<string, dataoutputstream=""> map : MAP_USERS.entrySet()) {
//evita que o proprio usuario que solicitou a lista receba
// seu proprio nome entre os listados.
if (!user.getLogin().equals(map.getKey())) {
lista = lista + " [" + map.getKey() + "] ";
}
}
send(outputStream, lista);
}
/**
* metodo para subscricao de usuários em outro usuario
*
* @param request objeto com os dados enviados pelo cliente
* @param outputStream objeto socket do cliente atual
* @param subscrever objeto que contem o login de quem quer
* se escrever em outro cliente
*/
public void subscrever(CommandRequest request, DataOutputStream outputStream,
String subscrever) {
//pelo comando obtemos em qual cliente vamos registar
// a subscricao
String login = request.getCmd2();
//criamos a url com o arquivo do cliente que recebera
// a inscricao de outro cliente
String url = login + XML;
//recuperamos do arquivo encontrado os dados la contidos
User user = (User) dao.unmarshalFromFile(url);
//adicionamos ao objeto user o novo login que sera
// subscrito na lista de subscritos
user.getSubscritos().add(subscrever);
//agora salvamos as alteracoes no arquivo
dao.marshalToFile(user, url);
//envia a mensagem para o cliente que a solicitou
send(outputStream, SUBS_YES);
}
/**
* metodo responsavel por publicar novos eventos
*
* @param request objeto com os dados enviados pelo cliente
* @param user objeto com o usuario atual da thread
*/
public void publicar(CommandRequest request, User user) {
//recuperamos o titulo do evento
String titulo = request.getCmd2();
//recuperamos o conteudo da mensagem do evento
String conteudo = request.getCmd3();
//le o arquivo para recuperar a lista atualizada
// dos clientes subscritos
user = (User) new UserDao().unmarshalFromFile(user.getLogin() + ".xml");
//mensagem que sera publicada. Na classe cliente
// retiraremos a parte EVT YES
String mensagem = EVT_YES + "(" + user.getLogin() + ") " + titulo + " : " + conteudo;
//envia a mensagem pelo metoso sendAll
sendAll(mensagem, user);
}
/**
* metodo resposavel pelo login
*
* @param request objeto com os dados enviados pelo cliente
* @param outputStream objeto de saida de dados da thread atual
* @return o retorno sera um objeto com o login,
* senha e lista de subscritos.
* Ou null, se o usuario ainda nao tiver um cadastro, ou seja,
* um arquivo.
*/
public User logon(CommandRequest request, DataOutputStream outputStream) {
//instancia um objeto usuario e o popula com os dados enviados pelo cliente
User user = new User();
user.setLogin(request.getCmd2());
user.setPassword(request.getCmd3());
//cria a url do arquivo xml
String url = user.getLogin() + XML;
//vamos testar se o arquivo com os dados do cliente já existe.
File file = new File(url);
if (MAP_USERS.containsKey(user.getLogin())) {
//caso o cliente ja esteja logado nao podera logar novamente
send(outputStream, LOGIN_LOGGED);
} else if (file.exists()) {
//se o arquivo existe capturamos suas informacoes e
// populamos o novo objeto user
User xml = dao.unmarshalFromFile(url);
//agora testamos se o login e senha enviadas pelo cliente,
// correspondem com o login e senha do arquivo lido. Se SIM,
// retornamos esse usuario, e ele logara e sera inserido
// na lista de clientes ativos.
// Caso contrario retornamos null
if (xml.getLogin().equals(user.getLogin()) && xml.getPassword().equals(user.getPassword())) {
//enviamos a mensagem LOGIN_YES para a classe Cliente
send(outputStream, LOGIN_YES);
//Aqui retornamos o usuario lido no xml para o objeto user da thread
return xml;
} else {
//se o login e senha nao forem os corretos,
// retornamos LOGIN_NO para a classe Cliente.
send(outputStream, LOGIN_NO);
}
} else {
//se arquivo nao existe retornamos a mensagem
// LOGIN_NO para a classe Cliente
send(outputStream, LOGIN_NO);
}
return null;
}
/**
* metodo resposavel por registrar novos clientes
*
* @param request objeto com os dados enviados pelo cliente
* @param logado objeto usuario da thread atual
* @param output
*/
public void registrar(CommandRequest request, User logado, DataOutputStream output) {
User user = new User();
user.setLogin(request.getCmd2());
user.setPassword(request.getCmd3());
String url = user.getLogin() + XML;
File file = new File(url);
if (logado != null) {
//se logado for diferente de null e porque um usuario
// online esta tentando registrar um novo cliente,
// o que nao sera permitido.
send(output, REG_LOGGED);
} else if (file.exists()) {
//se arquivo ja existe, o registro sera cancelado, isto porque
// ja se tem um cliente com este login do registro
send(output, REG_NO);
} else {
if (user.getLogin() != null && user.getPassword() != null) {
//se nao existe, fazemos o registro
dao.marshalToFile(user, url);
send(output, REG_YES);
} else {
send(output, REG_NO);
}
}
}
}
4. App Cliente
Na aplicação cliente, teremos quatro classes, CommandRequest
, CommandResponse
, SocketClient
e EventClient
. Na classe SocketClient
temos os métodos responsáveis pela conexão com o servidor, como também pelos demais métodos que correspondem com cada evento do usuário. Já a classe EventClient
fica responsável por exibir ao usuário as respostas enviadas do servidor. Novamente, leia os comentários dos códigos para ter uma idéia geral do que cada método faz.
package com.wp.mb.evt.client;
import com.wp.mb.evt.command.CommandRequest;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
import static com.wp.mb.evt.command.CommandRequest.*;
import static com.wp.mb.evt.command.CommandResponse.*;
/**
* http://www.mballem.com/
*/
public class SocketClient {
private Socket socket;
private ObjectOutputStream output;
private CommandRequest request;
public SocketClient() {
try {
initSocket();
} catch (NullPointerException e) {
System.out.println("without connection");
} catch (IOException e) {
System.out.println("failed connection " + e.getMessage());
}
}
private void initSocket() throws IOException {
//leitor de dados do teclado
Scanner keyboard = new Scanner(System.in);
String input = "";
while (!input.equals(LOGOUT)) {
System.out.print(":> ");
//recebe a linha digitada no teclado
input = keyboard.nextLine();
//seleciona o comando digitado
if (input.contains(CONNECT)) {
connect(input);
} else if (input.contains(LOGIN)) {
actions(input);
} else if (input.contains(REG)) {
actions(input);
} else if (input.contains(PUB)) {
actions(input);
} else if (input.contains(SUBS)) {
actions(input);
} else if (input.equals(LIST)) {
list();
} else if (input.equals(LOGOUT)) {
//quando o usuario digitar /logout fechamos o socket cliente
socket.close();
} else if (input.equals(HELP)) {
CommandRequest.help();
}
}
}
/**
* Envia o comando /list para o servidor
*
* @throws IOException
*/
private void list() throws IOException {
CommandRequest message = new CommandRequest();
//passamos o comando /list para o servidor
message.setCmd1(LIST);
//enviamos a mensagem para o servidor com o comando
// /list no objeto comandos
sendMessage(message);
}
/**
* Como os comandos estao em uma unica linha,
* vamos separa-los para adicionar
* as partes especificas em um objeto CommandRequest.
* @param input entrada do teclado
* @throws IOException
*/
private void actions(String input) throws IOException {
// instancia da classe CommandRequest para criar o objeto
// que sera enviado como mensagem para o servidor
CommandRequest message = new CommandRequest();
//quebra a linha digitada no teclado em posicoes em um array
// separadas pelo caracter espaco " ".
String inputLine[] = input.split(" ");
String aux = "";
for (int i = 0; i < inputLine.length; i++) {
if (i == 0) {
//aqui pegamos o comando, /login, /register, ...
message.setCmd1(inputLine[0]);
} else if (i == 1) {
//aqui pegamos a segunda instrucao,
// como login ou titulo do evento publicado.
message.setCmd2(inputLine[1]);
} else {
//aqui pegamos as demais instrucoes,
// como senha ou a mensagem do evento publicado.
//concatenamos as palavras.
// Se a mensagem for: Ola Mundo Java,
// sera quebrada em 3 posicoes, assim,
// precisamos concatena-las novamente.
aux = aux.concat(inputLine[i] + " ");
//trim() elimina qualquer espaco em branco do inicio e
// do final da frase.
message.setCmd3(aux.trim());
}
}
//se a entrada do teclado nao for fazia,
// entao enviamos a mensagem.
if (!input.equals("")) {
sendMessage(message);
}
}
/**
* metodo que envia as mensagens para o servidor
*
* @param message sera um objeto da classe Comandos
*/
private void sendMessage(CommandRequest message) throws IOException {
output.writeObject(message);
output.flush();
}
/**
* realiza a conexao com o servidor.
* @param input entrada do teclado
* @throws IOException
*/
private void connect(String input) throws IOException {
//aqui separamos o /connect do ip, criando um array de 2 posicoes
//[0] = /con/ect
//[1] = ip (localhost...)
String[] ip = input.split(" ");
//queremos apenas o ip (posicao 1)
socket = new Socket(ip[1], 5555);
//inicializa o objeto que envia dados para o servidor
output = new ObjectOutputStream(socket.getOutputStream());
System.out.println("connection ok! n Please, enter your login or register.");
//inicializa a thread ouvinte, que recebera os dados do servidor
receiveMessage();
}
/**
* Metodo que contem a thread ouvinte do socket
*
* @throws IOException
*/
private void receiveMessage() throws IOException {
//recebe os dados enviados pelo servidor
final DataInputStream input = new DataInputStream(socket.getInputStream());
final EventClient eventClient = new EventClient();
//thread que fica escutando o servidor
Thread thread = new Thread() {
@Override
public void run() {
String message;
try {
while ((message = input.readUTF()) != null) {
//Quando uma mensagem chegar, chamamos o método referente.
if (message.equals(REG_YES) ||
message.equals(REG_NO) || message.equals(REG_LOGGED)) {
eventClient.confirmarRegistro(message);
} else if (message.equals(LOGIN_YES) ||
message.equals(LOGIN_NO) || message.equals(LOGIN_LOGGED)) {
eventClient.confirmarLogin(message);
} else if (message.contains(EVT_YES)) {
eventClient.receberEventos(message);
} else if (message.contains(ON_LINE)) {
eventClient.receberLista(message);
} else if (message.equals(SUBS_YES)) {
eventClient.subscrever();
}
}
} catch (IOException e) {
System.out.println("connection finished!");
}
}
};
thread.start();
}
public static void main(String[] args) {
new SocketClient();
}
}
package com.wp.mb.evt.client;
import static com.wp.mb.evt.command.CommandResponse.*;
/**
* Created by IntelliJ IDEA.
* User: Marcio Ballem
* Date: 24/12/12
* Time: 10:05
* http://www.mballem.com/
*/
public class EventClient {
/**
* metodo que recebe a resposta do servidor.
*/
public void subscrever() {
System.out.print("Congratulations, your subscription was successful!" + " ");
}
/**
* metodo que recebe a lista de clientes ativos no servidor.
*
* @param resposta contem a lista enviada pelo servidor
* com os clientes ativos + a instrucao Online
*/
public void receberLista(String resposta) {
//O método contains() é parecido com o equals(), porem ele analisa se em uma frase,
// contem tal palavra.
System.out.print("List of users " + resposta + " ");
}
/**
* metodo que recebe o evento publicado por outro cliente.
*
* @param resposta a instrucao EVT YES + a mensagem publica,
* com titulo e o login de quem a enviou.
*/
public void receberEventos(String resposta) {
String mensagem = resposta.replace(EVT_YES, "");
System.out.print(mensagem + " " );
}
/**
* metodo que confirma se houve sucesso na requisição do login.
*
* @param resposta mensagem com a instrucao LOGIN YES se houve sucesso,
* LOGIN NO se nao foi possivel fazer o login e
* LOGIN LOGGED se tentar logar com um usuario ja logado
*/
public void confirmarLogin(String resposta) {
if (resposta.equals(LOGIN_YES)) {
System.out.print("You're logged!" + " ");
} else if (resposta.equals(LOGIN_NO)) {
System.out.print("Login not exist or wrong password!" + " " );
} else if (resposta.equals(LOGIN_LOGGED)) {
System.out.print("You're already logged!" + " " );
}
}
/**
* metodo que confirma se houve sucesso no registro de um novo cliente.
*
* @param resposta se o registro foi feito a mensagem tera REG YES,
* caso contrario REG NO e se o usuario logado tentar
* se registrar novamento, recebe um REG LOGGED
*/
public void confirmarRegistro(String resposta) {
if (resposta.equals(REG_YES)) {
System.out.print("Login registered successfully!" + " ");
} else if (resposta.equals(REG_NO)) {
System.out.print("Login denied! Trying a new login and password." + " ");
} else if (resposta.equals(REG_LOGGED)) {
System.out.print("Client logged must not make a new record!" + " ");
}
}
}
Veja na fígura 2 a estrutura do projeto Cliente:
Fígura 2 – Estrutura da App Cliente
Referências