'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