Aplicación cliente-servidor en sockets TCP. Aplicación cliente-servidor en un socket de flujo TCP

Última actualización: 31/10/2015

Veamos cómo crear una aplicación cliente-servidor multiproceso. De hecho, se diferenciará de uno de un solo subproceso solo en que el procesamiento de la solicitud del cliente se llevará a cabo en un subproceso separado.

Primero, creemos un proyecto para el cliente. Llamemos al proyecto ConsoleClient y definamos el siguiente código en la clase Programa:

Usando el Sistema; utilizando System.Net.Sockets; usando System.Text; espacio de nombres ConsoleClient ( clase Programa ( const int puerto = 8888; const string dirección = "127.0.0.1"; static void Main(string args) ( Console.Write("Ingrese su nombre:"); string userName = Console.ReadLine() ; TcpClient client = null; try ( client = new TcpClient(dirección, puerto); NetworkStream stream = client.GetStream(); while (true) ( ​​​​Console.Write(userName + ": "); // ingresa un mensaje mensaje de cadena = Console.ReadLine(); mensaje = String.Format("(0): (1)", nombre de usuario, mensaje // convierte el mensaje a una matriz de bytes datos de bytes = Encoding.Unicode.GetBytes(mensaje) ; stream.Write(data, 0, data.Length); // obtiene los datos de respuesta = new byte; // buffer para los datos recibidos StringBuilder builder = new StringBuilder(); 0, datos.Longitud); constructor.Append(Encoding.Unicode.GetString(datos, 0, bytes)); mientras (stream.DataAvailable); mensaje = constructor.ToString(); (Excepción ex) ( Console.WriteLine(ex.Message);

) finalmente ( cliente.Close(); ) ) ) )

En el programa cliente, el usuario ingresará primero su nombre y luego el mensaje a enviar. Además, el mensaje se enviará en el formato Nombre: mensaje.

Después de enviar el mensaje, el cliente recibe el mensaje del servidor.

Usando el Sistema; utilizando System.Net.Sockets; usando System.Text; espacio de nombres ConsoleServer (clase pública ClientObject (cliente público TcpClient; público ClientObject(TcpClient tcpClient) (cliente = tcpClient;) público void Process() (NetworkStream stream = null; try (stream = client.GetStream(); byte de datos = nuevo byte; // buffer para los datos recibidos while (true) ( ​​// recibe el mensaje StringBuilder builder = new StringBuilder(); int bytes = 0; do ( bytes = stream.Read(data, 0, data.Length); builder. Append(Encoding .Unicode.GetString(data, 0, bytes)); while (stream.DataAvailable); string message = builder.ToString() // enviar de vuelta un mensaje en mayúsculas message = message.Substring (message.IndexOf) (":") + 1).Trim().ToUpper(); datos = Encoding.Unicode.GetBytes(mensaje); stream.Write(data, 0, data.Length) ) catch(Exception); .WriteLine(ex.Message); finalmente (if (stream!= null) stream.Close(); if (client!= null) client.Close(); ) ) ) )

En esta clase, por el contrario, primero recibimos el mensaje en el bucle do.. while y luego lo cambiamos un poco (cortamos los dos puntos y lo convertimos a mayúsculas) y lo enviamos de regreso al cliente. Es decir, la clase ClientObject contiene todas las acciones para trabajar con una conexión independiente.

En la clase principal del proyecto del servidor, definiremos el siguiente código:

Usando el Sistema; utilizando System.Net; utilizando System.Net.Sockets; usando System.Threading; espacio de nombres ConsoleServer ( clase Programa ( const int puerto = 8888; oyente TcpListener estático; static void Main(argumentos de cadena) ( try ( oyente = nuevo TcpListener(IPAddress.Parse("127.0.0.1"), puerto); oyente.Start() ; Console.WriteLine("Esperando conexiones..."); while(true) ( ​​​​TcpClient client = listener.AcceptTcpClient(); ClientObject clientObject = new ClientObject(cliente); // crea un nuevo hilo para servir el nuevo cliente Thread clientThread = new Thread(new ThreadStart(clientObject.Process)); clientThread.Start(); catch(Exception ex) ( Console.WriteLine(ex.Message); ) finalmente (if(oyente!=null) oyente. Detener(); ) ) ) )

Inmediatamente después de conectar un nuevo cliente:

Cliente TcpClient = oyente.AcceptTcpClient()

Se crea un objeto ClientObject y se crea un nuevo hilo que ejecuta el método de proceso del objeto ClientObject, donde realmente se reciben y envían los mensajes. De esta forma, el servidor podrá procesar simultáneamente varias solicitudes a la vez.

Resultados del programa. Uno de los clientes:

Ingrese su nombre: Evgeniy Evgeniy: hola mundo Servidor: HOLA MUNDO Evgeniy: adiós paz Servidor: BYE MUNDO Evgeniy: _

Esperando conexiones... Evgeniy: hola mundo Evgeniy: adiós mundo Tom: hola chat

Última actualización: 31/10/2015

Veamos cómo crear un servidor que se ejecute en el protocolo TCP utilizando sockets. El esquema general de cómo funciona un socket de servidor TCP será el siguiente:

El código del programa del servidor será así:

Usando el Sistema; usando System.Text; utilizando System.Net; utilizando System.Net.Sockets; espacio de nombres SocketTcpServer ( clase Programa ( static int port = 8005; // puerto para recibir solicitudes entrantes static void Main(string args) ( // obtener direcciones para iniciar el socket IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1" ) , port); // crea un socket Socket listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try ( // vincula el socket a un punto local a través del cual recibiremos datos listeningSocket.Bind( ipPoint); // comienza a escuchar listeningSocket.Listen(10); Console.WriteLine("El servidor se está ejecutando. Esperando conexiones..."); recibir el mensaje StringBuilder builder = new StringBuilder( ); int bytes = 0; // número de bytes recibidos byte data = nuevo byte; // buffer para los datos recibidos do ( bytes = handler.Receive(data); builder.Append(Codificación .Unicode.GetString(datos, 0, bytes)); ) while (handler.Available>0); Console.WriteLine(DateTime.Now.ToShortTimeString() + ": " + builder.ToString());

// envía una cadena de respuesta mensaje = "tu mensaje ha sido entregado";

datos = Codificación.Unicode.GetBytes(mensaje);

controlador.Enviar(datos);

// cerrar el controlador de socket.Shutdown(SocketShutdown.Both);

controlador.Close();

) ) catch(Excepción ex) ( Console.WriteLine(ex.Message); ) ) ) )

Primero, después de crear un socket, lo vinculamos a un punto local usando el método Bind:

El método Accept elimina la primera solicitud de la cola de solicitudes pendientes y crea un objeto Socket para procesarla. Si la cola de solicitudes está vacía, el método Accept bloquea el hilo de llamada hasta que haya una nueva conexión disponible.

Para procesar la solicitud, primero en el bucle do.. while recibimos los datos usando el método Recibir:

Hacer ( bytes = handler.Receive(data); builder.Append(Encoding.Unicode.GetString(data, 0, bytes)); ) while (handler.Available > 0);

El método Recibir toma como parámetro una matriz de bytes en la que se leen los datos recibidos y devuelve el número de bytes recibidos.

Si no hay datos disponibles para leer, el método Recibir bloquea el hilo de llamada hasta que haya datos disponibles, a menos que se haya establecido un valor de tiempo de espera mediante el objeto Socket.ReceiveTimeout. Si se excede el valor del tiempo de espera, el objeto Recibir generará una SocketException. Para rastrear la presencia de datos en una secuencia, se utiliza la propiedad Disponible.

Después de recibir los datos, se envía un mensaje de respuesta al cliente mediante el método Enviar, que toma una matriz de bytes como parámetro:

Controlador.Enviar(datos);

Al final de procesar la solicitud, debe cerrar el socket asociado a ella:

Handler.Shutdown(SocketShutdown.Ambos); controlador.Close();

Llamar al método Shutdown() antes de cerrar el socket garantiza que no queden datos sin procesar. Este método toma un valor de la enumeración SocketShutdown como parámetro:

    Ambos: detiene tanto el envío como la recepción de datos en el socket.

    Recibir: dejar de recibir datos

    Enviar: dejar de enviar datos

Cliente

Ahora agreguemos un proyecto para el cliente. El esquema general de cómo funciona el cliente en los sockets será ligeramente diferente:

Código de cliente completo:

Usando el Sistema; usando System.Text; utilizando System.Net; utilizando System.Net.Sockets; espacio de nombres SocketTcpClient ( clase Programa ( // dirección y puerto del servidor al que nos conectaremos static int port = 8005; // puerto del servidor static string dirección = "127.0.0.1"; // dirección del servidor static void Main(string args) ( try (IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(dirección), puerto); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // conectarse al socket del host remoto.Connect(ipPoint ); Console .Write("Ingrese el mensaje:"); mensaje de cadena = Console.ReadLine(); // buffer para respuesta StringBuilder builder = new StringBuilder(); = socket.Receive(datos, datos.Longitud, 0); constructor.Append(Encoding.Unicode.GetString(datos, 0), bytes)); mientras (socket.Available > 0); " + builder.ToString()); // cerrar el socket socket.Shutdown(SocketShutdown.Both);

socket.Cerrar();

) catch(Excepción ex) ( Console.WriteLine(ex.Message); ) Console.Read();

) ) )

Para el cliente todo es igual, solo que ahora después de crear el socket, se llama al método Connect(), al que se le pasa la dirección del servidor:

IPEndPoint ipPoint = nuevo IPEndPoint(IPAddress.Parse(dirección), puerto); socket.Connect(ipPoint);

Ahora iniciemos los programas del servidor y del cliente. Consola del cliente:

Ingrese su mensaje: hola mundo respuesta del servidor: su mensaje ha sido entregado

Consola del servidor:

El servidor está funcionando. Esperando conexiones... 22:34: hola mundo .

Aplicación cliente-servidor en un socket de flujo TCP

El siguiente ejemplo utiliza TCP para proporcionar flujos de bytes bidireccionales ordenados y confiables. Construyamos una aplicación completa que incluya un cliente y un servidor. Primero, demostramos cómo construir un servidor usando sockets de flujo TCP y luego una aplicación cliente para probar nuestro servidor.

El siguiente programa crea un servidor que recibe solicitudes de conexión de los clientes. El servidor se construye sincrónicamente, por lo tanto, la ejecución del hilo se bloquea hasta que el servidor acepta conectarse con el cliente. Esta aplicación demuestra un servidor simple que responde a un cliente. El cliente finaliza la conexión enviando un mensaje al servidor.

// SocketServer.cs usando System; usando System.Text; utilizando System.Net; utilizando System.Net.Sockets; espacio de nombres SocketServer ( clase Programa ( static void Main(string args) ( // Establece el punto final local para el socket IPHostEntry ipHost = Dns.GetHostEntry("localhost"); IPAddress ipAddr = ipHost.AddressList; IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000); // Crear un socket Tcp/Ip sListener = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // Asigna el socket al punto final local y escucha los sockets entrantes try ( sListener.Bind(ipEndPoint); ); sListener. Listen(10); // Comienza a escuchar las conexiones mientras (true) ( ​​​​Console.WriteLine("Esperando una conexión en el puerto (0)", ipEndPoint); // El programa se detiene, esperando una controlador de socket de conexión entrante = sListener.Accept(); string data = null; // Esperamos a que el cliente intente conectarse con nosotros byte bytes = nuevo byte en la consola Console.Write("Texto recibido: " + datos + "; \n\n"); // Enviar una respuesta al cliente\ cadena respuesta = "Gracias por la solicitud en " + data.Length.ToString() + "caracteres";

mensaje de byte = Codificación.UTF8.GetBytes(respuesta);

controlador.Enviar(msg);

si (datos.IndexOf("

") > -1) ( Console.WriteLine("El servidor ha terminado la conexión con el cliente."); break; ) handler.Shutdown(SocketShutdown.Both); handler.Close(); ) ) catch (Excepción ex) ( Console.WriteLine (ex.ToString()); ) finalmente ( Console.ReadLine(); ) ) ) )

IPHostEntry ipHost = Dns.GetHostEntry("localhost"); Dirección IP ipAddr = ipHost.AddressList; IPEndPoint ipEndPoint = nuevo IPEndPoint(ipAddr, 11000);

Aquí la clase IPEndPoint representa localhost en el puerto 11000. A continuación, creamos un socket de flujo con una nueva instancia de la clase Socket. Habiendo configurado un punto final local para escuchar conexiones, podemos crear un socket:

Socket sListener = nuevo Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

Transferir DirecciónFamilia especifica los esquemas de direccionamiento que una instancia de la clase Socket puede usar para resolver una dirección.

en el parametro Tipo de enchufe Los sockets TCP y UDP son diferentes. En él podrás definir, entre otras cosas, los siguientes valores:

Dgrama

Soporta datagramas. El valor de Dgram requiere que se especifique Udp para el tipo de protocolo e InterNetwork en el parámetro de familia de direcciones.

Crudo

Admite el acceso al protocolo de transporte subyacente.

Arroyo

Admite tomas de corriente. El valor Stream requiere que se especifique Tcp para el tipo de protocolo.

El tercer y último parámetro especifica el tipo de protocolo requerido para el socket. en el parametro Tipo de protocolo Puede especificar los siguientes valores más importantes: Tcp, Udp, Ip, Raw.

El siguiente paso debería ser asignar el socket usando el método Unir(). Cuando un constructor abre un socket, no se le asigna un nombre, solo se reserva un identificador. Se llama al método Bind() para asignar un nombre al socket del servidor. Para que un socket de cliente identifique un socket de flujo TCP, el programa servidor debe darle un nombre a su socket:

SListener.Bind(ipEndPoint);

El método Bind() vincula un socket a un punto final local. Se debe llamar al método Bind() antes de cualquier intento de llamar a los métodos Listen() y Accept().

Ahora, después de haber creado un socket y haberle asociado un nombre, puede escuchar los mensajes entrantes utilizando el método Escuchar(). En el estado de escucha, el socket escuchará los intentos de conexión entrantes:

SListener.Listen(10);

El parámetro define reserva, que indica el número máximo de conexiones en espera en la cola. En el código anterior, el valor del parámetro permite que se acumulen hasta diez conexiones en la cola.

En el estado de escucha, debe estar listo para aceptar conectarse con el cliente, para lo cual se utiliza el método. Aceptar(). Este método obtiene una conexión de cliente y completa la asociación de nombres de cliente y servidor. El método Accept() bloquea el hilo del programa que realiza la llamada hasta que llega una conexión.

El método Accept() elimina la primera solicitud de conexión de la cola de solicitudes pendientes y crea un nuevo socket para procesarla. Aunque se crea un nuevo socket, el socket original continúa escuchando y puede usarse con subprocesos múltiples para aceptar múltiples solicitudes de conexión de los clientes. Ninguna aplicación de servidor debería cerrar un socket de escucha. Debe continuar trabajando junto con los sockets creados por el método Accept para procesar las solicitudes entrantes de los clientes.

Mientras (verdadero) ( ​​Console.WriteLine("Esperando una conexión en el puerto (0)", ipEndPoint); // El programa se detiene mientras espera una conexión entrante Socket handler = sListener.Accept();

Una vez que el cliente y el servidor han establecido una conexión entre sí, los mensajes se pueden enviar y recibir utilizando los métodos Enviar() Y Recibir() clase Zócalo.

El método Send() escribe datos salientes en el socket conectado. El método Recibir() lee los datos entrantes en el socket de transmisión. Cuando se utiliza un sistema basado en TCP, se debe establecer una conexión entre los sockets antes de ejecutar los métodos Enviar() y Recibir(). El protocolo exacto entre las dos entidades comunicantes debe definirse de antemano para que las aplicaciones cliente y servidor no se bloqueen entre sí al no saber quién debe enviar sus datos primero.

Cuando se completa el intercambio de datos entre el servidor y el cliente, debe cerrar la conexión utilizando los métodos Cerrar() Y Cerca():

Handler.Shutdown(SocketShutdown.Ambos); controlador.Close();

SocketShutdown es una enumeración que contiene tres valores para detener: Ambos- deja de enviar y recibir datos por el socket, Recibir- impide que el socket reciba datos y Enviar- deja de enviar datos por el socket.

El socket se cierra llamando al método Close(), que también establece la propiedad Conectado del socket en falso.

Cliente TCP

Las funciones que se utilizan para crear una aplicación cliente son más o menos similares a las de una aplicación de servidor. Al igual que con el servidor, se utilizan los mismos métodos para determinar el punto final, crear una instancia del socket, enviar y recibir datos y cerrar el socket.

En este ejemplo, desarrollaremos un servidor simple y un programa cliente simple que mantienen un diálogo "pausado" entre sí. Construiremos el cliente según la tecnología. Formularios de Windows, y el servidor - Servicio de Windows. El servidor tendrá un conjunto de respuestas marcadas listas para usar, esperará las solicitudes marcadas de los clientes y les responderá con los mensajes apropiados. Esto nos preparará para crear un sistema aún más complejo: ver dibujos remotos desde la base de datos, del que nos ocuparemos más adelante.

Creando un cliente

Comencemos con un programa cliente que se puede ejecutar en muchas instancias ("http://msdn.microsoft.com/ru-ru/library/system.net.sockets.tcplistener.accepttcpclient.aspx")


Tabla 19.7.
Elemento Propiedad Significado
Forma Texto Cliente
Tamaño 300; 300
Cuadro de lista (Nombre) cuadro de lista
Muelle Arriba
Fuente Arial; 12 puntos
Elementos
  1. ¡Hola!
  2. Lelik
  3. ¿Cómo es la vida?
  4. ¿Salimos hoy?
  5. Pues bien, ¡adiós!
Modo de selección Uno
Tamaño 292; 119
Botón (Nombre) btnEnviar
Tamaño automático Verdadero
Fuente Arial; 10 puntos
Ubicación 96; 127
Tamaño 101; 29
Texto Enviar
Cuadro de texto (Nombre) cuadro de texto
Muelle Abajo
Ubicación 0; 162
multilínea Verdadero
Barras de desplazamiento Vertical
Tamaño 292; 105

Agregaremos las configuraciones necesarias restantes para los elementos del formulario mediante programación.

usando Sistema; usando System.Collections.Generic; usando System.ComponentModel; usando System.Data; usando System.Drawing; usando System.Text; usando System.Windows.Forms; // Espacios de nombres adicionales usando System.IO; utilizando System.Net; utilizando System.Net.Sockets; usando System.Threading; espacio de nombres SimpleClient (clase parcial pública Form1: Form (int puerto = 12000; String hostName = "127.0.0.1"; // cliente TcpClient local = null; // Referencia del cliente public Form1() (InitializeComponent(); // Seleccione el primero elemento de lista listBox.SelectedIndex = 0; listBox.Focus(); // Menú contextual para borrar TextBox ContextMenuStrip contextMenu = new ContextMenuStrip(); textBox.ContextMenuStrip = elemento contextMenuItem = new ToolStripMenuItem ("Borrar"); .MouseDown += new MouseEventHandler(item_MouseDown); // Envía una solicitud y recibe una respuesta private void btnSubmit_Click(object sender, EventArgs e) ( if (listBox.SelectedIndices.Count == 0) ( MessageBox.Show ("Seleccionar mensaje" ); return; ) try ( // Crea un cliente conectado al servidor client = new TcpClient(hostName, port); // Establece los tamaños de los portapapeles tú mismo (¡Opcional!) client.SendBufferSize = client.ReceiveBufferSize = 1024 ; ) catch ( MessageBox.Show ("¡El servidor no está listo!");

devolver; ) // Escribe la solicitud en el protocolo AddString("Cliente: " + listBox.SelectedItem.ToString()); Flujo de red y empaquetarlos en carcasas que sean convenientes para el control de lectura/escritura. El intercambio con el servidor se muestra en el protocolo. Cuadro de texto. Para borrar el protocolo, se creó dinámicamente un menú contextual.

Clase Cliente TCP, que usamos en el código, es un contenedor de alto nivel (y simplificado) alrededor del socket (clase Enchufe). Si se requiere un control de socket de nivel inferior (más detallado), se almacena un enlace en la propiedad TcpClient.Client. Pero como esta propiedad está protegida ( protegido), entonces sólo se puede acceder a él desde un derivado de Cliente TCP clase.

Si ejecuta la aplicación ahora Cliente simple, entonces funcionará, pero cuando intente enviar algo al servidor, se mostrará un mensaje que indica que el servidor no está listo. Aún no hay ningún servidor, creemos uno.

Creando un servidor

Hagamos que el programa del servidor sea residente según la plantilla. Servicio de Windows, como hicimos en el ejemplo anterior, aunque se puede hacer con la interfaz, lo principal es que se ejecute en una sola copia en el ordenador local. Si el programa del servidor está incluido en la red global, entonces con el usado IP y debería ser el único puerto en esta red. Por lo tanto, el uso de la red IP Para la red global es necesario obtener permiso.



usando Sistema; usando System.ComponentModel; usando System.Configuration.Install; usando System.ServiceProcess; espacio de nombres SimpleServer ( // Durante la instalación del ensamblado, el instalador debe llamarse public parcial class Installer1: Installer ( private ServiceInstaller serviceInstaller; private ServiceProcessInstaller serviceProcessInstaller; public Installer1() ( // Crear configuraciones para el servicio serviceInstaller = new ServiceInstaller(); serviceProcessInstaller = new ServiceProcessInstaller( ); // Nombre del servicio para la máquina y el usuario serviceInstaller.ServiceName = "SimpleServerServiceName"; serviceInstaller.DisplayName = "SimpleServer"; iniciar this.serviceProcessInstaller.Account = ServiceAccount.LocalService; this.serviceProcessInstaller.Password = null; this.serviceProcessInstaller.Username = null // Agregar configuración a la colección del objeto actual this.Installers.AddRange(new Installer ( serviceInstaller, serviceProcessInstaller ) ) )

usando Sistema; usando System.Collections.Generic; usando System.Text; // Espacios de nombres adicionales usando System.IO; utilizando System.Net; utilizando System.Net.Sockets; usando System.Threading; usando System.ServiceProcess; usando System.Collections; espacio de nombres SimpleServer ( clase Servicio1: ServiceBase ( servidor TcpListener = null; // Enlace al servidor int puerto = 12000; String hostName = "127.0.0.1"; // dirección IP local localAddr; String respuestas = ( "1. ¿Quién eres?" , "2. ¡Hola, Lelik!", "3. ¡Lo mejor de todo!", "4. Por supuesto, al máximo", "5. ¡Nos vemos por la noche!" localAddr = IPAddress.Parse( hostName);// Convertir a otro formato Thread thread = new Thread(ExecuteLoop); thread.IsBackground = true; thread.Start() ( try ( server = new TcpListener(localAddr, port);/ / Crear un servidor de escucha server.Start(); // Iniciar el servidor String data; // Bucle sin fin de escucha de clientes while (true) ( ​​​​if (!server.Pending()) // La cola de solicitudes está vacía continue; TcpClient client = server. AcceptTcpClient();// Cliente actual // Configuramos los tamaños de los portapapeles nosotros mismos (¡Opcional!) // De forma predeterminada, ambos buffers están configurados en 8192 bytes de tamaño client.SendBufferSize = client.ReceiveBufferSize = 1024;

// Conecte NetworkStream y sumérjalo en shells para mayor comodidad NetworkStream streamIn = client.GetStream(); Flujo de red NetworkStream streamOut = cliente.GetStream();

StreamReader lectorStream = nuevo StreamReader(streamIn); StreamWriter escritorStream = nuevo StreamWriter(streamOut);// Leer los datos de la solicitud = lectorStream.ReadLine(); // Envía la respuesta int index; con clientes. Si algún cliente se conectó a él ( servidor.Pendiente()!=falso), entonces el servidor recupera este cliente usando AceptarTcpClient()- crea un socket para recibir/transmitir con una dirección de retorno lista, crea un flujo bidireccional (o dos unidireccionales), luego lee la solicitud y transmite la respuesta.



Tenga en cuenta que si el código de nuestro programa de servidor no está empaquetado en un hilo separado Hilo(hilo de ejecución), entonces el sistema operativo no iniciará este programa en la ventana de servicios (¡pruébelo!). La razón es que en el código del método Ejecutar bucle() El servidor utiliza un bucle interminable para escuchar una cola de solicitudes de clientes. Si este bucle se deja en el hilo principal de ejecución ( Hilo) aplicación, simplemente entrará en un bucle y no podrá finalizar normalmente. Por lo tanto, colocamos el código con el bucle en un hilo separado (hilo) y lo ponemos en segundo plano para que se cierre junto con el hilo principal de la aplicación (hilo del servidor).

Nota importante

Fluir Flujo de red es de longitud fija de doble cara. Método Obtener corriente() solo establece una conexión de dirección entre los sockets del cliente y del servidor. Pero su longitud real está determinada por el mensaje del remitente. Puede usar un hilo para recibir/transmitir, pero la longitud del mensaje enviado por el servidor no debe exceder la longitud del mensaje que recibió del cliente (¡casi pierdo el ojo!). Es por eso que utilizamos dos flujos en cada lado para la transmisión unidireccional separada entre dos nodos de conexión de red.

Ejemplo 3. Aplicación cliente-servidor para ver imágenes desde una base de datos

En el sencillo ejemplo anterior, nos familiarizamos (un poco) con los principios de creación de aplicaciones de red. Ahora construyamos un ejemplo más complejo, donde el cliente solicita imágenes y el servidor las recupera del almacenamiento y las envía al cliente. EN Ejercicio 7 Hemos desarrollado tres repositorios de imágenes diferentes y tres programas de visualización. En este ejemplo usaremos la base de datos. Imágenes.my2.mdb con dibujos ya hechos y en base a ellos crearemos una aplicación de red (para aquellos que no lo han hecho Ejercicio 7, DB está adjunto en el catálogo. Fuente/Datos).

Construyendo un cliente

Para el cliente, crearemos una aplicación de ventana como WPF con una interfaz de usuario parcialmente tomada de Ejemplo 6 Ejercicios 7.


El servidor no está listo, ¡espera! Estamos intentando contactar Lo siento por los inconvenientes ocasionados...

Para mostrar una pantalla de presentación con el texto de que el servidor no está listo, usamos el elemento cuadro de vista, en el que se colocó otro elemento Borde con contenido de texto. Este “jardín” te permitirá aumentar el salvapantallas en proporción al tamaño de la ventana. Sin embargo, la introducción del elemento cuadro de vista comienza a ralentizar notablemente el rediseño de la interfaz cuando se mueve la ventana, porque intenta recalcular constantemente las escalas de sus elementos secundarios. Asignamos nombres solo a aquellos elementos de la interfaz que vamos a administrar en código de procedimiento.

usando Sistema; usando System.Collections.Generic; usando System.Text; usando System.Windows; usando System.Windows.Controls; usando System.Windows.Data; usando System.Windows.Documents; usando System.Windows.Input; usando System.Windows.Media; usando System.Windows.Media.Animation; usando System.Windows.Media.Imaging; usando System.Windows.Shapes; // Espacios de nombres adicionales para Stream usando System.IO; usando IO = System.IO; // Alias ​​para direccionar Path usando System.Windows.Threading; // Para DispatcherTimer // Espacios de nombres adicionales para Socket //usando System.Net; utilizando System.Net.Sockets; usando System.Collections; // Lista espacio de nombres PicturesClientDB ( clase pública parcial Window1: Window ( int port = 12000; String hostName = "127.0.0.1"; // cliente TcpClient local = null; // Enlace del cliente String sendMessage = "!!!GetNames!!!"; / / Solicitud de una lista (complicada) Char separator = ( "#" ); // Para convertir la respuesta a una matriz de nombres DispatcherTimer timer; // Temporizador // Constructor public Window1() ( InitializeComponent(); // Crear y iniciar un temporizador temporizador = new DispatcherTimer(); timer.Tick += new EventHandler(timer_Tick); timer.Interval = TimeSpan.FromSeconds(1); remitente, EventArgs e) ( Execute(listBox); ) private void listBox_SelectionChanged(objeto remitente, SelectionChangedEventArgs e) ( Execute((ListBox)sender); ) void Execute(ListBox lst) ( // Complete la lista con nombres de imágenes intente ( / / Si el servidor está disponible, cree un cliente client = new TcpClient(hostName, port) catch ( // El servidor no está listo, inicie el cronómetro y salga if (Prompt.Visibility != Visibility.Visible) ( Prompt.Visibility = Visibilidad.Visible; Cerca(); lectorStream.Close(); lista = nueva lista (client.ReceiveBufferSize); // Capacidad incremental Bytes = nuevo Byte; // Tamaño del búfer del socket int count = 0; // Porciones de datos entrantes while ((count = lectorStream.Read(bytes, 0, bytes.Length)) != 0) for (int i = 0; i< count; i++) list.Add(bytes[i]); // Преобразуем в массив результата bytes = new Byte; list.CopyTo(bytes); // Закрываем соединение и потоки, порядок неважен client.Close(); writerStream.Close(); readerStream.Close(); return bytes; } } // Для привязки к ресурсу class Pictures { // Поле public static ImageBrush picture = new ImageBrush(); static Pictures() { // Дежурный рисунок заставки picture.ImageSource = new BitmapImage(new Uri(@"flower2.jpg", UriKind.Relative)); picture.Stretch = Stretch.Fill; picture.Opacity = 1.0D; } // Привязываемое в интерфейсе свойство public static ImageBrush Picture { get { return picture; } } } }

Tenga en cuenta que al mostrar imágenes hemos abandonado el elemento tradicional. Imagen, como se hizo en el ejercicio anterior. Y para variar, lo hicieron de forma totalmente poco convencional (en turco). Ahora mostraremos los dibujos con un pincel. Pincel de imagen en el fondo de un rectángulo Borde a través de un objeto adherido a él Fotos. Por supuesto, en la vida es poco probable que tengas que pervertir de esta manera, pero esta opción puede resultar útil en alguna parte.


La pantalla de presentación aparecerá tan pronto como se detecte que el servidor falta o está detenido. Y una vez que se detecte el servidor, el protector de pantalla desaparecerá. Este mecanismo funcionará inmediatamente gracias a la temporizador del sistema. Sin embargo, todavía no hay un servidor y debería crearse.

Construyendo un servidor de base de datos como servicio
  • Equipo Archivo/Agregar/Nuevo proyecto agregar a la solución Flujo de red nuevo proyecto llamado ImágenesServidorDB tipo Servicio de Windows


usando Sistema; usando System.Collections.Generic; usando System.ComponentModel; usando System.Data; utilizando System.Diagnostics; usando System.ServiceProcess; usando System.Text; // Espacios de nombres adicionales para ADO.NET usando System.Data.OleDb; usando System.Data.Common; // Espacios de nombres adicionales usando System.IO; utilizando System.Net; utilizando System.Net.Sockets; usando System.Threading; usando System.Collections; espacio de nombres PicturesServerDB (clase pública parcial Servicio1: ServiceBase (int puerto = 12000; String hostName = "127.0.0.1"; // dirección IP local localAddr; TcpListener server = null; // Enlace al servidor String separator = "#"; // Nombres de separador en la línea de respuesta String connectString; // Cadena de conexión de la base de datos public Service1() ( // Extrae la cadena de conexión de la base de datos del archivo App.config al campo connectString = System.Configuration.ConfigurationManager. ConnectionStrings["PicturesDB"]. ConnectionString; // Convertir la IP a otro formato localAddr = IPAddress.Parse(hostName); // Ejecutar en un nuevo hilo (hilo) Thread thread = new Thread(ExecuteLoop).IsBackground = true; ) ( try ( server = new TcpListener(localAddr, port);// Crear un servidor-escucha server.Start();// Iniciar el servidor // Bucle sin fin de escucha de clientes mientras (verdadero) ( ​​// Verificar la cola de conexión if (!server .Pending()) // La cola de solicitudes está vacía continuar; NET conexión OleDbConnection = nueva OleDbConnection (cadena de conexión);




Arriba