CODIGO FUENTE DE UN CHAT CON SOCKETS

Iniciado por syst, 8 Junio 2012, 12:15 PM

0 Miembros y 1 Visitante están viendo este tema.

syst

Hola a todos! Me animo a poner un programa que he hecho estando en prácticas de una FP y bueno, quiero que me digan la porquería que sobra, y, también, mejores formas de hacer lo que yo hice.
Sólo lo he probado en local, no se si funcionaría fuera. Ya lo probaré. De momento con que me digan que mejorar y que sobra, me vale.

Clase Cliente:
Código (vbnet) [Seleccionar]
'Chat V1.0.
'Formulario cliente.
'Creado por Alejandro Cuenca.
'El código se puede usar, reutilizar o lo que consideren, aunque es el primero
'que hago y valdrá de poco. Recuerden agradecer, sólo eso.

Imports System.IO
Imports System.Net.Sockets
Imports System.Threading
Imports System.Text

Public Class Cliente

#Region "VARIABLES"
    Dim TCPCli As TcpClient 'Puerto e IP de conexion al servidor.
    Dim thread As Thread 'Para recibir mensajes del servidor.
    Dim stm As NetworkStream 'Datos que recibo del servidor.
    Dim bufferLectura() As Byte 'Donde guardare los mensajes recibidos del servidor.
#End Region

#Region "BOTONES"
    Private Sub btnConectar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnConectar.Click
        'Me conecto al servidor.
        TCPCli = New TcpClient
        'Envio un mensaje con el nick.
        'Dim bufferEscritura() As Byte
        Try
            'Me conecto al servidor con IP y Puerto.
            TCPCli.Connect(txtIP.Text, CType(txtPort.Text, Integer))
            'Inicializo el array.
            'bufferEscritura = New Byte(100) {}
            'Si se ha conectado lo muestro, y si no, tambien.
            If TCPCli.Connected = True Then
                'Con getStream obtengo la red en la que estoy.
                stm = TCPCli.GetStream
                'Escribo en el RichTextBox.
                rtbConversacion.Text = rtbConversacion.Text & "Conexion realizada. Conectado a " & txtIP.Text & "." & vbCrLf
                'Mando el nick.
                Dim outStream As Byte() = System.Text.Encoding.ASCII.GetBytes(txtNick.Text + "$")
                stm.Write(outStream, 0, outStream.Length)
                stm.Flush()
                'Inicio un thread llamando al metodo recibirMensaje para escuchar lo que llega del servidor y mostrarlo.
                thread = New Thread(AddressOf recibirMensaje)
                thread.Start()
                'Activo los controles para enviar mensajes.
                txtTexto.Enabled = True
                btnEnviar.Enabled = True
                rtbConversacion.Enabled = True
            Else
                rtbConversacion.Text = rtbConversacion.Text & "Conexion fallida." & vbCrLf
            End If
        Catch ex As Exception
            rtbConversacion.Text = rtbConversacion.Text & "Error en la conexion" & vbCrLf
        End Try

    End Sub

    Private Sub btnEnviar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEnviar.Click
        'Defino el array de bytes para enviar el mensaje al servidor.
        Dim bufferEscritura() As Byte
        Dim aux As Boolean = True
        While aux
            Try
                bufferEscritura = New Byte(10000) {}
                'Si el texto empieza por '.', salgo del programa.
                If txtTexto.Text = "." Then
                    terminarConexion()
                Else
                    'Lo codifico para enviarlo.
                    bufferEscritura = Encoding.ASCII.GetBytes(txtTexto.Text)
                    'Escribo en el RichTextBox el mensaje que acabo de enviar.
                    rtbConversacion.Text = rtbConversacion.Text & Chr(13) & "Usted dice: " & Encoding.ASCII.GetString(bufferEscritura) & Chr(13)
                    stm.Write(bufferEscritura, 0, bufferEscritura.Length)
                    'Me situo al final del RichTextBox
                    rtbConversacion.SelectionStart = rtbConversacion.TextLength
                    rtbConversacion.ScrollToCaret()
                    txtTexto.Text = ""
                    txtTexto.Focus()
                End If
            Catch ex As Exception
                rtbConversacion.Text = rtbConversacion.Text & "Error al enviar datos" & vbCrLf
            End Try
            aux = False
        End While
    End Sub

