Cualquier cosa que valga la pena se hace en equipo

Cualquier cosa que valga la pena se hace en equipo

sábado, 26 de febrero de 2011

Learning through Application

Victor R. Basili

University of Maryland and Fraunhofer Center - Maryland

The good news is that empirical studies have finally become recognized as an important component of the software engineering discipline. One sees more and more empirical studies and experiments in the literature. The bad news is that these studies are often used as confirming or not confirming the effectiveness of some method, technique, or tool, rather than as part of the process of discovery process. The experiment is an add-on, we do the study after the concept is considered complete, rather than applying the method, technique or tool and learning from the application about how to evolve the concept. This is the basis of the scientific method; theories are tested and evolved over time. In the software engineering discipline, where the theories and models are still in the formative stages and processes are applied by humans a part of a creative process, observing the application or performing exploratory studies become an important step in the evolution of the discipline.

Software engineering has several characteristics that that distinguish it from other disciplines. Software is developed, in the creative, intellectual sense, rather than produced in the manufacturing sense. Software processes are development processes not production processes. They are not replicated over and over again. This aspect of the discipline is probably the most important. This affects greatly how we learn. We need to always be on the lookout for the effect of context variables. Since it a human-based discipline there will always be variation in study results and we will never be able to control or even identify all the context variables. It creates a need for continual experimentation as we must understand how to modify and tailor processes for people.

The context issue is more than just the people; all software is not the same and all software development environments are different. One consequence of this is that process is a variable, goals are variable, etc. That is, we need to select the right processes for the right goals for the environment we are analyzing. So, before we decide how to study something we need to know something about the environment and the characteristics of the thing we are about to build.

Also, the software engineering discipline is still quite immature in the sense that there is a lack of models that allow us to reason about the process, the product and their relationships. This compounds the non-visible nature of software. It intensifies the need to learn from the application of the ideas in different situations and the requirement to abstract from what we see.

Add to this the fact that developing models of our experiences for future use (reuse) requires additional resources in the form of money, organization support, processes, people, etc. Building models, taking measurements, experimenting to find the most effective technologies, and feeding back information for corporate learning, cost both time and money. These activities are not a by-product of software development. If these activities are not explicitly supported, independent of the product development, they will not occur and we will not make quality improvements in the development process.

All this makes good experimentation difficult and expensive. Experiments can only be confirmatory in the small and are subject to problems in understanding scale-up, the integration of one process with another, the understanding of the effect of context variables, etc.

I believe we need to focus more attention on informal exploratory studies that provide insights, coupled, when appropriate, with more formal empirical studies to test out pieces of the whole that can be added to the tapestry that helps make the discipline clear. I believe that the study of the software engineering discipline is exploratory and evolutionary. It follows the scientific method but because of its nature, real experiments are not always possible or useful.

I like to say that the study of software engineering is a laboratory science; and it is a big science. So the laboratory is quite grand and we need methods that support the exploratory nature of this big science. The discipline cannot be understood only by analysis. We need to learn from application whether relationships hold, how they vary, what the limits of various technologies are, so we can know how to configure process to develop software better.

We need to take advantage of all opportunities to explore various ideas in practice, e.g., test its feasibility, find out if humans can apply it, understand what skills are required to apply it, test its interaction with other concepts. Based upon that knowledge, we need to refine and tailor it to the application environment in which we are studying it so it can be easily transferred into practice. So, we need to try out our ideas in practice and evolve them, even before we can build models. We are an exploratory science – we are more dependent on empirical application of methods and techniques than many disciplines and we need to share the results.

Time is an important aspect in the application of the scientific method to software engineering; there needs to be many applications of a process, in different environments, with each application providing a better understanding of the concepts and their interaction. Over time all context variables need to be considered and many of them don't even pop up until we have seen the application of the approach in practice a by different people in different sites.

Context Counts: Position Paper for SEMAT


 

By Scott W. Ambler

Chief Methodologist/Agile, IBM Rational


 

I believe that for the SEMAT endeavor to be successful that as a community we need to come to a consensus regarding both our philosophical foundation and our scope. Ivar Jacobson, Bertrand Meyer, and Richard Soley have made good inroads towards addressing these issues [1, 2] but we still have some work ahead of us.


 

My main points:

  1. Practices are contextual, never "best"
  2. We must go beyond practices
  3. We're more successful than we think
  4. We need to reuse existing resources


 

1. Practices are Contextual, never "Best"

We must define the context in which practices will be applied, because the context determines the applicability of the practice as well as how it is tailored. An approach which works well for a medium-sized co-located team in a regulatory compliance situation is likely to fare poorly for a small distributed team developing an informational website. Furthermore context is particularly important for the research behind the practices, because without a clear indication as to the context in which the practice was evaluated it will be very difficult for practitioners to identify which strategies are best suited for them. At IBM Rational we've been applying the 1+8 scaling factors of the Agile Scaling Model (ASM) [3] to help communicate the context faced by project teams. A tenth factor, paradigm, is implied. These factors are:

  1. Life cycle scope. Is your focus on the construction life cycle? On the delivery life cycle? On the full system life cycle (include project identification activities, production, and retirement)? On the enterprise IT life cycle?
  2. Team size. The strategy followed by a team of 7 people will be different than a team of 25, than a team of 50, than a team of 200, and so on.
  3. Geographical distribution. A co-located team will work differently than a team distributed across several cubes on the same floor, which in turn works differently than a team distributed across different locations within the same city, which works differently than an internationally distributed team.
  4. Regulatory compliance. A team which needs to conform to the FDA CFR 21 regulations will work differently than a team which doesn't need to do so. A team working in an ISO 9000 compliant organization will work differently than a team working in another organization.
  5. Domain complexity. A team addressing a very straightforward problem, such as developing a data entry application or an informational Web site, will work differently than a team building an air traffic control system.
  6. Organizational distribution. A team made up of people working for the same division of an organization works differently than a team made up of people from different divisions which in turn works differently than teams made up of people from several divisions. Teams with on-site contractors work differently than teams where some of the work is outsourced to an external organization.
  7. Technical complexity. Teams building new systems from scratch will work differently than those working with legacy systems and data. Teams working with a single technology platform will work different than those working with several platforms. Teams building only software will work differently than systems engineering teams building both software and hardware.
  8. Organizational complexity. Teams working in an organization with a flexible culture will work differently than teams working in one with a rigid culture. Teams working in organizations with homogenous cultures will work different than teams in heterogeneous cultures. Teams working in organizations with cultures that are pro-IT will work differently than teams in organizations where this isn't the case.
  9. Enterprise discipline. Teams working in organizations with effective enterprise disciplined (enterprise architecture, portfolio management, governance, asset management, administration, …) will work differently in organization where this isn't the case.
  10. Paradigm. Teams following an agile paradigm will work differently than those following a traditional/serial paradigm which will work differently than those following an ad-hoc paradigm.


 

These factors affect how you address kernel elements. For example, I'll be so bold as to assert that there will be some sort of requirements elicitation element in the kernel. I recently wrote about how to scale agile requirements strategies by working through the ASM scaling factors and describing how each factor affects your approach [4]. The various factors affected the timing of elicitation, the specification strategy, the tooling strategy, the collaboration strategy, and other aspects of requirements work. This is just one example, but the same holds true for architecture, quality, development, management activities, and other potential kernel elements. The SEMAT kernel needs to recognize the contextual factors faced by organizations and of individual development teams.


 


 

2. We Must Go Beyond Practices

Although there's been a lot of discussion around practices we've also recognized that there is a lot more to it than that. At IBM Rational we've found that to be successful you must address the "5 Ps" of IT [5]:

  1. People. People and the way they work together have a greater effect on the outcomes of a project than the processes they're following or the products (tools and technologies) that they're using. People issues include having visible executive sponsorship, building an environment of trust, empowering staff, focusing on leadership as well as management, recognizing that the primary gating factor when improving processes is people's ability to absorb change, and promoting a cross-discipline strategy at both the team and individual levels.
  2. Principles. We've found both internally within IBM as well as with many of our customers that there is a need to define a common set of principles to provide a consistent foundation to enable effective teamwork and continuous process improvement. These principles help to guide people's decisions when their processes and practices don't directly address the situation which they find themselves in.
  3. Practices. A practice is a self-contained, deployable component of a process.
  4. Products. This includes the technologies – such as databases, application servers, networks, and client platforms – and tools such as integrated development environments, testing tools, and project planning tools used to create solutions for stakeholders.
  5. Processes. The previous 4Ps do not exist in a vacuum, we need some sort of glue to help piece all of this together. Minimally this glue is a lifecycle although more often than not it is a full process or method.


 

I believe that these five issues must be addressed by the scope definition of the SEMAT initiative.


 

3. We're More Successful Than We Think

My experience, backed up by recent surveys [6], shows that the way that organizations define project success vary based on their context, and as a result I'm not convinced that our track record is as bad as we think it is. My surveys which ask people how their organization actually defines success reports higher success rates than studies which enforce their own definition of success on respondents. I am convinced that a definition of on time, on budget, and on scope compared against up-front promises is clearly not appropriate for most project teams. Perhaps one thing that the SEMAT initiative can do is start educating people on the futility of this strategy. If the people applying the SEMAT kernel cannot reasonably measure success, how can SEMAT in turn ever be seen as successful itself?


 


 

4. We Need to Reuse Existing Resources

There is a significant amount of practice and process-oriented intellectual property (IP) available via open source and similar licensing strategies. For example, the Eclipse Process Framework (EPF) at www.eclipse.org/epf/ includes both process tooling and IP which we could choose to leverage free of charge. This IP includes descriptions of Extreme Programming (XP), Scrum, the Open Unified Process (OpenUP), various agile practices, and other material. Of course there are many more examples of process/practice IP available under Creative Commons licenses. How can SEMAT be successful if we strive to reinvent the wheel?


 

5. References

  1. Jacobson, I. Meyer, B. and Soley, R. (2009). The SEMAT Charter – A Proposal. http://sematblog.wordpress.com/2009/12/19/the-semat-charter-a-proposal/
  2. Jacobson, I. Meyer, B. and Soley, R. (2009). What is the scope of the kernel?
    http://sematblog.wordpress.com/2009/12/18/what-is-the-scope-of-the-kernel/
  3. Ambler, S.W. (2009). The Agile Scaling Model (ASM): Adapting Agile Methods for Complex Environments. ftp://ftp.software.ibm.com/common/ssi/sa/wh/n/raw14204usen/RAW14204USEN.PDF
  4. Ambler, S.W. (2009). Agile Requirements at Scale. https://www.ibm.com/developerworks/mydeveloperworks/blogs/ambler/entry/agile_requirements_at_scale
  5. Ambler, S.W. (2010). Scaling Agile: An Executive Guide. To be published Feb 1 at www.ibm.com.

Ambler, S.W. (2006). Surveys Exploring the Current State of Information Technology Practices.
http://www.ambysoft.com/surveys/

domingo, 23 de enero de 2011

Algo mas sobre SOCKETS y Ports




Manual del

Lenguaje de programación

C#

Parte VI-A


Sockets y Ports






OBJETIVO DEL MANUAL 3

OBJETIVO DEL MANUAL 3

Sockets y Ports 3

SOCKET 4

¿Con que contamos en C#? 6

Procesos de un solo thread y sincrónicos.- 7

Procesos de mas de un thread y asincrónicos.- 8

Usar un socket para comunicaciones con el protocolo HTTP 8

El protocolo HTTP 8

Y lo ejecutamos, vemos la salida de texto que se muestra a continuación: 16

Usar sockets para armar un programa de mensajería entre pares (chat) 18

Codigo en Form1.cs 20

Código en program.cs 21

Código en Talker.cs 23

Como ejecutar la aplicación 32

Sockets y Ports 33

El protocolo FTP 33



OBJETIVO DEL MANUAL


Sockets y Ports

Un Socket es una abstracción.-

Representa un punto de intercomunicación entre dos procesos (por ejemplo cliente y servidor). Los procesos pueden estar en el mismo host, nodo o computadora o en nodos diferentes.-

En un host pueden correr diferentes procesos brindando diferentes servicios. Por ejemplo servicios de HTTP, de Telnet, de FTP, de SMTP, etc. Cada uno de estos procesos tiene una dirección que lo identifica unívocamente respecto de los otros. Esta dirección se llama PORT.

Generalmente el servicio de HTTP se brinda en el port 80, el de Telnet en el 23, FTP en el 21 y SMTP en el 25. Decimos entonces que un PORT es una dirección relativa. Identifica al proceso pero no identifica al host en el que corre dicho proceso.

En una red TCP/IP como Internet cada host tiene una identificación única llamada número IP.

Un SOCKET es un número IP + un PORT. Por lo tanto es una dirección absoluta ya que identifica unívocamente un proceso en toda la red.


SOCKET

La especificación Windows Sockets define una interfaz de programación en Windows basada en el paradigma de sockets del BSD (Berkeley Software Distribution).-

Esta biblioteca, escrita en lenguaje C por ingenieros, profesores y estudiantes de la Universidad de Berkeley, fue pensada para la comunicación entre procesos que se ejecutan en la misma computadora o en computadoras de una misma red.-

EL software fue escrito originalmente como una API (Application Programming Interface) y el código que figura a continuación es un pequeño esqueleto que muestra como es la secuencia de inicio y cierre de un proceso de comunicación, pensando en un solo thread de ejecución:


#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <stdio.h>

#include <stdlib.h>

#include <strings.h>

int main()

{

//Inicio

int32_t i32SocketFD = socket(AF_INET, SOCK_STREAM, 0);


if(0 == i32SocketFD)

{

printf("no se puede crear el socket");

exit(-1);

}

struct sockaddr_in stSockAddr;

bzero(&stSockAddr, sizeof(stSockAddr));


stSockAddr.sin_family = AF_INET;

stSockAddr.sin_port = htons(1100);

stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);


//Enlace

if(-1 == bind(i32SocketFD,(struct sockaddr*) &stSockAddr, sizeof(stSockAddr)))

{

printf("error: bind fallido");

exit(-1);

}


//Escucha

if(-1 == listen(i32SocketFD, 10))

{

printf("error: listen fallido");

exit(-1);

}


