C # - Criando um Chat - Parte 1 - O Cliente
Neste artigo eu vou mostrar como criar um Chat procurando ser bem objetivo e mantendo as coisas o simples possível. |
Na verdade uma aplicação de Chat (bate-papo) consiste em duas aplicações:
Para poder acompanhar e entender tudo que vamos fazer é necessário que se tenha conhecimento sobre os seguintes tópicos:
Vamos iniciar criando o cliente.
Chat - Criando a aplicação cliente
A aplicação cliente é a mais simples, já que tudo o que ela tem a fazer é tentar se conectar ao servidor de bate-papo, pedir um nome de usuário, começar a ouvir as mensagens e enviar suas próprias e, finalmente, desligar.
Inicie o Visual C# 2010 Express Edition e crie um novo projeto do tipo Windows Appliation com o nome ChatCliente:
Vamos alterar o nome do formulário form1.cs criado por padrão no projeto para frmCliente.cs e a seguir definir o leiaute conforme mostra a figura abaixo no formulário:
Controles usados no
formulário:
|
Vamos agora ao código do cliente:
1- Namespaces usados:
using
System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System;
2- Declarando as variáveis usadas no projeto:
// Trata o
nome do usuário
private
string NomeUsuario = "Desconhecido";
private StreamWriter stwEnviador;
private StreamReader strReceptor;
private TcpClient tcpServidor;
//
Necessário para atualizar o formulário com mensagens da outra
thread
private
delegate void AtualizaLogCallBack(string strMensagem);
//
Necessário para definir o formulário para o estado
"disconnected" de outra thread
private
delegate void FechaConexaoCallBack(string strMotivo);
private Thread mensagemThread;
private IPAddress enderecoIP;
private bool Conectado;
3- Código do evento Click do botão Conectar que verifica se estamos conectados e inicia a conexão;
private void btnConectar_Click(object sender, System.EventArgs e) { // se não esta conectando aguarda a conexão if (Conectado == false) { // Inicializa a conexão InicializaConexao(); } else // Se esta conectado entao desconecta { FechaConexao("Desconectado a pedido do usuário."); } } |
4- Código da rotina IncializaConexao():
private void InicializaConexao() { // Trata o endereço IP informado em um objeto IPAdress enderecoIP = IPAddress.Parse(txtServidorIP.Text); // Inicia uma nova conexão TCP com o servidor chat tcpServidor = new TcpClient(); tcpServidor.Connect(enderecoIP, 2502); // AJuda a verificar se estamos conectados ou não Conectado = true; // Prepara o formulário NomeUsuario = txtUsuario.Text; // Desabilita e habilita os campos apropriados txtServidorIP.Enabled = false; txtUsuario.Enabled = false; txtMensagem.Enabled = true; btnEnviar.Enabled = true; btnConectar.Text = "Desconectado"; // Envia o nome do usuário ao servidor stwEnviador = new StreamWriter(tcpServidor.GetStream()); stwEnviador.WriteLine(txtUsuario.Text); stwEnviador.Flush(); //Inicia a thread para receber mensagens e nova comunicação mensagemThread = new Thread(new ThreadStart(RecebeMensagens)); mensagemThread.Start(); } |
O endereço IP é analisado a partir do TextBox em um objeto IPAddress, e então abrimos uma conexão TCP para esse endereço. A porta é 2502, mas você pode usar qualquer porta disponível. Em seguida, preparar os controles no formulário, desativando alguns e habilitando outros.
Alteramos também a legenda de btnConetar para exibir a mensagem "Desconectado". Através de um Stream, informamos ao servidor o nome de usuário, e imediatamente depois que começamos uma nova thread que chama o método RecebeMensagens() que irá ouvir as mensagens recebidas a partir de agora.
Colocando isso em uma thread separada, a nossa aplicação é totalmente utilizável, enquanto ele está escutando as mensagens do servidor e mantem a conexão ativa.
5- Código da rotina RecebeMensagens
private void RecebeMensagens() { // recebe a resposta do servidor strReceptor = new StreamReader(tcpServidor.GetStream()); string ConResposta = strReceptor.ReadLine(); // Se o primeiro caracater da resposta é 1 a conexão foi feita com sucesso if (ConResposta[0] == '1') { // Atualiza o formulário para informar que esta conectado this.Invoke(new AtualizaLogCallBack(this.AtualizaLog), new object[] { "Conectado com sucesso!" }); } else // Se o primeiro caractere não for 1 a conexão falhou { string Motivo = "Não Conectado: "; // Extrai o motivo da mensagem resposta. O motivo começa no 3o caractere Motivo += ConResposta.Substring(2, ConResposta.Length - 2); // Atualiza o formulário como o motivo da falha na conexão this.Invoke(new FechaConexaoCallBack(this.FechaConexao), new object[] { Motivo }); // Sai do método return; } // Enquanto estiver conectado le as linhas que estão chegando do servidor while (Conectado) { // exibe mensagems no Textbox this.Invoke(new AtualizaLogCallBack(this.AtualizaLog), new object[] { strReceptor.ReadLine() }); } } |
Um novo leitor dee stream está ligado ao cliente TCP. Ele vai ouvir as mensagens recebidas. Mas antes de tudo, lemos a primeira linha vindo do servidor. O motivo é que sabemos que a primeira linha contém uma resposta nos dizendo se estamos ou não conectados com êxito.
Há duas razões para a conexão falhar: 1: usar um nome de usuário já em uso ou 2: usar Administrador como nome de usuário.
O primeiro caractere da resposta dada pelo servidor nos diz o seguinte:
Quando a conexão falha, lemos também o porquê da falha, que inicia no terceiro caractere da mensagem, já que o primeiro é o número, e o segundo é um caractere de pipe. Por exemplo: 0|Nome de Usuário já está em uso. Assim se o primeiro caractere não é um 1 lemos a cadeia que começa no caractere 3 e termina no final da linha para obter essa informação.
O método this.Invoke(...) chama o formulário para se atualizar. Não podemos atualizar diretamente os elementos do formulário nos mesmos a partir deste método porque ele esta em uma thread distinta (lembre-se nós o chamamos usando ThreadStart ()) e operações entre threads são ilegais.
Finalmente, o loop while (Conectado) continua chamando o método strReceptor.ReadLine() que verifica as mensagens recebidas do servidor.
Em seguida, temos o método que continuamos chamando this.Invoke(...); tudo que ele faz é atualizar o TextBox txtLog com a mensagem mais recente.
6- Código da rotina AtualizaLog
private void AtualizaLog(string strMensagem) { // Anexa texto ao final de cada linha txtLog.AppendText(strMensagem + "\r\n"); } |
7- Código do evento Click do botão Enviar
private void btnEnviar_Click(object sender, System.EventArgs e) { EnviaMensagem(); } |
8- O código evento KeyPress da caixa de texto Mensagem:
private void txtMensagem_KeyPress(object sender, KeyPressEventArgs e) { // Se pressionou a tecla Enter if (e.KeyChar == (char)13) { EnviaMensagem(); } } |
Para enviar uma mensagem usamos o evento click do botão Enviar ou quando a tecla Enter for pressionada. Nestes casos chamamos a rotina EnviaMensagem() conforme os itens 7 e 8 acima.
9- Código da rotina EnviaMensagem
// Envia a mensagem para o servidor private void EnviaMensagem() { if (txtMensagem.Lines.Length >= 1) { //escreve a mensagem da caixa de texto stwEnviador.WriteLine(txtMensagem.Text); stwEnviador.Flush(); txtMensagem.Lines = null; } txtMensagem.Text = ""; } |
A rotina EnviaMenagem() apenas verifica se o número de linhas é maior ou igual a 1, e então escreve a linha para a conexão TCP através do objeto StreamWriter. A chamda ao método Flush() garante que as mensagens estão sendo enviadas de imediato.
10 - Código da rotina FechaConexao
// Fecha a conexão com o servidor private void FechaConexao(string Motivo) { // Mostra o motivo porque a conexão encerrou txtLog.AppendText(Motivo + "\r\n"); // Habilita e desabilita os controles apropriados no formulario txtServidorIP.Enabled = true; txtUsuario.Enabled = true; txtMensagem.Enabled = false; btnEnviar.Enabled = false; btnConectar.Text = "Conectado"; // Fecha os objetos Conectado = false; stwEnviador.Close(); strReceptor.Close(); tcpServidor.Close(); } |
Para fechar a conexão chamamos o método FechaConexao e neste código temos que o formulário está sendo trazido de volta para o estado não conectado, e a conexão TCP e os streams estão sendo fechados. Mas o que acontece se o usuário não clicar para desconectar mas apenas fechar o aplicativo enquanto a conexão com o servidor está ativa ?
Nós certamente não queremos deixar a conexão aberta como esta até que ele morra por conta própria. Felizmente existe o evento ApplicationExit que é acionado quando o aplicativo for fechado, e é aí que podemos fechar a nossa conexão. Para tratar este evento vamos alterar o código do construtor do formulário conforme abaixo:
11- Definição do tratamento do evento ApplicationOnExit
public frmCliente() { // Na saida da aplicação : desconectar Application.ApplicationExit += new EventHandler(OnApplicationExit); InitializeComponent(); } |
12- Código do evento OnApplicationExit()
// O tratador de evento para a saida da aplicação public void OnApplicationExit(object sender, EventArgs e) { if (Conectado == true) { // Fecha as conexões, streams, etc... Conectado = false; stwEnviador.Close(); strReceptor.Close(); tcpServidor.Close(); } } |
O código do evento OnApplicationExit() vai ocorrer quando o usuário fechar o aplicativo diretamente com a conexão ativa e vai verificar se estamos conectados e fechar as conexões e os recursos usados
Pegue a primeira parte do projeto completo aqui: ChatCliente.zip
Na segunda parte do artigo irei mostrar a criação do Servidor que irá receber e distribuir as mensagens recebidas: C # - Criando um Chat - Parte 2 - O Servidor (inativo)
"Meus filhinhos , estas coisas vos escrevo, para que não pequeis; e se alguém pecar, temos um advogado para com o Pai, Jesus Cristo, o justo." I João 2:1
Referências: