Este documento describe una aplicación cliente-servidor para realizar operaciones CRUD (crear, leer, actualizar, borrar) en una tabla de una base de datos. La aplicación cliente permite insertar, consultar, actualizar y eliminar registros mediante una interfaz gráfica. Envía las solicitudes al servidor, el cual las procesa y envía una respuesta. El servidor se conecta a la base de datos para realizar las operaciones y enviar notificaciones al cliente.
1. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
Ejemplo de aplicación Cliente-Servidor
La aplicación cliente consiste en un una GUI (capa de presentación), en esta se captura la
tabla “Region” de la BD “Northwind” de SQL Server.
La Tabla Region consiste en únicamente dos campos:
RegionID: Campo numérico entero
RegionDescription: Como campo string.
Las funcionalidades del GUI cliente consisten en Agregar, Eliminar, Actualizar y Consultar la
información de la tabla Region a través del servidor.
La aplicación servidor (capa de negocio) recibe una cadena de texto que contiene un
“layout” que viene separados por punto y coma “;” y contiene primero el tipo de operación,
después el valor de RegionID, y finalmente el valor de RegionDescription. Las operaciones
permitidas son CONSULTA, INSERT, DELETE, y UPDATE.
El servidor descompone la cadena de texto y la envía a un arreglo desde el cual puede
obtener los valores de los campos de la tabla, y enviar la operación correspondiente al SQL
Server por medio de los controles de base de datos ya vistos en clase.
Finalmente, el servidor envía un acuse de recibo al cliente o bien el resultado de la consulta
para ser desplegado.
El Cliente despliega el mensaje del acuse de recibo o la consulta solicitada en su caso.
El SQL Server (capa de datos) contiene una instancia a la que el servidor se conecta y realiza
las consultas y modificaciones en la BD.
Solo el servidor tiene conectividad con SQL Server, el cliente no conoce la instancia ni tiene
capacidad para conectarse.
Ambas aplicaciones tienen validaciones básicas por lo que si se usan para otras apps deben
ser validados.
GUI del Cliente Consola del Servidor
2. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
// Programa Cliente
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace ClienteGUI
{
public partial class Form1 : Form
{
TcpClient tcpclnt = new TcpClient();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//conectar();
} // fin onUpload
private void conectar()
{
try
{
TcpClient tcpclnt = new TcpClient();
// utilizar para este caso IP local ya que
// cliente y servidor corren en la misma PC
mensaje.Text = "Conectandose con el servidor...";
tcpclnt.Connect("127.0.0.1", 8001);
mensaje.Text = "";
}
catch (Exception er)
{
MessageBox.Show("Error al conectar con Servidor: " +
er.Message.ToString());
Application.Exit();
}
}// fin conectar()
private void enviar(string operacion)
{
string[] subcadena;
string acuse = "";
int n = 0;
if ((codigo.Text.ToString().Length <= 0) ||
(!int.TryParse(codigo.Text.ToString(), out n)))
{
MessageBox.Show("El código de la región no es válido",
"Error");
return;
}
TcpClient tcpclnt = new TcpClient();
3. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
try
{
// utilizar para este caso IP local ya que
// cliente y servidor corren en la misma PC
mensaje.Text = "Conectandose con el servidor...";
tcpclnt.Connect("127.0.0.1", 8001);
mensaje.Text = "";
// formar la cadena que se enviará al servidor, contiene:
// tipo de operacion: 3 caracteres
// codigo de la region: 5 caracteres
// descripción de la región: 40 caracteres
string cadena = operacion + ";" +
codigo.Text.ToString() + ";" +
descrip.Text.ToString() + ";";
Stream stm = tcpclnt.GetStream();
// convertir cadena a ascii para transmitirla
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(cadena);
mensaje.Text = "Transmitiendo informacion...";
stm.Write(ba, 0, ba.Length);
// recibir acuse, se debe converir a string
byte[] bb = new byte[100];
int k = stm.Read(bb, 0, 100);
for (int i = 0; i < k; i++)
{
acuse = acuse + Convert.ToChar(bb[i]);
}
tcpclnt.Close();
mensaje.Text = "";
//determinar el tipo de accion del SQL
}
catch (Exception er)
{
MessageBox.Show("Error al conectar con Servidor: " +
er.Message.ToString());
Application.Exit();
}
subcadena = acuse.Split(';');
try
{
if (subcadena[0].ToString().ToUpper().Trim() == "CONSULTA")
{
if (subcadena[1].ToString().Trim() != "-1")
{
codigo.Text = subcadena[1].ToString().Trim();
descrip.Text = subcadena[2].ToString().Trim();
}
else
{
MessageBox.Show("Registro no encontrado");
codigo.Text = "";
descrip.Text = "";
}
}
else
{
int registros = Int32.Parse(subcadena[1].ToString().Trim());
4. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
if (registros >0)
{
// si regresa mayor de ceros corresponde
// al numero de registros que fueron afectados
mensaje.Text="Operación realizada: "+
subcadena[0].ToString().Trim();
}
else
if (registros==0)
{
// Si regresa cero quiere decir que
// ningun registro fue afectado
MessageBox.Show("Registro no Encontrado: ",
subcadena[0].ToString().Trim());
}
else
if (registros < 0)
{
// si regresa menor de cero quiere decir
// que hubo un errr y la operacion no se realizo
MessageBox.Show("La operación no pudo ser realizada: ",
subcadena[0].ToString().Trim());
}
}
}
catch (Exception er)
{
MessageBox.Show("Error al conectar con Servidor: " +
er.Message.ToString());
}
}// fin enviar()
private void consultar_Click(object sender, EventArgs e)
{
enviar("CONSULTA");
}
private void salir_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void insertar_Click(object sender, EventArgs e)
{
enviar("INSERT");
}
private void eliminar_Click(object sender, EventArgs e)
{
enviar("DELETE");
}
private void modificar_Click(object sender, EventArgs e)
{
enviar("UPDATE");
}
} // fin clase
} // fin namespace
5. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
// Programa Servidor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Data.SqlClient;
using System.Data;
namespace SvrP
{
class Servidor
{
static string acuse = "";
static Socket s;
static IPAddress ipAd;
static TcpListener myList;
static ASCIIEncoding asen;
static string operacion = "";
static string cadSQL = "";
static string[] subcadena;
static string conectSQL = @"Server=.sqlexp; database=Northwnd; " +
"Integrated Security=TRUE";
static void Main(string[] args)
{
Console.WriteLine("*** Server Application ***");
// Se ejecuta el Listener en un ciclo para que se repita
// de forma indefinida y acepte tantas conexiones sean posibles
while (true)
{
try
{
// asignar IP local a la app server
ipAd = IPAddress.Any;
// Utilizamos la dirección local e igual en el cliente
// Inicializar Listener
myList = new TcpListener(ipAd,8001);
myList.Start();
Console.WriteLine("Servidor corriendo en Puerto 8001...");
Console.WriteLine("Local End Point:" + myList.LocalEndpoint);
Console.WriteLine("Esperando conexión...");
s = myList.AcceptSocket();
Console.WriteLine("Conexión recivida desde: " +
s.RemoteEndPoint);
// Guardar en una variable la información recibida del cliente
byte[] b = new byte[500];
int k = s.Receive(b);
Console.WriteLine("Recievido...");
// Convierte e iprime el contenido de la cadena
string cadena = "";
for (int i = 0; i < k; i++)
{
cadena = cadena + Convert.ToChar(b[i]);
}
Console.WriteLine(cadena);
//determinar el tipo de accion del SQL
6. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
subcadena = cadena.Split(';');
operacion = subcadena[0].ToString().ToUpper().Trim();
}
catch (Exception e)
{
acuse = operacion.ToUpper().Trim() + ";-1;" +
e.StackTrace + ";";
enviarAcuse(acuse);
return;
}
try
{
if (operacion == "CONSULTA")
{
string consulta="Select * from [NORTHWND].[dbo].[Region] "+
" where regionID=" + subcadena[1].ToString().Trim();
// Conectarse con el SQL Server
SqlConnection conectar = new SqlConnection(conectSQL);
conectar.Open();
SqlDataAdapter adaptador = new SqlDataAdapter();
adaptador.SelectCommand = new SqlCommand(
consulta, conectar);
DataSet ds = new DataSet();
adaptador.Fill(ds);
conectar.Close();
if (ds.Tables[0].Rows.Count > 0)
{
for (int i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
acuse = "CONSULTA;" +
ds.Tables[0].Rows[i].ItemArray[0].ToString()+"; "+
ds.Tables[0].Rows[i].ItemArray[1].ToString()+"; ";
}
}
else
{
// un registro negativo indica al cliente
// que no se encontró la información
acuse = "CONSULTA;-1;;";
}
}
else
{
SqlConnection cm = new SqlConnection();
cm.ConnectionString = conectSQL;
cm.Open();
if (operacion == "DELETE")
{
cadSQL = "delete [NORTHWND].[dbo].[Region] " +
" where regionID=" +
subcadena[1].ToString().Trim();
}
else
if (operacion == "INSERT")
{
cadSQL="insert into [NORTHWND].[dbo].[Region] values(" +
subcadena[1].ToString().Trim() +
7. Mtro José Antonio Sandoval Acosta.
Docente de Ingeniería en Sistemas Computacionales.
México. 18 de marzo de 2017.
", '" + subcadena[2].ToString().Trim() +
"')";
}
else
if (operacion == "UPDATE")
{
cadSQL = "update [NORTHWND].[dbo].[Region] set " +
"regionDescription='" +
subcadena[2].ToString().Trim() +
"' where regionID=" +
subcadena[1].ToString().Trim();
}
SqlCommand cmd = new SqlCommand(cadSQL, cm);
// regresa numero de registros afectados
int x = cmd.ExecuteNonQuery();
cm.Close();
acuse = operacion + ";"+x.ToString()+";;";
Console.WriteLine(operacion+
"; Registros afectados: "+x.ToString());
}
// Ejecutar la cadena como comando del SQL
// solo aplica para Insert, Delet y UPdate
Console.WriteLine(cadSQL);
// Enviar señal exitosa al Cliente
// Esta señal puede variar dependiendo del proceso ejecutado
asen = new ASCIIEncoding();
s.Send(asen.GetBytes(acuse));
Console.WriteLine(acuse + "nAcuse enviado");
s.Close();
myList.Stop();
}
catch (Exception e)
{
acuse = operacion + ";-1;" + "nError... " +
e.StackTrace + ";";
enviarAcuse(acuse);
return;
}
} // fin ciclo
} // fin main
private static void enviarAcuse(string acuse)
{
asen = new ASCIIEncoding();
ipAd = IPAddress.Any;
myList = new TcpListener(ipAd, 8001);
myList.Start();
s = myList.AcceptSocket();
s.Send(asen.GetBytes(acuse));
s.Close();
myList.Stop();
Console.WriteLine(acuse);
return;
}// fin metodo enviarAcuse
} // fin clase
} // fin namespace