for(; ;)

{

int32_t i32ConnectFD = accept(i32SocketFD, NULL, NULL);


if(0 > i32ConnectFD)

{

printf("error accept fallido");

exit(-1);

}

// acá es donde se debe escribir el código especifico ...

…..

//cierre

shutdown(i32ConnectFD, 2);

close(i32ConnectFD);

}

return 0;

}


¿Con que contamos en C#?

En C#, encontramos la funcionalidad de esta especificación, encapsulada en una clase llamada Socket, que es la que provee los métodos para la comunicación a través de la red así como la transferencia (sincrónica o asíncrona de datos).-

La clase Socket se encarga de proveer métodos y propiedades para la comunicación en red.-

Lo realmente interesante de esta clase es que encapsula la complejidad de la comunicación y una vez establecida el manejo de lo entrante y saliente se hace manejando flujos (streams).-

Estos flujos de comunicación pueden ser sincrónicos o asíncronos.-

Procesos de un solo thread y sincrónicos.-

Igual que en el ejemplo en lenguaje C visto al inicio de este manual, para procesos de un solo thread podemos utilizar los métodos específicos para modo de operación asíncrona: Listen(), Accept(), Bind(), Send() y Receive().-

Protocolo TCP

Estos métodos se utilizan en protocolos orientados a conexión (connection-oriented) como por ejemplo TCP.-

En este caso el servidor de la comunicación puede escuchar los pedidos con el método Listen .-

EL método Accept() procesa cualquier pedido de conexión entrante y retorna un Socket que es el que se utilizara para comunicar datos con el host remoto.-

Es este Socket el que se usa para llamar a los métodos Send() o Receive().-

Como en el caso general se desea indicar la dirección IP y el numero de puerto, se debe llamar al método Bind(), previo a llamar a Listen().-

En caso de no seguir esta secuencia, el proveedor del servicio subyacente asignara estos valores por nosotros. Es posible luego obtener la dirección IP y port asignados a nuestro Socket, del modo recién indicado.-

En el caso de querer conectarse a un servidor que esta escuchando requisiciones, se debe llamar al método Connect() y luego utilizar Send() y Receive() para envío y recepción de datos.-

// crear un socket

Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

// enlazar el socket que escucha

IPAddress hostIP = (Dns.Resolve(IPAddress.Any.ToString())).AddressList[0];

IPEndPoint ep = new IPEndPoint(hostIP, port);

listenSocket.Bind(ep);

// escuchar

listenSocket.Listen(backlog);


Protocolo UDP

Si usamos un protocolo como UDP (protocolo sin conexión: connectionless), entonces no es necesario "escuchar" para conectarse. El método ReceiveFrom() es el que debe usarse para recibir los datagramas entrantes y el método SendTo() es el que debe usarse para el envio de datagramas al host remoto.

Procesos de mas de un thread y asincrónicos.-

Para procesar comunicaciones utilizando threads separados, existen una serie de mtodos pensados al efecto, es decir en un modo de operacion asincrono.-

Protocolo TCP

Cuando se utilizan protocolos orientados a la conexión (connection-oriented) como TCP, entonces debe utilizar los métodos Socket(), BeginConnect() y EndConnect() para comenzar las tareas de conexiones con el server que atiende los pedidos.-

El asincronismo en el envío y la recepción de datos (en los casos donde trabajamos con mas de un thread) necesita del uso de los métodos específicos de comunicación asincrónica. Estos son: BeginSend(),EndSend() , BeginReceive() y EndReceive(). Los pedidos de conexión entrante los debe procesar con los métodos: BeginAccept() y EndAccept().

Si estamos usando UDP entonces los métodos adecuados son para enviar datagramas son BeginSendTo() y EndSendTo() y para la recepción se debe usar BeginReceiveFrom() y EndReceiveFrom().

Usar un socket para comunicaciones con el protocolo HTTP

El ejemplo siguiente utiliza una clase Socket para enviar y recibir respuestas de un servidor HTTP.

Pensemos en un servidor WEB que esta corriendo en algún punto de la red.

El modo en el que escribimos el código requiere de algunos conceptos de cómo trabaja el protocolo HTTP, damos un repaso que consideramos adecuado.-

El protocolo HTTP

El Protocolo de Transferencia de Hipertexto (Hypertext Transfer Protocol) es un protocolo para comunicación entre un nodo cliente y otro nodo servidor.-

Su uso central es en la WEB, pero aclaremos que un servidor HTTP, puede recibir pedidos y enviar información sin necesidad de la existencia de browsers. Es decir es un mecanismo de comunicación que puede llegar a utilizarse en procesos puramente batch , no solo para interactivos.-

El protocolo esta montado sobre el servicio de conexión TCP/IP.

HTTP se basa en sencillas operaciones de solicitud/respuesta.

