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
'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
'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