#End Region

#Region "METODOS"
    Private Sub recibirMensaje()
        While True
            Try
                bufferLectura = New Byte(10000) {}
                stm = TCPCli.GetStream
                'Me quedo esperando a que llegue algun mensaje, y lo leo.
                stm.Read(bufferLectura, 0, bufferLectura.Length)
                escribir()
            Catch ex As Exception
                Exit Sub
            End Try
        End While
    End Sub

    Private Sub terminarConexion()
        Me.Close()
    End Sub

    Private Sub txtTexto_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtTexto.KeyPress
        If e.KeyChar = Chr(13) Then
            e.Handled = True
        Else
            e.Handled = False
        End If
    End Sub

    Private Sub escribir()
        'Variables para dividir el mensaje entre el nick y el mensaje.
        Dim men As String
        Dim nom As String
        Dim todo As String
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf escribir))
        Else
            Try
                'Obtengo el mensaje enviado con todos los datos.
                todo = Encoding.ASCII.GetString(bufferLectura)
                'Si es un '.', es que el nick ya se ha elegido.
                If todo.Substring(0, todo.LastIndexOf(Chr(46)) + 1) = "." Then
                    txtNick.Text = ""
                    rtbConversacion.Text = rtbConversacion.Text & Chr(13) & "El nick ya existe, elija otro." & Chr(13)
                Else
                    'Si no es un punto, divido el mensaje en texto y remitente.
                    men = todo.Substring(todo.IndexOf(Chr(93)) + 1, todo.LastIndexOf(Chr(91)) + 1)
                    nom = todo.Substring(0, todo.IndexOf(Chr(93)))
                    'Escribo en el RichTextBox el mensaje reenviado del servidor.
                    rtbConversacion.Text = rtbConversacion.Text & Chr(13) & nom & " dice: " & men & Chr(13)
                End If
            Catch ex As Exception
                'Si no puede asignar nom a nada, es porque el mensaje es del servidor.
                rtbConversacion.Text = rtbConversacion.Text & Chr(13) & "Servidor dice: " & todo & Chr(13)
            End Try
        End If
    End Sub

    Private Sub Cliente_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        'Declare an array of processes
        Dim myProcesses() As Process
        'single process variable
        Dim myProcess As Process
        'Get the list of processes
        myProcesses = Process.GetProcesses()
        'Iterate through the process array
        For Each myProcess In myProcesses
            If myProcess.ProcessName = "TCPCient" Then
                myProcess.Kill()
            End If
        Next
    End Sub
#End Region
 
End Class


Clase servidor:
Código (vbnet) [Seleccionar]
'Chat V1.0.
'Formulario servidor.
'Creado por Alejandro Cuenca.
'El código se puede usar, reutilizar o lo que consideren, aunque es el primero
'que hago y valdrá de poco. Recuerden agradecer, sólo eso.


Imports System.IO
Imports System.Net.Sockets
Imports System.Threading
Imports System.Text

Public Class Servidor

#Region "VARIABLES"
    Dim clientSocket As TcpClient
    Dim tcpLis As TcpListener 'Creamos el listener para oir un puerto determinado.
    Dim clientes As New Hashtable 'Creamos una coleccion con los clientes que se conectan.
    Dim thread As Thread 'Creamos un thread para recibir mensajes de los clientes.
    Dim clienteActual As Net.IPEndPoint 'Guardamos la informacion de un cliente para abrir o cerrar la conexion.
    Dim cli As infoCliente 'Guardamos el cliente actual.
#End Region

#Region "ESTRUCTURAS"
    Private Structure infoCliente
        Public sock As Socket 'Socket utilizado para mantener la conexion con el cliente
        Public thre As Thread 'Thread utilizado para escuchar al cliente
        Public ultimosDatos As String 'Ultimos datos enviados por el cliente
        Public nombre As String 'Nombre utilizado en el server.
    End Structure
#End Region