Un cliente establece una conexión con un servidor y envía un mensaje con los datos de la solicitud.-

El servidor responde con un mensaje similar, que contiene el estado de la operación y su posible resultado.-

La primera parte de una transacción HTTP es cuando un cliente crea y envia una solicitud al servidor.

Por ejemplo:

GET /index.html /HTTP 1.1

Es una pedido absolutamente correcto para un servidor HTTP activo

Existen tres verbos básicos que un cliente puede utilizar para dialogar con el servidor: GET, para recoger un objeto, POST, para enviar información al servidor y HEAD, para solicitar las características de un objeto (por ejemplo, la fecha de modificación de un documento HTML).

Todas las operaciones pueden adjuntar un objeto o recurso sobre el que actúan; cada objeto Web (documento HTML, fichero multimedia o aplicación CGI) es conocido por su URL (Uniform resources location podría concebirse como la extensión del concepto de nombre completo de un archivo (path)).

Cada operación HTTP implica una conexión con el servidor, que es liberada al término de la misma. (Esa es la verdadera esencia de Internet ¡!)

Es decir, en una operación se puede recoger un único objeto y no se mantienen estados.

Cada petición de un cliente a un servidor no es influida por las transacciones anteriores. El servidor trata cada petición como una operación totalmente independiente del resto.

El acceso a los servidores HTTP se realiza a través de las URLs, que empaquetan toda la información necesaria para localizar un determinado recurso en Internet. Cuando el servidor recibe una URL, a través del protocolo HTTP, la decodifica y devuelve al cliente la información correspondiente, que puede ser de dos tipos, en función del contenido de la URL:

Información estática: que se publica a base de situar los ficheros que la contienen en unos directorios especiales que define cada servidor. Este es el procedimiento que se ha utilizado hasta ahora.

Información dinámica: en este caso, el servidor HTTP arranca una aplicación utilizando la propia URL como parámetro de ejecución. El resultado de la ejecución de la aplicación será devuelto al cliente

El término 'información dinámica' se amplia cada vez más, a medida que se dispone de nuevas aplicaciones y tecnologías de publicación de información.

A través de estas nuevas tecnologías, es posible generar vistas a través del Web de elementos tan dispares como hojas de cálculo, bases de datos, agendas de reuniones, correos electrónicos e incluso periódicos completos, que se actualizan automáticamente en función de los cambios en el contenido de información original.

using System;

using System.Text;

using System.IO;

using System.Net;

using System.Net.Sockets;


public class mySocket

{

private static Socket connectSocket(string server, int port)

{

Socket s = null;

IPHostEntry iphe = null;

try

{

//

iphe = Dns.Resolve(server);

// Esta iteracion a traves de la lista de direcciones ( AddressList) es para obtener la

//llamada familia de direcciones ( AddressFamily) soportada.Esto evitara una excepcion en el caso

//en que la dirección IP no sea compatible con dicha familia.-

foreach (IPAddress ipad in iphe.AddressList)

{

IPEndPoint ipe = new IPEndPoint(ipad, port);

Socket tmpS =

new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

tmpS.Connect(ipe);


if (tmpS.Connected)

{

s = tmpS;

break;

}

else

continue;

}

}


catch (SocketException e)

{

Console.WriteLine("SocketException !!!");

Console.WriteLine("Origen : " + e.Source);

Console.WriteLine("Mensaje : " + e.Message);

}

catch (Exception e)

{

Console.WriteLine("Exception atrapada!!!");

Console.WriteLine("Origen : " + e.Source);

Console.WriteLine("Mensaje : " + e.Message);

}

return s;


}


// este método consulta el contenido de la pagina Home del servidor indicado.

// y muestra el contenido recibido

private static string socketSendReceive(string server, int port)

{


//Preparamos para escribir al server

Encoding ASCII = Encoding.ASCII;

string Get = "GET / HTTP/1.1\r\nHost: " + server +

"\r\nConnection: Close\r\n\r\n";

Byte[] ByteGet = ASCII.GetBytes(Get);

Byte[] RecvBytes = new Byte[256];

String strRetPage = null;


// Ahora creamos un socket como conneccion con el server y el port indicado

Socket s = connectSocket(server, port);


if (s == null)

return ("Conneccion fallida");


// Ahi viaja el pedido al servidor.

s.Send(ByteGet, ByteGet.Length, 0);


// Aca viene la respuesta de la pagina home del server

Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);


// Leer los primeros 256 bytes.Esto lo determina el uso de

//RecvBytes.Length en el método Receive

strRetPage = "Pagina Default HTML en el " + server + ":\r\n";

strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);


while (bytes > 0)

{

bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);

strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);

}

return strRetPage;

}


public static void Main(string[] args)

{

string host;

int port = 91;


if (args.Length == 0)

// si no hay server indicado, usamos el nombre del host.

host = Dns.GetHostName();

else

host = args[0];

string result = socketSendReceive(host, port);

Console.WriteLine(result);

}

}

Vamos a hacer la prueba de este programa conectándolo contra un server WEB TOMCAT.


En mi equipo, como se observa, esta levantado en el puerto 8088.-

Si ejecuto el programa, colocando como parámetro en la línea de ejecución del programa, esta dirección.


Y lo ejecutamos, vemos la salida de texto que se muestra a continuación:


Por supuesto que es mejor verla con el browser ¡!


Usar sockets para armar un programa de mensajería entre pares (chat)

Vamos a construir una aplicación Windows que nos entregue una interfaz simple para comunicación. Queremos que sea un contenedor con dos paneles.

Uno que me muestre los mensajes que recibo y el otro que me permita escribir mensajes.-

La propia aplicación trabajara de dos modos diferenciados dependiendo de cómo se la invoque en la llamada. En un caso será servidor y en otro cliente.-

Alcanza con solo servidor para atender múltiples clientes.-

El esquema que armamos es el de "estrella". Es decir tal como armamos la aplicación todos los clientes pueden hablar con el servidor, pero no entre si.-

Una variante de este modo en el que el servidor atrape el pedido y lo derive u otro cliente de la red es relativamente sencillo de hacer basado en la tecnología mostrada en este ejemplo.-


Se habrá creado un proyecto con una serie de componentes como muestra el grafico


En este tipo de aplicaciones dentro del Form (Form1.cs), la IDE de .NET escribe algunos códigos de inicialización que no queremos para nuestro caso.-

Codigo en Form1.cs

Ingrese a código de Form1.Designer.cs y reemplácelo por el código que figura a continuación:


namespace UnChat

{

partial class Form1

{

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.IContainer components = null;



#region Windows Form Designer generated code


/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{


}


#endregion

}

}


Código en program.cs

Reemplace el código del modulo program.cs por el código que figura a continuación


using System;

using System.Collections.Generic;

using System.Windows.Forms;

using System.Net;

using System.Globalization;

using System.Net.Sockets;


namespace UnChat

{


static
class
Program

{


// -


private
static
IPEndPoint endPoint;


//Indica si esta actuando en modo servidor o cliente


private
static
bool client;



// Entrada al programa


public
static
void Main(String[] args)

{


// -


if (ParseArgs(args))

{


//


Talker talker = new
Talker(endPoint, client);



// Referencia del Talker dentro del Form


TalkForm form = new
TalkForm(talker);



// Arranca el talker

talker.Start();



// Arranca la apliacion con el Form


Application.Run(form);

}

}





// Parser de la linea de comandos


private
static
bool ParseArgs(String[] args)

{


try

{


if (args.Length == 0)

{

client = false;

endPoint = new
IPEndPoint(IPAddress.Any, 5150);


return
true;

}


if (args[0][0] != '/' && args[0][0] != '-')

{

ShowUsage();


return
false;

}


switch (Char.ToUpper(args[0][1], CultureInfo.InvariantCulture))

{


case
'L': //Listen: Escucha


int port = 5150;


if (args.Length > 1)

{

port = Convert.ToInt32(args[1]);

}

endPoint = new
IPEndPoint(IPAddress.Any, port);

client = false;


break;


case
'C'://Call: Llama

port = 5150;


String address = "127.0.0.1";

client = true;


if (args.Length > 1)

{

address = args[1];

port = Convert.ToInt32(args[2]);

}

endPoint = new
IPEndPoint(Dns.GetHostEntry(address).AddressList[0], port);


break;


default:

ShowUsage();


return
false;

}

}


catch

{

ShowUsage();


return
false;

}



return
true;

}


// Como se usa

private static void ComoUsarme()

{


MessageBox.Show("UnChat [switch] [parametros...]\n\n" +

" /L [port]\t\t-- Escucha en el port. Default: 5150\n" +

" /C [direccion] [port]\t-- Conecta a una direccion y port.\n\n" +

"Ejemplo Server - \n" +

"UnChat /L\n\n" +

"Ejemplo Client - \n" +

"UnChat /C NombreDeServidor 5150", "Uso de UnChat");

}

}

}


Código en Talker.cs


Creemos ahora la clase que representara al Talker



Y coloquemos dentro del archivo de esta clase el código que se indica a continuación:


using System;

using System.Collections.Generic;

using System.Text;

using System.Net;

using System.Net.Sockets;

using System.IO;

using System.Threading;


namespace UnChat