#Region "METODOS"
    Private Sub esperarCliente()
        Dim infoClienteActual As infoCliente = Nothing
        Dim bufferLectura() As Byte
        With infoClienteActual
            While True
                clientSocket = tcpLis.AcceptTcpClient
                'Cuando se recibe la conexion, guardo la informacion del cliente.
                bufferLectura = New Byte(10000) {}
                'El socket.
                .sock = clientSocket.Client
                clienteActual = .sock.RemoteEndPoint 'Aqui guardamos su EndPoint, que sera la clave del hashtable.
                'Obtenemos el nick.
                Dim networkStream As NetworkStream = clientSocket.GetStream()
                networkStream.Read(bufferLectura, 0, CInt(clientSocket.ReceiveBufferSize))
                .nombre = System.Text.Encoding.ASCII.GetString(bufferLectura)
                .nombre = .nombre.Substring(0, .nombre.IndexOf("$"))
                'Luego el thread.
                .thre = New Thread(AddressOf leerCliente) 'Este thread escucha los mensajes del cliente.
                'Asignamos al cliente que acaba de entrar a la variable global cli.
                cli = infoClienteActual
                'Guardamos en el hashtable los datos del cliente. Usamos synclock para que se ejecute solo eso.
                SyncLock Me
                    'Compruebo si ya exite el nick.
                    comprobarNick()
                    'Si no existe el nick, lo guardo en el hashtable.
                    If cli.nombre <> Nothing Then
                        clientes.Add(clienteActual, infoClienteActual)
                        'Iniciamos la escucha.
                        .thre.Start()
                        'Lleno el listbox con los clientes.
                        llenarlistbox()
                        'Muestro un mensaje de conexion en el servidor.
                        joined()
                    End If
                End SyncLock
            End While
        End With
    End Sub

    Private Sub leerCliente()
        Dim IDReal As Net.IPEndPoint 'ID del cliente que envia algo para leer.
        Dim bufferLectura() As Byte 'Aqui se guardara el mensaje que llega.
        Dim infoClienteActual As infoCliente 'Aqui se guardara la informacion del cliente.
        Dim ret As Integer 'Aqui se guarda la longitud del mensaje.
        IDReal = clienteActual 'Guardamos el cliente actual.
        infoClienteActual = clientes(IDReal) 'Obtenemos los datos de la coleccion que corresponden a ese cliente.
        'Con el cliente seleccionado de la coleccion, obtenemos el mensaje.
        With infoClienteActual
            While True
                'Si el socket esta conectado, leo el mensaje.
                If .sock.Connected Then
                    bufferLectura = New Byte(100) {}
                    Try
                        'Me quedo esperando a que llegue un mensaje desde el cliente
                        ret = .sock.Receive(bufferLectura, bufferLectura.Length, SocketFlags.None)
                        If ret > 0 Then
                            'Guardo el mensaje recibido
                            .ultimosDatos = Encoding.ASCII.GetString(bufferLectura)
                            clientes(IDReal) = infoClienteActual
                            'Guardo el cliente actual para usar el nick y el mensaje.
                            cli = infoClienteActual
                            'Se recibe el mensaje y se muestra en el RichTextBox.
                            escribirMensaje()
                            'Reenvio el mensaje a los usuarios para que reciban lo que dicen los demas usuarios.
                            reenviarMensaje()
                        Else
                            'Genero el evento de la finalizacion de la conexion
                            'RaiseEvent ConexionTerminada(IDReal)
                            Exit While
                        End If
                    Catch ex As Exception
                        If Not .sock.Connected Then
                            'Genero el evento de la finalizacion de la conexion
                            cerrarThread(infoClienteActual.sock)
                            Exit While
                        End If
                    End Try
                End If
            End While
        End With
    End Sub

    Private Sub llenarlistbox()
        Dim cliente As infoCliente
        'Si el metodo no puede controlar el listbox, se hace el invoke y luego va al else.
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf llenarlistbox))
        Else
            lstUsuarios.Items.Clear()
            lstUsuarios.Items.Add("USUARIOS")
            For Each cliente In clientes.Values
                lstUsuarios.Items.Add(cliente.nombre)
            Next
        End If

    End Sub

    Private Sub escribirMensaje()
        'Si el metodo no puede controlar el richtextbox, se hace el invoke y luego va al else.
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf escribirMensaje))
        Else
            rtbConversacion.Text = rtbConversacion.Text & Chr(13) & cli.nombre & " dice: " & cli.ultimosDatos
        End If
    End Sub

    Private Sub joined()
        'Si el metodo no puede controlar el richtextbox, se hace el invoke y luego va al else.
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf joined))
        Else
            rtbConversacion.Text = rtbConversacion.Text & cli.nombre & " se ha unido a la sala." & Chr(13)
        End If
    End Sub

    Private Sub reenviarMensaje()
        'Creamos un cliente para recorrer la coleccion.
        Dim cliente As infoCliente
        Dim men As String
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf reenviarMensaje))
        Else
            'Men sera el mensaje a enviar.
            men = cli.nombre & Chr(93) & cli.ultimosDatos.Trim() & Chr(91)
            For Each cliente In clientes.Values
                If cliente.nombre <> cli.nombre AndAlso cliente.sock.RemoteEndPoint.ToString <> cli.sock.RemoteEndPoint.ToString Then
                    cliente.sock.Send(Encoding.ASCII.GetBytes(men))
                End If
            Next
        End If
    End Sub

    Private Sub comprobarNick()
        'Creo un cliente para recorrer la coleccion.
        Dim cliente As infoCliente
        Dim men As String
        'If Me.InvokeRequired Then
        '    Me.Invoke(New MethodInvoker(AddressOf comprobarNick))
        'Else
        'Men es el mensaje a enviar.
        men = Chr(46) & Chr(1)
        For Each cliente In clientes.Values
            If cliente.nombre = cli.nombre Then
                cli.sock.Send(Encoding.ASCII.GetBytes(men))
                'Si se ha encontrado el nombre del cliente en la coleccion, mando un mensaje determinado y pongo cli a nothing
                'para no guardar nada en el hashtable.
                cli.nombre = Nothing
                Exit Sub
            End If
        Next
        'End If
    End Sub

    Private Sub cerrarThread()
        Dim cliente As infoCliente
        'Cierro el thread que se encargaba de escuchar a cada cliente.
        Try
            For Each cliente In clientes.Values
                cliente.thre.Abort()
            Next
        Catch ex As Exception
            Exit Sub
        End Try
    End Sub

    Private Sub cerrarThread(ByVal socket As Socket)
        'Cierro el thread que le corresponde al cliente que abandona la sala.
        Dim cliente As infoCliente
        Try
            For Each cliente In clientes
                If cliente.sock.ToString = socket.ToString Then
                    cliente.thre.Abort()
                    'Lo borro de la coleccion.
                    clientes.Remove(socket.RemoteEndPoint)
                End If
            Next
        Catch ex As Exception
            Exit Sub
        End Try
    End Sub
#End Region

#Region "BOTONES"

    Private Sub Servidor_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        cerrarThread()
        'Declare an array of processes
        Dim myProcesses() As Process
        'single process variable
        Dim myProcess As Process
        'Get the list of processes
        myProcesses = Process.GetProcesses()
        'Iterate through the process array
        For Each myProcess In myProcesses
            If myProcess.ProcessName = "TCPServer" Then
                myProcess.Kill()
            End If
        Next
    End Sub

    Private Sub Servidor_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'En el load, asignamos el puerto de escucha.
        tcpLis = New TcpListener(6666)
        'Iniciamos la escucha.
        tcpLis.Start()
        'Creo un thread para que se quede escuchando la llegada de un cliente.
        thread = New Thread(AddressOf esperarCliente)
        thread.Start()
        rtbConversacion.Text = rtbConversacion.Text & "CHAT" & Chr(13)
    End Sub

    Private Sub btnEnviar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnviar.Click
        Dim cliente As infoCliente
        'Mando el mensaje a cada cliente de la coleccion.
        For Each cliente In clientes.Values
            cliente.sock.Send(Encoding.ASCII.GetBytes(txtTexto.Text))
        Next
        txtTexto.Text = ""
    End Sub
#End Region

End Class