{

// Esta clase es una capa que encpasula a la clase Socket y le agrega

// algunas habilidades de chateo.-

class Talker : IDisposable

{

private Socket socket;


private TextReader reader;

private TextWriter writer;


bool client;

IPEndPoint endPoint;


private String prevSendText;

private String prevReceiveText;

private String statusText;


private Status status;


// Constructor

public Talker(IPEndPoint endPoint, bool client)

{

this.endPoint = endPoint;

this.client = client;


socket = null;

reader = null;

writer = null;


statusText = prevSendText = prevReceiveText = String.Empty;

}


// -

~Talker()

{

Dispose();

}


// Este es un modo ordenado y seguro de liberar todos

//los recursos que se usaron en el momento en que se baja

//la aplicacion

public void Dispose()

{

GC.SuppressFinalize(this);

if (reader != null)

{

reader.Close();

reader = null;

}

if (writer != null)

{

writer.Close();

writer = null;

}

if (socket != null)

{

socket.Close();

socket = null;

}

}


// Clase delegate y evento

public delegate

void NotificationCallback(Notification notify, Object data);

public event NotificationCallback Notifications;


// enum de los tipos de notificaciones

public enum Notification

{

Initialized = 1,

StatusChange,

ReceivedRefresh,

ReceivedAppend,

End,

Error

}


// enum de los posibles estados

public enum Status

{

Escuchando,

Conectado,

Cliente

}



// EL inicio de una instancia de la clase es un thread

//que se encola en el pool de threads

public void Start()

{

ThreadPool.QueueUserWorkItem(new WaitCallback(EstablishSocket));

}


// Envio de texto a la coneccion remota

public void EnviaMensaje(String newText)

{

String send;

// es un agregado al texto (append)

if ((prevSendText.Length <= newText.Length) && String.CompareOrdinal(

newText, 0, prevSendText, 0, prevSendText.Length) == 0)

{

String append = newText.Substring(prevSendText.Length);

send = String.Format("A{0}:{1}", append.Length, append);


}// o reemplazo completo

else

{

send = String.Format("R{0}:{1}", newText.Length, newText);

}

// Envio datos y hace flush

writer.Write(send);

writer.Flush();

// Guardar el texto para comparar si es necesario

prevSendText = newText;

}


// Notifica un estado

private void SetStatus(Status status)

{

this.status = status;

Notifications(Notification.StatusChange, status);

}



// Establecer una coneccion via socket y comenzar la recepcion

private void EstablishSocket(Object state)

{

NetworkStream stream = null;

try

{

// Si no es un cliente, entonces levantar una Escucha (listener)

if (!client)

{

Socket listener;


try

{

listener = new Socket(AddressFamily.InterNetwork,

SocketType.Stream, ProtocolType.Tcp);

listener.Blocking = true;

listener.Bind(endPoint);

SetStatus(Status.Escuchando);

listener.Listen(0);

socket = listener.Accept();

listener.Close();

stream = new NetworkStream(socket);

reader = new StreamReader(stream);

writer = new StreamWriter(stream);

writer.Write("WINTALK .NET");

writer.Flush();

}

catch (SocketException e)

{


// Este error indica que alguien esta usando el puerto

//Asumamos que en ese caso se debe intentar conectarse como un cliente

//pensando que ya hay un server para los nodos de la red

if (e.ErrorCode == 10048)

{

client = true;

endPoint = new IPEndPoint(

Dns.Resolve("127.0.0.1").AddressList[0], endPoint.Port);

}

else

{

Notifications(

Notification.Error,

"Error al inicializar el Socket:\n" + e.ToString());

}

}

}



// Intenta coneccion como cliente

if (client)

{

SetStatus(Status.Cliente);

Socket temp = new

Socket(AddressFamily.InterNetwork,

SocketType.Stream, ProtocolType.Tcp);

temp.Blocking = true;

temp.Connect(endPoint);

socket = temp;

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 5000);

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);

stream = new NetworkStream(socket);

reader = new StreamReader(stream);

writer = new StreamWriter(stream);

char[] handshake = new char[12];


try

{

reader.Read(handshake, 0, 12);

string sHandShake = new string(handshake);


if (!(reader.Read(handshake, 0, 12) > 0 && sHandShake == "WINTALK .NET"))

{

socket.Close();

socket = null;

}

else

{

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 0);

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 0);

}

}

catch

{

socket.Close();

socket = null;

}

}


// Si establecimos el socket, comenzar a chatear

if (socket != null)

{

SetStatus(Status.Conectado);

Notifications(Notification.Initialized, this);

// -

//NetworkStream.Read()es un metodo llamado por el metodo ReceiveTalk

//El primer metodo genera una excepcion cuando se cierra la coneccion remota

//La excepcion se maneja en el catch

ReceiveTalk();


// Por aca pasa cuando NetworkStream.Read() retorna un cero (algo que hace

//en alguans versiones del SO

Notifications(Notification.End, "La coneccion remota se cerro.");

}

else

{

Notifications(Notification.Error,

"Falla al establecer el , Verifique si especifico el port correcto");

}

}

catch (IOException e)

{

SocketException sockExcept = e.InnerException as SocketException;

if (sockExcept != null && 10054 == sockExcept.ErrorCode)

{

Notifications(Notification.End, "La coneccion remota se cerro.");

}

else

{

if (Notifications != null)

Notifications(Notification.Error, "Error :\n" + e.Message);

}

}

catch (Exception e)

{

Notifications(Notification.Error, "Error:\n" + e.Message);

}

}



// Recibe la comunicacion del cliente remoto

private void ReceiveTalk()

{

char[] commandBuffer = new char[20];

char[] oneBuffer = new char[1];

int readMode = 1;

int counter = 0;

StringBuilder text = new StringBuilder();


while (readMode != 0)

{

if (reader.Read(oneBuffer, 0, 1) == 0)

{

readMode = 0;

continue;

}


switch (readMode)

{

case 1:

if (counter == commandBuffer.Length)

{

readMode = 0;

continue;

}

if (oneBuffer[0] != ':')

{

commandBuffer[counter++] = oneBuffer[0];

}

else

{

counter = Convert.ToInt32(

new String(commandBuffer, 1, counter - 1));

if (counter > 0)

{

readMode = 2;

text.Length = 0;

}

else if (commandBuffer[0] == 'R')

{

counter = 0;

prevReceiveText = String.Empty;

Notifications(Notification.ReceivedRefresh, prevReceiveText);

}

}

break;

case 2:

text.Append(oneBuffer[0]);

if (--counter == 0)

{

switch (commandBuffer[0])

{

case 'R':

prevReceiveText = text.ToString();

Notifications(Notification.ReceivedRefresh, prevReceiveText);

break;

default:

string newText = text.ToString();

prevReceiveText += newText;

Notifications(Notification.ReceivedAppend, newText);

break;

}

readMode = 1;


}

break;

default:

readMode = 0;

continue;

}

}

}

}

}

Como ejecutar la aplicación


Usar una sesión como cliente y otra como servidor

Para eso generar dos archivos .bat (llamémoslos ChatCliente.bat y ChatServer.bat) que inicien cada uno una sesión del programa


Por ejemplo para ChatCliente.bat

UnChat.exe /C m3nb02 5150

Que significa que el programa desea actuar como Cliente (de allí la letra C) del equipo m3nb02 (es el nombre de mi equipo en esta red en la que estoy trabajando) y "hablar" por el port 5150.-


Por ejemplo para ChatServer.bat


UnChat.exe /L 5150

Que significa que el programa desea actuar como Server (La letra L por Listen) y "hablar" por el port 5150.-


Sockets y Ports

El protocolo FTP

Codigos de retorno de FTP

Code

Explanation

100

Series: The requested action is being initiated, expect another reply before proceeding with a new command.

110

Restart marker replay . In this case, the text is exact and not left to the particular implementation; it must read: MARK yyyy = mmmm where yyyy is User-process data stream marker, and mmmm server's equivalent marker (note the spaces between markers and "=").

120

Service ready in nnn minutes.

125

Data connection already open; transfer starting.

150

File status okay; about to open data connection.

200

Command okay.

202

Command not implemented, superfluous at this site.

211

System status, or system help reply.

212

Directory status.

213

File status.

214

Help message.On how to use the server or the meaning of a particular non-standard command. This reply is useful only to the human user.

215

NAME system type. Where NAME is an official system name from the list in the Assigned Numbers document.

220

Service ready for new user.

221

Service closing control connection.

225

Data connection open; no transfer in progress.

226

Closing data connection. Requested file action successful (for example, file transfer or file abort).

227

Entering Passive Mode (h1,h2,h3,h4,p1,p2).

228

Entering Long Passive Mode (long address, port).

229

Entering Extended Passive Mode (port).

230

User logged in, proceed. Logged out if appropriate.

231

User logged out; service terminated.

232

Logout command noted, will complete when transfer done.

250

Requested file action okay, completed.

257

"PATHNAME" created.

331

User name okay, need password.

332

Need account for login.

350

Requested file action pending further information

421

Service not available, closing control connection. This may be a reply to any command if the service knows it must shut down.

425

Can't open data connection.

426

Connection closed; transfer aborted.

434

Requested host unavailable.

450

Requested file action not taken.

451

Requested action aborted. Local error in processing.

452

Requested action not taken. Insufficient storage space in system.File unavailable (e.g., file busy).

500

Syntax error, command unrecognized. This may include errors such as command line too long.

501

Syntax error in parameters or arguments.

502

Command not implemented.

503

Bad sequence of commands.

504

Command not implemented for that parameter.

530

Not logged in.

532

Need account for storing files.

550

Requested action not taken. File unavailable (e.g., file not found, no access).

551

Requested action aborted. Page type unknown.

552

Requested file action aborted. Exceeded storage allocation (for current directory or dataset).

553

Requested action not taken. File name not allowed.