<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Blog de Javier Smaldone &#187; Programación</title>
	<atom:link href="http://blog.smaldone.com.ar/category/software/programacion/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.smaldone.com.ar</link>
	<description>Todos los días se aprende algo viejo</description>
	<lastBuildDate>Sat, 05 Nov 2011 07:32:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Los Riesgos de las Universidades-Java</title>
		<link>http://blog.smaldone.com.ar/2010/06/01/los-riesgos-de-las-universidades-java/</link>
		<comments>http://blog.smaldone.com.ar/2010/06/01/los-riesgos-de-las-universidades-java/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 20:53:13 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Computación]]></category>
		<category><![CDATA[Educación]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=432</guid>
		<description><![CDATA[Este artículo de Joel Spolsky es, desde hace tiempo, uno de mis favoritos. Publicado originalmente en el año 2005, el paso del tiempo sólo lo ha hecho ganar rigor y actualidad. Spolsky alerta sobre la creciente tendencia de muchas universidades a sobre-simplificar la currícula de las carreras de Ciencias de la Computación (con Java como [...]]]></description>
			<content:encoded><![CDATA[<p>Este artículo de <a href="http://en.wikipedia.org/wiki/Joel_Spolsky">Joel Spolsky</a> es, desde hace tiempo, uno de mis favoritos. <a href="http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html">Publicado originalmente</a> en el año 2005, el paso del tiempo sólo lo ha hecho ganar rigor y actualidad.</p>
<p><strong>Spolsky</strong> alerta sobre la creciente tendencia de muchas universidades a sobre-simplificar la currícula de las carreras de Ciencias de la Computación (con <strong>Java</strong> como &#8220;lenguaje insignia&#8221;), con el único objetivo de producir mayor cantidad de egresados y en menos tiempo. La consecuencia es directa: cada vez más abundan programadores que poco entienden de conceptos de programación (corrección, recursión, complejidad, paralelismo, entre tantos otros) y para los cuales los mecanismos &#8220;internos&#8221; de los sistemas informáticos (gestión de memoria, interacción con el sistema operativo, comunicación entre procesos, etc.) son completamente esotéricos.</p>
<p><span id="more-432"></span></p>
<p>El principal impulsor de este embrutecimiento es cierto sector de la llamada &#8220;Industria del Software&#8221; que requiere de mano de obra barata y de pobre formación, y empuja a las instituciones educativas a proveerles &#8220;recursos&#8221; tan rápido y en tanta cantidad como sea posible (ni qué decir de las instituciones educativas que ceden a esta presión).</p>
<p>Nadie está en contra de hacer la tarea del programador más simple, ni mucho menos de la evolución de los lenguajes y las metodologías de desarrollo de software. Lo que se reprocha aquí es la negación de la realidad como forma de evasión. Esto es: la necesidad de dotar a los programadores de ciertos conceptos, aún cuando esto provoque la deserción de quienes no puedan llegar a dominarlos (y mal que le pese al referido sector de la &#8220;Industria&#8221;).</p>
<h3>Los Riesgos de las Universidades-Java</h3>
<p><strong>por Joel Spolsky</strong><br />
Jueves 29 de diciembre de 2005</p>
<p>Chicos perezosos.</p>
<p>¿Qué pasó con el trabajo duro?</p>
<p>Un seguro indicio de mi decadencia hacia la senilidad son mis continuas quejas y lamentos sobre &#8220;los chicos de hoy&#8221;, y cómo ya no quieren o no pueden hacer cosas difíciles.</p>
<p>Cuando yo era un muchacho, aprendí a programar con tarjetas perforadas. En esos tiempos si cometías un error, no tenías ninguna de esas modernas funciones como la tecla &#8220;<em>backspace</em>&#8221; para corregirlo. Tenias que tirar la tarjeta y empezar todo de nuevo.</p>
<p>Cuando empecé a entrevistar programadores en 1991, les dejaba usar generalmente cualquier lenguaje que quisieran para resolver los problemas de programación que les planteaba. El 99% de las veces, ellos elegían C.</p>
<p>Ahora ellos tienden a elegir Java.</p>
<p>No me malinterpreten: no hay nada malo con Java como lenguaje de implementación.</p>
<p>Un momento, quiero rectificar eso último. No digo, <em>en este artículo en particular</em>, que haya algo de malo con Java como lenguaje de implementación. Hay un montón de cosas mal con Java, pero tendrán que esperar hasta otro artículo.</p>
<p>En cambio, lo que quiero decir es que Java no es, generalmente, un lenguaje de programación lo suficientemente difícil para que pueda ser usado para distinguir entre excelentes programadores y programadores mediocres. Puede ser un buen lenguaje para trabajar, pero ese no es el tema de hoy. Puedo ir incluso mas allá y decir que el hecho de que Java no sea lo suficientemente difícil es una característica, no un bug, pero que tiene ese problema.</p>
<p>Si puedo ser atrevido, diría que en mi humilde experiencia han sido dos las cosas tradicionalmente enseñadas en las universidades como parte de la carrera de Ciencias de la Computación (CS) las que mucha gente nunca llega realmente a comprender: punteros y recursión.</p>
<p>En aquellos tiempos lo normal era empezar la universidad con un curso de estructuras de datos, con listas enlazadas, tablas hash y, por qué no, con un uso intensivo de punteros. Esos cursos eran frecuentemente usados como filtros: eran tan difíciles que cualquiera que no pudiera soportar el desafío mental de un grado en CS se daría por vencido, lo que era bueno, porque si piensas que los punteros son difíciles, espera hasta intentar probar cosas en teoría de punto fijo.</p>
<p>Todos esos chicos que lo habían hecho muy bien en la secundaria escribiendo juegos de &#8220;pong&#8221; en BASIC para su Apple II, iban a la universidad, tomaban el curso CompSci 101, sobre estructuras de datos, y cuando llegaban al asunto de los punteros, sus cerebros estallaban completamente; y lo próximo de lo que tenías noticias es que estaban especializándose en Ciencias Políticas, porque la escuela de leyes parecía ser una mejor idea. He visto todo tipo de de índices de deserción en CS y usualmente están entre el 40% y  el 70%. Las universidades tienden a ver esto como un derroche; yo creo que es sólo la poda necesaria de gente que no va a ser feliz o exitosa en una carrera de programación.</p>
<p>El otro curso difícil para muchos jóvenes estudiantes de CS era el curso donde aprendías programación funcional, incluyendo programación recursiva. MIT puso una barrera muy alta en esos cursos, creando un <a href="http://sicp.csail.mit.edu/Fall-2005/">curso</a> obligatorio (6.001) y un libro de texto (<a href="http://mitpress.mit.edu/sicp/full-text/book/book.html">Estructura e Interpretación de Programas de Computadora</a> de Abelson y Sussman, el cual era usado en docenas o quizás cientos de carreras de CS prestigiosas como el estándar de facto para la introducción a las Ciencias de la Computación. (Puedes, y deberías echarle una ojeada a la antigua versión de las clases <a href="http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/">en línea</a>).</p>
<p>La dificultad de esos cursos es asombrosa. En la primera clase has aprendido casi todo Scheme, y ya has sido introducido a la función de punto fijo que toma otra función como parámetro. Cuando me esforzaba en pasar un curso similar, CSE121 en Penn, observaba cómo muchos sino la mayoría de los estudiantes simplemente no lo lograba. La materia era muy difícil. Inclusive escribí un largo email de lloriqueando a mi profesor diciendo que “simplemente no era justo”. Alguien en Penn debe haberme escuchado (o a alguno de los otros llorones), porque ese curso se dicta ahora con Java.</p>
<p>Desearía que no hubiesen escuchado.</p>
<p>Aquí radica el quid del debate. Años de lloriqueo de estudiantes perezosos como yo, combinados con quejas de la industria acerca de cuan pocos graduados en CS salen de las universidades americanas, han pagado su precio, y en la ultima década un gran numero de otrora perfectamente buenas universidades se han vuelto 100% Java. Esta de moda, a los reclutadores que usan “grep&#8221; parece gustarles, y, lo mejor de todo, no hay nada lo suficientemente difícil en Java como para filtrar aquellos programadores sin la parte del cerebro que entiende punteros y recursión. Así es que la deserción es menor, y los departamentos de ciencias de la computación tienen mas alumnos y mayores presupuestos y todo está bien.</p>
<p>Los afortunados chicos de esas Universidades-Java nunca van a toparse con raros fallos de segmentación tratando de implementar tablas hash basadas en punteros. Nunca se van a volver locos tratando de empaquetar cosas en bits. Nunca tendrán que ocupar sus cabezas en cómo en un lenguaje puramente funcional, el valor de una variable nunca cambia, y aun así, ¡cambia todo el tiempo! ¡Una paradoja!</p>
<p>Ellos no necesitan esa parte del cerebro para obtener un 4 en la materia.</p>
<p>¿Soy sólo uno de esos viejos cascarrabias anticuados, vanagloriándose acerca de cuán duro era sobrevivir a todas esas dificultades?</p>
<p>Rayos, en 1900, el Latín y el Griego eran asignaturas requeridas en la universidad, no porque sirvieran de algún propósito, sino porque de alguna manera eran considerados un requisito obvio de la gente educada. De cierta manera mi argumento no es diferente del argumento expuesto por la gente pro-Latín: “[El Latín] entrena tu mente. Entrena tu memoria. Desembrollar una sentencia en Latín es un excelente ejercicio del pensamiento, un verdadero acertijo intelectual y una buena introducción al pensamiento lógico”, <a href-"http://www.promotelatin.org/whylatin.htm">escribe</a> Scout Barrer. Pero ya no puedo encontrar una sola universidad que requiera Latín. ¿Son los punteros y la recursión el Latín y el Griego de las ciencias de la computación?</p>
<p>Ahora, admito que programar con punteros no es necesario en el 90% del código escrito en la actualidad, y de hecho es totalmente peligroso en el código de producción. OK. Está bien. Y que la programación funcional no es muy empleada en la práctica. De acuerdo.</p>
<p>Pero todavía sigue siendo importante para algunos de las tareas más excitantes en programación. Sin punteros, por ejemplo, nunca serías capaz de trabajar en el Kernel de Linux. No puedes entender una sola línea del código de Linux, o de hecho, de cualquier sistema operativo, sin realmente entender punteros.</p>
<p>Sin entender programación funcional, no podrás inventar <a href="http://labs.google.com/papers/mapreduce.html">MapReduce</a>, el algoritmo que hace Google tan masivamente escalable. Los términos Map y Reduce vienen de Lisp y la programación funcional. MapReduce es, en retrospectiva, obvio para cualquiera que recuerde de su clase equivalente a 6.001 que los programas puramente funcionales no tienen efectos colaterales y por ende son trivialmente paralelizables. El simple hecho que Google inventara MapReduce, y no Microsoft, dice algo del por qué Microsoft está aun jugando a lograr que funcionen algunas características básicas de búsqueda, mientras Google se ha movido ya al siguiente problema: construir <a href="http://en.wikipedia.org/wiki/Skynet">Skynet</a>^H^H^H^H^H^H, la mayor <a href="http://www.pbs.org/cringely/pulpit/pulpit20051117.html">supercomputadora</a> masivamente paralela del mundo. Simplemente no creo que Microsoft entienda completamente cuan retrasados están en ese campo.</p>
<p>Pero mas allá de la importancia a simple vista de los punteros y la recursión, su valor real radica en que construir grandes sistemas requiere del tipo de flexibilidad mental que adquieres aprendiéndolos, y de la actitud mental que necesitas para no huir de los cursos en donde son enseñados. Punteros y recursión requieren cierta habilidad para razonar, para pensar en abstracciones, y más importante, para ver un problema en diversos niveles de abstracción simultáneamente. Por lo tanto, la habilidad para entender punteros y recursión esta directamente correlacionada con la habilidad de ser un gran programador.</p>
<p>No hay nada en grado académico 100% Java que realmente descarte a los estudiantes que carecen de la agilidad mental para tratar con esos conceptos. Como empleador, he visto que las Universidades 100% Java han empezado a producir en serie una buena cantidad de graduados quienes simplemente no son lo suficientemente listos para trabajar como programadores en nada mas sofisticado que &#8220;Sólo Otra Aplicación Contable en Java&#8221;, aunque se las han arreglado para colarse a través de la (ahora simplificada) carrera. Esos estudiantes nunca sobrevivirían al 6.001 del MIT o al CS 323 en Yale y, francamente, esa es una razón por la cual, como empleador, un título en CS del MIT o Yale tiene más peso que uno de Duke, que recientemente se hizo 100%-Java, o de Penn, donde remplazaron Scheme y ML por Java tratando de enseñar la materia que casi nos mata a mis compañeros y a mi, CSE121. No es que no quiera contratar chicos listos de Duke o Penn, lo hago, es sólo que es mucho más difícil para mí darme cuenta de cuáles son. Yo estaba acostumbrado a decir que los chicos listos eran aquellos que podían desmenuzar un algoritmo recursivo en segundos, o implementar funciones de manipulación de listas enlazadas usando punteros tan rápido como podían escribir en la pizarra. Pero con graduado sde Universidades-Java, no puedo saber si padecen esos problemas a causa de haber sido mal educados o si los padecen porque realmente carecen de esa parte del cerebro que van a necesitar para ser buenos programadores en el trabajo. Paul Graham los llama &#8220;<a href="http://www.paulgraham.com/avg.html"><em>Blub Programmers</em></a>&#8220;.</p>
<p>Ya es bastante malo que las Universidades-Java fallen en filtrar los chicos que nunca van a ser buenos programadores, algo que las universidades podrían justificablemente decir que no es su problema. Después de todo es la industria, o al menos los reclutadores-que-usan-grep, quien está pidiendo a gritos que se enseñe Java.</p>
<p>Pero las Universidades-Java fallan también en entrenar las mente de los chicos para ser hábiles, ágiles y lo suficientemente flexibles para lograr buen diseño de software (y no me refiero al “diseño” OO, donde gastas incontables horas acomodando tu jerarquía de objetos, o preocupándote de &#8220;problemas&#8221; superfluos como “tiene-un” vs. “es-un”). Necesitas entrenamiento para pensar en las cosas a varios niveles de abstracción simultáneamente, y ese tipo de pensamiento es exactamente lo que necesitas para diseñar excelentes arquitecturas de software.</p>
<p>Puedes estar preguntándote si la enseñanza de programación orientada a objetos (OOP) es un buen sustituto de los punteros y la recursión para el filtrado. La respuesta rápida: no. Sin debatir acerca de los meritos de la OOP, simplemente no es lo suficientemente difícil para filtrar a los programadores mediocres. OOP en las universidades consiste básicamente en memorizar un puñado de términos de vocabulario como “encapsulacion” y “herencia” y tomar exámenes del tipo &#8220;multiple-choice&#8221; acerca de las diferencias entre polimorfismo y sobrecarga. No más difícil que memorizar fechas destacadas y nombres en una clase de historia, la OOP tiene desafíos mentales inadecuados para espantar a los estudiantes de primer año. Cuando te enfrentas con un problema de OOP, <em>tu programa aun funciona</em>, sólo que es algo difícil de mantener. Supuestamente. Pero cuando te enfrentas a un problema con punteros, tu programa produce línea <strong>Fallo de segmentación</strong> y no tienes ni la menor idea de lo que está pasando, hasta que te paras, tomas una fuerte bocanada de aire y tratas de forzar tu mente a trabajar en dos diferentes niveles de abstracción simultáneamente.</p>
<p>Los reclutadores-que-usan-grep, de hecho, son ridiculizados aquí, y por un buen motivo. Nunca he conocido alguien que pueda usar Scheme, Haskell y punteros en C, que no pueda entender Java en dos días, y crear mejor código en Java que gente con cinco años de experiencia en Java. Pero trata de explicar eso al zombie de Recursos Humanos.</p>
<p>¿Pero que hay de la misión del compromiso con las CS de las facultades de CS? ¡Ellas no son escuelas vocacionales! No debería ser su trabajo entrenar gente para trabajar en la industria. Eso queda para los terciarios y los programas de capacitación del gobierno para trabajadores desplazados, dirán. Ellas se suponen que están para dar a los estudiantes las herramientas fundamentales para vivir sus vidas, no para prepararlos para sus primeras semanas de trabajo. ¿No es cierto?</p>
<p>Aun así, las CS son demostraciones (recursión), algoritmos (recursión), lenguajes (cálculo lambda), sistemas operativos (punteros), compiladores (cálculo lambda), y entonces la conclusión es que la Universidad-Java que no enseña C y no enseña Scheme, tampoco está enseñando realmente ciencias de la computación. Tan inútil como el concepto de <a href="http://en.wikipedia.org/wiki/Currying"> currificación de funciones</a> puede serle al mundo real, es un obvio prerrequisito para un graduado en CS. No puedo entender por qué los profesores en las comisiones curriculares de las facultades de CS han permitido que sus programas sean embrutecidos a tal punto que  no sólo no pueden producir <em>programadores operativos</em>, sino que ya ni siquiera pueden producir graduados en CS que puedan obtener PhDs y puedan competir por sus puestos de trabajo. Oh, esperen. No importa. Quizás entienda.</p>
<p>Si volvemos en el tiempo y analizamos las discusiones que tomaron lugar en el mundo académico durante el “Gran Levantamiento Java”, encontraremos que la mayor preocupación fue que Java no era lo suficientemente <em>simple</em> para ser usado como un lenguaje de enseñanza.</p>
<p><em>Mi Dios</em>, pensé, <em>¡están tratando de embrutecer la curricula aun mas!</em> ¿Por que mejor no le llevamos la comida a la boca a los estudiantes? Dejemos que los ayudantes de cátedra den los exámenes por ellos también, entonces nadie se cambiara a Estudios Americanos. ¿Cómo se supone que alguien aprenderá algo si la curricula ha sido cuidadosamente diseñada para hacer todo más fácil de lo que ya es? Parece haber una comisión de trabajo (<a href="http://www.sigcse.org/topics/javataskforce/java-task-force.pdf">PDF</a>) intentando idear un subconjunto simple de Java que pueda ser enseñado a estudiantes, produciendo documentación simplificada que esconde cuidadosamente toda esa basura EJB/J2EE de sus tiernas mentes, de manera tal que no tengan que preocupar sus cabecitas con otras clases que no necesiten para resolver sus aun más fáciles problemas de CS.</p>
<p>La interpretación mas compasiva de por qué las facultades CS son tan entusiastas en embrutecer sus clases es porque ello les dará más tiempo para enseñar verdaderos conceptos de CS, así no necesitaran dos clases enteras para esclarecer a los alumnos las diferencias entre, digamos, un <strong>int</strong> y un <strong>Integer</strong> en Java. Bueno pero si ese fuera el caso, 6.001 tiene la respuesta perfecta: Scheme, un lenguaje de enseñanza tan simple que el lenguaje entero puede enseñarse a estudiantes brillantes en unos 10 minutos; entonces puedes gastar el resto del semestre enseñando puntos fijos.</p>
<p>Fiu&#8230;</p>
<p>Voy a regresar a los unos y ceros.</p>
<p>(¿Te tocaron unos? ¡Bastardo suertudo! A nosotros nos tocaron todos ceros.)</p>
<p>&nbsp;</p>
<p><em>Esta traducción está basada en la versión disponible en <a href="http://local.joelonsoftware.com/wiki/Riesgos_de_las_escuelas_Java">el Wiki de Joel Spolsky</a>, corregida y adaptada por mí.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2010/06/01/los-riesgos-de-las-universidades-java/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>¿Cómo decimos verdades que pueden lastimar?</title>
		<link>http://blog.smaldone.com.ar/2010/06/01/verdades-que-pueden-lastimar/</link>
		<comments>http://blog.smaldone.com.ar/2010/06/01/verdades-que-pueden-lastimar/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 07:59:14 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Computación]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=417</guid>
		<description><![CDATA[Lo que sigue es una traducción de un excelente artículo de Edsger W. Dijkstra titulado originalmente &#8220;How do we tell truths that might hurt?&#8220;. Dijkstra es reconocido como uno de los pioneros de las Ciencias de la Computación por sus abundantes aportes, tanto en el área teórica como en la práctica. Resulta ser que, además [...]]]></description>
			<content:encoded><![CDATA[<p>Lo que sigue es una traducción de un excelente artículo de <a href="http://es.wikipedia.org/wiki/Edsger_Dijkstra">Edsger W. Dijkstra</a> titulado originalmente &#8220;<a href="http://userweb.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html">How do we tell truths that might hurt?</a>&#8220;.</p>
<p><strong>Dijkstra</strong> es reconocido como uno de los pioneros de las Ciencias de la Computación por sus abundantes aportes, tanto en el área teórica como en la práctica. Resulta ser que, además de un notable científico, también fue un gran visionario. Una muestra de ello es el artículo aquí reproducido, publicado originalmente en el año <strong>1975</strong>.</p>
<p>Realmente es penoso ver que, aún <strong>35 años después</strong>, muchos siguen sin caer en la cuenta de cuestiones que ya en aquella época resultaban evidentes.</p>
<p><span id="more-417"></span></p>
<h3>¿Cómo decimos verdades que pueden lastimar?</h3>
<p>A veces descubrimos verdades desagradables. Cada vez que lo hacemos, estamos en dificultades: suprimirlas es científicamente deshonesto, por lo que tenemos que decirlas; pero diciéndolas, sin embargo, se volverán en nuestra contra. Si las verdades son lo suficientemente desagradables, nuestro público será psíquicamente incapaz de aceptarlas y seremos tachados de irrealistas, idealistas sin remedio, peligrosamente revolucionarios, tontamente crédulos o lo que sea. (Además de eso, decir tales verdades es una forma segura de volverse impopular en muchos círculos, y como tal, es un hecho que en general no está exento de riesgos personales. Recordemos a Galileo Galilei&#8230;)</p>
<p>Las Ciencias de la Computación parecen sufrir gravemente este conflicto. En general, se mantienen silenciosas y tratan de escapar del mismo desviando la atención. (Por ejemplo: con respecto a COBOL se puede hacer sólo una de dos cosas: luchar contra la enfermedad o pretender que no existe. La mayoría de los departamentos de Ciencias de la Computación han optado por la última como una salida más fácil). Pero, hermanos, os pregunto: ¿es honesto? ¿No está nuestro prolongado silencio corrompiendo la integridad intelectual de las Ciencias de la Computación? ¿Es decente premanecer en silencio? Si no, ¿cómo podemos hablar?</p>
<p>Para que se haga una idea de la magnitud del problema, he enumerado una serie de tales verdades. (Casi todos los científicos de computación que conozco bien estarán de acuerdo sin dudarlo con casi todas ellas. Sin embargo, permitimos que el mundo se comporte como si no las conociéramos&#8230;)</p>
<ul>
<li>La programación es una de las ramas más difíciles de las matemáticas aplicadas, los pobres matemáticos mejor hubieran seguido siendo matemáticos puros.</li>
<li>Las aplicaciones de computadora más fáciles son los cálculos técnicos/científicos.</li>
<li>Las herramientas que usamos tienen una profunda (¡y retorcida!) influencia en nuestros hábitos de pensamiento y, por lo tanto, en nuestra habilidad de pensar.</li>
<li>FORTRAN -&#8221;la enfermedad infantil&#8221;-, actualmente con casi 20 años de edad, es completamente inadecuado para cualquier aplicación informática que tenga en mente hoy: es demasiado torpe, demasiado arriesgado y demasiado costoso.</li>
<li>PL / I -&#8221;la enfermedad fatal&#8221;- pertenece más al conjunto de los problemas que al de las soluciones.</li>
<li>Es prácticamente imposible enseñar buena programación a estudiantes que han tenido exposición previa al BASIC: como programadores potenciales están mentalmente mutilados sin esperanza de regeneración.</li>
<li>El uso de COBOL incapacita la mente, y su enseñanza debe, por tanto, ser considerada como un delito penal.</li>
<li>APL es un error, llevado a cabo a la perfección. Es el lenguaje del futuro para técnicas de programación del pasado: crea una nueva generación de inútiles de la programación.</li>
<li>Los problemas de administración de empresas en general y de gestión de bases de datos, en particular, resultan mucho más complicados para las personas que piensan en IBMés, compuesto con un Inglés desalineado.</li>
<li>Sobre el uso del lenguaje: es imposible para afilar un lápiz con una cuchilla desafilada. Es igualmente inútil tratar de hacerlo con diez cuchillas desafiladas.</li>
<li>Además de una inclinación matemática, un dominio excepcional de la lengua nativa es el activo más vital de un programador competente.</li>
<li>Muchas compañías que se han vuelto dependientes de equipamiento IBM (y al hacerlo, han vendido su alma al diablo) se derrumbará bajo el peso de la complejidad incontrolada de sus sistemas de procesamiento de datos.</li>
<li>No podemos encontrar ninguna disciplina científica, ni una profesión robusta, basada en los errores técnicos del Departamento de Defensa y, principalmente, un fabricante de computadoras.</li>
<li>El uso de la terminología antropomórfica cuando se trabaja con sistemas de computación es un síntoma de inmadurez profesional.</li>
<li>Afirmando que pueden contribuir a la ingeniería de software, los científicos blandos se vuelven aún más ridículos. (No menos peligrosos, ¡ay!). A pesar de su nombre, la ingeniería de software requiere (cruelmente) el soporte de la ciencia dura.</li>
<li>En los buenos viejos tiempos los físicos repetían los experimentos de los demás, simplemente para estar seguros. Hoy se apegan a FORTRAN, de modo que puedan compartir los programas de cada uno, errores incluidos.</li>
<li>Los proyectos que promueven la progamación en &#8220;lenguaje natural&#8221; están intrínsecamente condenados al fracaso.</li>
</ul>
<p>¿No es esta lista suficiente para hacernos sentir incómodos? ¿Qué vamos a hacer? Volver a la orden del día, presumiblemente&#8230;</p>
<p><em>18 de junio 1975<br />
Plataanstraat 5<br />
Nuenen &#8211; 4565<br />
Holanda</em></p>
<p><em><strong>Prof. Dr. Edsger W. Dijkstra<br />
Burroughs Research Fellow</strong></em></p>
<p>PD: Si la conjetura &#8220;Usted hubiera preferido que no lo molestara enviándole esto&#8221; es correcta, puede añadirla a la lista de verdades incómodas.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2010/06/01/verdades-que-pueden-lastimar/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Programadores, ¿productores o consumidores?</title>
		<link>http://blog.smaldone.com.ar/2010/03/27/programadores-productores-o-consumidores/</link>
		<comments>http://blog.smaldone.com.ar/2010/03/27/programadores-productores-o-consumidores/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 23:46:36 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Opinión]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Software libre]]></category>
		<category><![CDATA[Software privativo]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=385</guid>
		<description><![CDATA[Los programadores somos, casi por definición, productores de software. Esto es, producimos programas (que muchas veces hasta son llamados &#8220;productos&#8221;, según la definición que dicta el marketing). El gran sueño de muchos programadores es desarrollar un &#8220;producto&#8221; implementando una idea innovadora (o cubriendo un nicho insatisfecho) y vender una gran cantidad de copias, multiplicando las [...]]]></description>
			<content:encoded><![CDATA[<p>Los programadores somos, casi por definición, <em>productores de software</em>. Esto es, producimos programas (que muchas veces hasta son llamados &#8220;productos&#8221;, según la definición que dicta el marketing). El gran sueño de muchos programadores es desarrollar un &#8220;producto&#8221; implementando una idea innovadora (o cubriendo un nicho insatisfecho) y vender una gran cantidad de copias, multiplicando las ganancias.</p>
<p>Esta visión, lentamente, está cambiando. Por un lado, las historias de aquellos que hicieron una fortuna (o establecieron una posición económica) mediante la venta de licencias de un programa son, a la vez, cada vez más lejanas y menos frecuentes. Pero, si empezamos a vernos a nosotros mismos también como consumidores de software, el razonamiento cambia radicalmente.</p>
<p><span id="more-385"></span></p>
<h3>Comparemos</h3>
<p>Tomemos como ejemplo el desarrollo de un programa que le demande 5 años de trabajo a un programador altamente calificado y que requiera de las siguientes herramientas:</p>
<ul>
<li><strong>Sistema Operativo</strong>.</li>
<li><strong>Compilador</strong>.</li>
<li><strong>Entorno de desarrollo</strong>: Depurador, editor de textos, etc.</li>
<li><strong>Herramientas de modelado</strong>: Herramientas para hacer diagramas de diseño, documentación, etc.</li>
<li><strong>Motor de base de datos</strong>.</li>
</ul>
<p>El lapso de 5 años no es arbitrario: es una buena cota superior del tiempo pasado el cual un desarrollador debe renovar las herramientas que utiliza, si es que desea mantenerse actualizado. (Si lo prefiere, puede suponer la suma de varios desarrollos de menor envergadura, hasta llegar a cubrir dicho tiempo).</p>
<p>Comparemos entonces la cantidad de líneas de código &#8220;producidas&#8221; respecto de las &#8220;consumidas&#8221;. El cociente será seguramente bastante cercano a cero. Desde este punto de vista, aún cuando un programador sigue produciendo software, su rol de consumidor es, por mucho, más relevante.</p>
<p>Alguien dirá que al vender muchas licencias del programa producido, su valor se multiplicará. Esto no es así, teniendo en cuenta que, para poder ejecutarse el programa requerirá, nuevamente, de un sistema operativo y del motor de base de datos. No es poco común ver a un programador cobrar determinada suma de dinero por un desarrollo y a su cliente desembolsar bastante más por licencias del software requerido para que funcione.</p>
<h3>Un mal negocio</h3>
<p>Entre los programadores independientes (o pequeñas empresas de desarrollo) es práctica común usar software sin pagar por sus licencias, con lo cual parte del análisis anterior no los afecta demasiado. Pero sus clientes (y cada vez más) no tienen opción.</p>
<p>Por ejemplo, un sistema que requiera de un servidor con el sistema operativo <strong>Windows 2008</strong> y el motor de bases de datos <strong>Microsoft SQL Server 2008</strong> para su utilización en 25 puestos de trabajo, requerirá el pago de  más de <strong>u$s 10.000</strong> en concepto de licencia de uso de dichos productos (en la Argentina, marzo de 2010). ¿Cuánto deberá trabajar el programador para cobrar una suma de dinero similar?</p>
<p>Claramente, esta forma de ver el negocio no tiene mucho sentido (a no ser para aquellas empresas de desarrollo que además son agente de ventas de quienes proveen el software de base y se llevan una jugosa comisión).</p>
<p>Quizá sea por este motivo que las empresas proveedoras de herramientas de desarrollo y software de base invierten tanto dinero para que los programadores se pongan &#8220;su camiseta&#8221;, recurriendo para esto a <a href="http://www.youtube.com/watch?v=8To-6VIJZRE">discursos motivadores</a>, aportes a instituciones educativas, descuentos, tazas de café, etc.</p>
<p>Asumir que esta es una situación natural es resignarse a ser, ya no un productor, sino un mero promotor de la venta de productos de otro (con un esfuerzo y un riesgo bastante altos).</p>
<h3>Una buena alternativa</h3>
<p>Debemos reconocernos a nosotros mismos, antes que como productores, como consumidores de software. De esta manera, la salida es clara: debemos reducir los costos de la &#8220;materia prima&#8221;. Debemos, en la medida de lo posible, utilizar software que no requiera el pago de licencias de uso. De esta manera disminuyen sensiblemente los costos para el desarrollador, como así también los costos extra del cliente. (Y cliente que gasta menos en licencias, tiene más dinero para pagarle al programador).</p>
<p>Si el sistema del ejemplo anterior pudiera correr en un servidor con <a href="http://es.wikipedia.org/wiki/GNU/Linux"><strong>GNU/Linux</strong></a> y el motor de bases de datos <a href="http://es.wikipedia.org/wiki/PostgreSQL"><strong>PostgreSQL</strong></a> tendría más de <strong>u$s 10.000</strong> de &#8220;ventaja&#8221; en condiciones similares.</p>
<p>Otro punto importante es el ahorro en hardware, al utilizar productos que reduzcan los requerimientos en este aspecto (característica que distingue a muchos programas libres, respecto de sus contrapartes privativas), aunque dicho análisis escapa al objetivo del presente artículo.</p>
<h3>¿Por qué no?</h3>
<p>Existe una marcada reticencia en ciertos programadores a analizar seriamente esta alternativa. Dejaremos de lado, por supuesto, el caso de aquellos que actúan, además, como agentes de venta de los proveedores de software de base (su caso es más que claro: ellos se benefician por ambos lados). Analizaremos algunas de las objeciones más comunes:</p>
<h4>El sistema operativo libre &#8220;X&#8221; es difícil de usar. No lo entiendo</h4>
<p>En casi todos los casos, &#8220;X&#8221; se refiere al sistema operativo <a href="http://es.wikipedia.org/wiki/GNU/Linux"><strong>GNU/Linux</strong></a> (el sistema operativo libre más difundido). Quizás el programador en cuestión haya tenido una mala experiencia tratando de usar alguna versión mal configurada u obsoleta. Seguramente tampoco haya dedicado demasiado tiempo ni esfuerzo a aprender los conceptos básicos (que no son, precisamente, como usar los botones del mouse), y se alejó cual <a href="http://es.wikisource.org/wiki/La_zorra_y_las_uvas_(Esopo)">la zorra de Esopo</a> murmurando &#8220;están verdes&#8221;.</p>
<p>De esto no se desprende que <a href="http://es.wikipedia.org/wiki/GNU/Linux"><strong>GNU/Linux</strong></a> sea tán fácil, cómodo o agradable de usar que <strong>Windows 7</strong> (o más), pero tratándose de un profesional y habiendo tanto dinero de por medio, bien merece algún pequeño sacrificio.</p>
<h4>En la empresa en donde se implanta mi sistema usan Windows</h4>
<p>Aquí hay una amplia variedad de casos. ¿Usan <strong>Windows</strong> en los equipos de escritorio? Asumiendo que ya han pagado las licencias (por el tiempo que reste hasta la actualización de los equipos), de todas maneras podría significar un ahorro importante a nivel de los servidores. ¿Usan <strong>Windows</strong> en los servidores? Proveer una aplicación que no lo requiera, puede significar un valor agregado interesante.</p>
<p>En la mayoría de los casos en que la empresa &#8220;usa <strong>Windows</strong>&#8221; es porque las aplicaciones existentes lo requieren. No es, por lo tanto, una excusa para no proveer una alternativa que posibilite un ahorro importante (si no en lo inmediato, en el mediano plazo).</p>
<h4>Estoy acostumbrado a desarrollar en (y para) Windows</h4>
<p>Todos sabemos que los programadores disponemos de poco tiempo para mantenernos actualizados y lo costoso que resulta abordar el aprendizaje de una nueva tecnología. Cada quién sabrá si realmente se justifica el esfuerzo, poniendo en la balanza costos y beneficios (o, según el planteo de este artículo, costos y ahorros).</p>
<h4>No hay un equivalente libre de la herramienta &#8220;Y&#8221;</h4>
<p>Es cierto que en algunos casos (no tantos como se alegan) no hay equivalentes libres a ciertas herramientas privativas. ¿Y aquí termina el análisis? ¿No merece la pena indagar sobre alguna alternativa? ¿Tan útil es la herramienta &#8220;Y&#8221; que se hace imprescindible e indiscutible?</p>
<p>Si alguien depende exclusivamente de una determinada herramienta para desarrollar software, entonces se encuentra ante un problema bastante más grave que el gastar dinero en licencias de uso. (Preguntar a desarrolladores en <strong>Delphi</strong>,  <strong>Visual Fox</strong>, <strong>Clarion</strong>, entre otros. Pero este tema también excede el alcance del presente artículo.).</p>
<h4>No hay un equivalente libre del motor de bases de datos &#8220;Z&#8221;</h4>
<p>Aquí &#8220;Z&#8221; suele tomar la forma de <strong>MS SQL Server</strong>, <strong>Oracle</strong> o <strong>DB/2</strong>. La afirmación suele hacerse seguida de alguna frase que parece extraída de una publicidad del proveedor correspondiente. La realidad es que existen numerosos motores de bases de datos libres, como por ejemplo <a href="http://es.wikipedia.org/wiki/MySQL"><strong>MySQL</strong></a> para pequeños volúmenes de información, o <a href="http://es.wikipedia.org/wiki/PostgreSQL"><strong>PostgreSQL</strong></a> que no tiene ninguna característica importante que envidiarle a ningún otro producto.</p>
<h4>En el caso de programas libres, nadie me ofrece el soporte que me da la empresa &#8220;W&#8221;</h4>
<p>¿Enserio? Prácticamente todos los programas libres importantes, cuentan con ofertas de soporte incluso mejores que las que puedan conseguirse respecto de programas privativos. Por un lado, el soporte ofrecido es de mejor nivel, ya que se ofrece a nivel de reparación de errores en el código fuente y, por otro, muchas veces existen múltiples proveedores de soporte, que compiten entre si (en el mundo del software privativo el soporte es un monopolio del fabricante).</p>
<h3>Porque no</h3>
<p>Es cierto que hay algunos casos en donde no hay alternativa (la utilización de determinada tecnología puede ser impuesta como un requerimiento), pero en la mayoría de los casos la situación es que el programador se encuentra &#8220;atado&#8221; a sus herramientas.</p>
<p>En algunos casos, porque las han utilizado durante mucho tiempo (y tienen una gran cantidad de código desarrollado ligado a ellas) y en otros porque han &#8220;comprado&#8221; el discurso de su productor, muchos programadores se resisten a realizar cualquier análisis que tenga que ver con cambiar sus herramientas. El caso más grave (y lamentablemente no poco común) es cuando el programador simplemente no puede usar otra tecnología más que la que domina: es la única que conoce.</p>
<p>De más está decir, las inversiones en marketing dan resultado. Hoy nos encontramos a muchos programadores que han sido formados en un ambiente académico muy similar a un &#8220;monocultivo&#8221; (que, a pesar de ellos, ni siquiera ofrece los amplios márgenes de ganancia de la soja) o que se han entregado más tarde a determinada combinación de herramientas/tecnología y la han abrazado como dispuestos a envejecer (y quedar obsoletos) junto con ella.</p>
<p>Y así es que el negocio seguirá funcionando muy bien para los &#8220;grandes productores de software&#8221;, en tanto que los pequeños no reconozcan su rol de consumidores. Afortunadamente, la situación ya está cambiando: abundan los ejemplos de quienes, siendo conscientes de esto, están haciendo una diferencia económica significativa.</p>
<h3>Adenda</h3>
<p>Nótese que en este artículo no se hace alusión a la licencia bajo la cual el programador entrega su programa al cliente final. El término &#8220;<em>libre</em>&#8221; sólo se aplica a las herramientas utilizadas para el desarrollo y a las requeridas para su posterior ejecución.</p>
<p>El <em>uso</em> de herramientas de desarrollo libre no impone ninguna condición sobre la licencia que acompañará al programa resultante. (Distinta situación puede darse si se incorpora <em>código</em> de un programa libre en uno de producción propia).</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2010/03/27/programadores-productores-o-consumidores/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Is MySQL a toy RDBMS?</title>
		<link>http://blog.smaldone.com.ar/2008/12/29/is-mysql-a-toy-rdbms/</link>
		<comments>http://blog.smaldone.com.ar/2008/12/29/is-mysql-a-toy-rdbms/#comments</comments>
		<pubDate>Mon, 29 Dec 2008 22:38:18 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=135</guid>
		<description><![CDATA[Since two days ago I&#8217;m trying to believe what I&#8217;ve found: MySQL (&#8220;the world&#8217;s most popular open source database&#8220;, as they say) is unable to resolve a simple SQL query in an efficient way, leading me (and the program I&#8217;m developing) to a dead end. The situation I have a table &#8220;mytable&#8221; (1,000,000 rows) with [...]]]></description>
			<content:encoded><![CDATA[<p>Since two days ago I&#8217;m trying to believe what I&#8217;ve found: <a href="http://www.mysql.com/">MySQL</a> (&#8220;<em>the world&#8217;s most popular open source database</em>&#8220;, as they say) is unable to resolve a simple SQL query in an efficient way, leading me (and the program I&#8217;m developing) to a dead end.</p>
<p><span id="more-135"></span> </p>
<h3>The situation</h3>
<p>I have a table &#8220;<em>mytable</em>&#8221; (1,000,000 rows) with a column named &#8220;<em>mycolumn</em>&#8221; of type &#8220;<strong>datetime</strong>&#8221; and an index created on it named &#8220;<em>idx_mycolumn</em>&#8220;. When I issue the following query:</p>
<blockquote><p><code>SELECT * FROM mytable ORDER BY mycolumn</code></p></blockquote>
<p>it tooks more than 1 minute to give the results (the 1,000,000 rows ordered by <em>mycolumn</em>.)</p>
<p>Using the command &#8220;<strong>EXPLAIN</strong>&#8221; I can see that the <a href="http://www.mysql.com/">MySQL</a> optimizer don&#8217;t use the index at all: it generates a filesort (on disk) in order to sort the resultset. The only way to solve this issue (as far as I know) is providing a &#8220;hint&#8221; to the optimizer, telling what indexes to use:</p>
<blockquote><p><code>SELECT * FROM mytable USE INDEX(idx_mycolumn) ORDER BY mycolumn</code></p></blockquote>
<p>But the optimizer still chooses to do a full table scan instead of using the &#8220;<em>idx_mycolumn</em>&#8221; index (and still generates a filesort, taking more than 1 minute.)</p>
<p>There is a way to force the optimizer to use the index:</p>
<blockquote><p><code>SELECT * FROM mytable FORCE INDEX(idx_mycolumn) ORDER BY mycolumn</code></p></blockquote>
<p>This time, the optimizer uses the index, and the query takes about 3 seconds. (Is the <a href="http://www.mysql.com/">MySQL</a> query optimizer that bad?)</p>
<h3>A bigger problem</h3>
<p>Then, I came into the real problem for me: Obviously I&#8217;ll never need to get all the rows from that table, but is a common task to &#8220;paginate&#8221; the rows (previously ordered by some column) taking, let&#8217;s say, 25 rows at a time and using an offset. The query looks as follows:</p>
<blockquote><p><code>SELECT * FROM mytable ORDER BY mycolumn LIMIT 100000,25</code></p></blockquote>
<p>It sorts the resultset using the values of &#8220;<em>mycolumn</em>&#8221; and then returns 25 rows skipping the first 100,000. Surprisingly, this time the query tooks less than 1 second to execute (and &#8220;<strong>EXPLAIN</strong>&#8221; says that is using the &#8220;<em>idx_mycolumn</em>&#8221; index. What a joy!).</p>
<p>But the problem is still there: If the limit reaches (or surpases) the last row, then the optimizer  throws away the index and generates a filesort (taking more than 1 minute, making the query totally useless&#8230;)</p>
<p>The solution (again) is to force the optimizer to use the index &#8220;<em>idx_mycolumn</em>&#8220;, getting a response in about 3 seconds.</p>
<h3>My conclusion</h3>
<p>Far from being a MySQL expert, the only conclusion I can reach is that the optimizer is really screwed up. How can it be possible that changing only the limit of the results, confuses the optimizer in such a way that leads to a simple query (that can&#8217;t take more than a few seconds) to take an unacceptable amount of time (not to mention the disk space)?</p>
<p>As I can&#8217;t (and don&#8217;t want) force indexes in every query involving large tables in my system, the only real solution for me is replacing <a href="http://www.mysql.com/">MySQL</a> with a more robust (and decent) DBMS (I&#8217;ve tried <a href="http://www.postgresql.org/">PostgreSQL</a> with the same database and the results are far more reasonable.)</p>
<h3>Some things to consider</h3>
<ul>
<li>The problem is the same being the engine used <strong>MyISAM</strong> or <strong>InnoDB</strong>.</li>
<li>The problem doesn&#8217;t get solved after doing a &#8220;<strong>ANALYZE TABLE</strong>&#8220;.</li>
<li>I&#8217;ve trying with other datatypes for the sorting column, with the same results.</li>
<li>I&#8217;ve tested this issue with versions 5.0.35 and 5.0.51a, with identical results.</li>
</ul>
<h3>Update (2008/12/30)</h3>
<p>The observed behavior must be related with the <a href="http://bugs.mysql.com/bug.php?id=28404">bug 28404</a> (<a href="http://lists.mysql.com/commits/31882">patched</a> in version 5.1). From the patch description:</p>
<blockquote><p>When there are many indexes in table, optimizer wrongly does not even consider the one on column used in ORDER BY, when LIMIT N clause is also present.</p>
</blockquote>
<p>I&#8217;ve oversimplified the example, but in my real scenario I had multiple single-column indexes on the table. I still can&#8217;t believe that the optimizer get confused by having multiple indexes to choose from (but only in presence of the LIMIT clause). Can you guess how is the optimizer coded? (That&#8217;s why I use the word &#8220;<em>toy</em>&#8221; in the title.)</p>
<p>A posible solution seems to be moving to the new version, but anyway, <strong>MySQL</strong> 5.1 also has <a href="http://www.planetmysql.org/entry.php?id=16232">several issues</a> (and the distribution installation script for <strong>GNU/Linux</strong> is totally broken), so I&#8217;m moving to <strong>PostgreSQL</strong>.</p>
<p><em>(Thanks to Abel Chiola for the links.)</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2008/12/29/is-mysql-a-toy-rdbms/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Charla sobre Ruby en Córdoba (7JRSL)</title>
		<link>http://blog.smaldone.com.ar/2008/10/02/charla-sobre-ruby-en-cordoba-7jrsl/</link>
		<comments>http://blog.smaldone.com.ar/2008/10/02/charla-sobre-ruby-en-cordoba-7jrsl/#comments</comments>
		<pubDate>Thu, 02 Oct 2008 21:54:55 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Charlas]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=123</guid>
		<description><![CDATA[Siguiendo con la tendencia a desempolvar cosas viejas, aquí va la presentación que utilicé en mi charla &#8220;Ruby: &#8216;Orientación a Objetos&#8217; y algo más&#8230;&#8221;, en el marco de las 7mas. Jornadas Regionales de Software Libre, realizadas en la ciudad de Córdoba, en agosto de 2007. Ruby: &#8220;Orientación a Objetos&#8221; y algo más&#8230; Presentación en formato [...]]]></description>
			<content:encoded><![CDATA[<p>Siguiendo con la tendencia a desempolvar cosas viejas, aquí va la presentación que utilicé en mi charla <a href="http://jornadas.grulic.org.ar/7/contenido/programa/charlas/charla43">&#8220;Ruby: &#8216;Orientación a Objetos&#8217; y algo más&#8230;&#8221;</a>, en el marco de las <a href="http://jornadas.grulic.org.ar/7/">7mas. Jornadas Regionales de Software Libre</a>, realizadas en la ciudad de Córdoba, en agosto de 2007.</p>
<h3>Ruby: &#8220;Orientación a Objetos&#8221; y algo más&#8230;</h3>
<ul>
<li><a href="/files/7jrsl/ruby_smaldone.pdf">Presentación en formato PDF</a></li>
<li><a href="/files/7jrsl/ruby_smaldone.odp">Presentación en formato ODP</a> (<a href="http://www.openoffice.org/">OpenOffice.org</a>)</li>
</ul>
<p><span id="more-123"></span></p>
<p>Bueno, ¡no podía faltar una foto! <strong>;)</strong></p>
<div class="centerpic"><img src="/files/7jrsl/cordoba.jpg" alt="7JRSL"></div>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2008/10/02/charla-sobre-ruby-en-cordoba-7jrsl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Charla sobre Ruby on Rails en Rosario (5JRSL)</title>
		<link>http://blog.smaldone.com.ar/2008/10/02/charla-sobre-ruby-on-rails-en-rosario-5jrsl/</link>
		<comments>http://blog.smaldone.com.ar/2008/10/02/charla-sobre-ruby-on-rails-en-rosario-5jrsl/#comments</comments>
		<pubDate>Thu, 02 Oct 2008 21:10:20 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Charlas]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=122</guid>
		<description><![CDATA[Esta es la presentación que utilicé en la charla &#8220;Primeros pasos en Rails&#8221; en ocasión de las 5tas. Jornadas Regionales de Software Libre, en noviembre del 2005, en Rosario, Santa Fé. Aunque se trata de material un tanto obsoleto, ya que está basado en las primeras versiones de Rails (en las cuales ni siquiera existían, [...]]]></description>
			<content:encoded><![CDATA[<p>Esta es la presentación que utilicé en la charla <a href="http://www.ant.org.ar/jornadas/html/programDescription.php?id=20">&#8220;Primeros pasos en Rails&#8221;</a> en ocasión de las <a href="http://www.ant.org.ar/jornadas/">5tas. Jornadas Regionales de Software Libre</a>, en noviembre del 2005, en Rosario, Santa Fé.</p>
<p>Aunque se trata de material un tanto obsoleto, ya que está basado en las primeras versiones de Rails (en las cuales ni siquiera existían, por ejemplo, las &#8220;migrations&#8221;), decidí publicarlo de todas formas.</p>
<h3>Primeros pasos en Rails</h3>
<ul>
<li><a href="/files/5jrsl/primeros_pasos_rails.pdf">Presentación en formato PDF</a></li>
<li><a href="/files/5jrsl/primeros_pasos_rails.odp">Presentación en formato ODP</a> (<a href="http://www.openoffice.org/">OpenOffice.org</a>)</li>
</ul>
<p><span id="more-122"></span></p>
<p>Un pequeño comentario: Todavía recuerdo el gran esfuerzo de la gente del <strong>Grupo de usuarios de GNU/Linux de Rosario</strong> (<a href="http://www.lugro.org.ar/">LUGRO</a>) y de los integrantes de la <strong>Asociación Argentina de Nuevas Tecnologías</strong> (<a href="http://ant.org.ar/">ANT</a>) que lograron una organización impecable y, como siempre ocurre en estos eventos, un excelente clima de camaradería.</p>
<div class="centerpic"><img src="/files/5jrsl/rosario.jpg" alt="5JRSL"></div>
<p>En la foto aparecemos (de izquierda a derecha) <a href="http://blog.dgomez.com.ar/">Diego Gomez</a> (gran amigo y colega) , yo, <a href="http://www.bea.org.ar/">Beatriz Busaniche</a> (excelente periodista, integrante de la <a href="http://www.vialibre.org.ar">Fundación Vía Libre</a> y gran activista en favor del Software Libre), <a href="http://federratas.codigolibre.net/">Federico Heinz</a> (amigazo, también integrante de <a href="http://www.vialibre.org.ar">Vía Libre</a> y hacker de aquellos&#8230;) y <a href="http://criadoindomable.wordpress.com/">Sebastián Criado</a> (incansable, movedizo y persistente luchador por el software libre, entre otras causas).</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2008/10/02/charla-sobre-ruby-on-rails-en-rosario-5jrsl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mi charla en las 8vas. JRSL</title>
		<link>http://blog.smaldone.com.ar/2008/09/02/mi-charla-en-las-8vas-jrsl/</link>
		<comments>http://blog.smaldone.com.ar/2008/09/02/mi-charla-en-las-8vas-jrsl/#comments</comments>
		<pubDate>Wed, 03 Sep 2008 01:07:34 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Charlas]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Redes]]></category>
		<category><![CDATA[Software libre]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=120</guid>
		<description><![CDATA[Tal como comenté en el artículo anterior, tuve la gran satisfacción de dar una charla en las 8vas. Jornadas Regionales de Software Libre, realizadas del 20 al 22 de agosto pasado en la Universidad de Belgrano. Aunque en algún momento estará disponible en el sitio del evento (junto con el video de la charla), no [...]]]></description>
			<content:encoded><![CDATA[<p>Tal como comenté en el <a href="http://blog.smaldone.com.ar/2008/08/02/8vas-jornadas-regionales-de-software-libre/">artículo anterior</a>, tuve la gran satisfacción de dar una charla en las <a href="http://jornadas.cafelug.org.ar/">8vas. Jornadas Regionales de Software Libre</a>, realizadas del 20 al 22 de agosto pasado en la Universidad de Belgrano.</p>
<p>Aunque en algún momento estará disponible en el sitio del evento (junto con el video de la charla), no resití la tentación de publicar la presentación que utilicé.</p>
<h3>Experiencias en la implementación de un ISP con software libre</h3>
<ul>
<li><a href="/files/8jrsl/isp_software_libre.pdf">Presentación en formato PDF</a></li>
<li><a href="/files/8jrsl/isp_software_libre.odp">Presentación en formato ODP</a> (<a href="http://www.openoffice.org/">OpenOffice.org</a>)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2008/09/02/mi-charla-en-las-8vas-jrsl/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>La base de datos del sistema de escrutinio de MSA</title>
		<link>http://blog.smaldone.com.ar/2008/06/25/la-base-de-datos-del-sistema-de-escrutinio-de-msa/</link>
		<comments>http://blog.smaldone.com.ar/2008/06/25/la-base-de-datos-del-sistema-de-escrutinio-de-msa/#comments</comments>
		<pubDate>Wed, 25 Jun 2008 09:06:21 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Política]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Voto electrónico]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/?p=115</guid>
		<description><![CDATA[Como comenté en el artículo anterior, tuve la oportunidad de tener cierto contacto con el sistema utilizado por la empresa Magic Software Argentina (MSA) en el escrutinio provisorio de las elecciones municipales de mi ciudad, Río Cuarto. Los fiscales informáticos tuvimos acceso a la interfaz de consulta del proceso de escrutinio y a un volcado [...]]]></description>
			<content:encoded><![CDATA[<p>Como comenté en <a href="http://blog.smaldone.com.ar/2008/06/23/las-elecciones-en-rio-cuarto-y-msa/">el artículo anterior</a>, tuve la oportunidad de tener cierto contacto con el sistema utilizado por la empresa <a href="http://www.msa.com.ar/">Magic Software Argentina</a> (<strong>MSA</strong>) en el escrutinio provisorio de las elecciones municipales de mi ciudad, Río Cuarto.</p>
<p>Los fiscales informáticos tuvimos acceso a la interfaz de consulta del proceso de escrutinio y a un volcado de la base de datos, que se realizaba cada 5 minutos. Lamentablemente, no tuvimos acceso al código fuente de la aplicación (aunque espero poder acceder al informe de los peritos contratados por la Junta Electoral).</p>
<p>Una vez finalizado el escrutinio provisorio, surgieron diferencias en los totales de votantes de los dos cargos electivos. A raíz de esto, decidí inspeccionar la base de datos con el objetivo de generar el listado de las mesas en las cuales se originaba dicha diferencia. De dicho análisis resultó el siguiente artículo, en el que me permito realizar algunas críticas.</p>
<p><span id="more-115"></span></p>
<h3>El diagrama de Entidad-Relación (DER)</h3>
<p>El siguiente es el DER que nos envió la gente de <strong>MSA</strong>:</p>
<div class="centerpic"><img src="/files/msa/database.png" alt="Diagrama de Entidad-Relación de la Base de Datos" /></div>
<p>Como puede verse se trata de una base de datos realmente simple (apenas 11 tablas). Lo que me llamó la atención a primera vista fue la utilización de palabras en castellano (muchos programadores opinamos que los programas deberían estar documentados en inglés). Siguiendo con los nombres, por más que me esforcé no pude imaginar la diferencia entre &#8220;<em>ubicaciones</em>&#8221; y &#8220;<em>lugares</em>&#8220;. No encontré palabras como &#8220;<em>mesa</em>&#8220;, &#8220;<em>escuela</em>&#8220;, &#8220;<em>circuito</em>&#8220;. A mi entender, la elección de la nomenclatura no ha sido la mejor.</p>
<h3>Las claves primarias</h3>
<p>En 9 tablas de la base de datos, las claves primarias son del tipo &#8220;<code>varchar</code>&#8220;. Si bien esto no es del todo incorrecto, no es muy recomendable. Las claves primarias deberían ser del tipo &#8220;<code>integer</code>&#8220;, y además auto-incrementadas (<a href="http://www.postgresql.org/">PostgreSQL</a>, el RDBMS utilizado, lo soporta). Esto no solamente simplifica la asignación de valores a las claves (no hay que verificar la unicidad), sino que tiene un gran impacto en el desempeño de la base de datos.</p>
<p>Al utilizar el tipo &#8220;<code>varchar</code>&#8220;, cuya longitud es variable, el tamaño de los índices es mayor que usando &#8220;<code>integer</code>&#8220;. Esto, al aumentar la cantidad de filas de las tablas, se torna cada vez más importante (es deseable que los índices quepan en la caché del RDBMS). (En Río Cuarto había solamente 352 mesas y menos de 110.000 votantes, por lo cual no debe haberse notado la diferencia, pero en una elección más amplia seguramente lo haría.)</p>
<p>Un factor que agrava la situación es que la comparación de claves, al tratarse de cadenas, debe hacerse caracter a caracter (a diferencia de la comparación de valores enteros, que se realiza mediante una simple resta). Como veremos más adelante, en algunas tablas los valores del atributo clave en varias filas tienen un prefijo en común. Esto ralentiza aún más la comparación, y la situación es empeorada por la utlización de la codificación de caracteres <strong>UTF-8</strong>, debido a la implementación interna de <strong>PostgreSQL</strong>. </p>
<p>En mi opinión pudiendo usar valores enteros como clave, debieron haberlo hecho.</p>
<h3>¿Relación inexistente?</h3>
<p>Primero, una breve explicación del problema: La ciudad se divide en 3 circuitos electorales, en los cuales se encuentran las distintas escuelas, en donde a su vez están las mesas de votación. Como el sistema permite analizar circuitos, escuelas y mesas por separado, el primer diseño que cualquier programador pensaría es disponer de tres tablas: &#8220;circuitos&#8221;, &#8220;escuelas&#8221; y &#8220;mesas&#8221; (en el caso de una elección provincial o nacional se agregarían algunas más). Entre esas tablas habría relaciones 1:N, es decir &#8220;1 circuito tiene N escuelas, 1 escuela pertenece a un circuito&#8221;, &#8220;1 escuela tiene N mesas, 1 mesa pertenece a 1 escuela&#8221;.</p>
<p>Aprovechando el hecho de que tanto las escuelas como las mesas tienen los mismos atributos, decidieron usar una única tabla, denominada &#8220;ubicaciones&#8221;, según muestra el DER:</p>
<div class="centerpic"><img src="/files/msa/database_ubicaciones.png" alt="Entidad Ubicaciones" /></div>
<p>Es común utilizar este tipo de representación para estructuras de &#8220;árbol&#8221;. Esto se logra agregando como atributo una clave foránea que referencia a la clave primaria de la misma tabla. En las filas &#8220;padre&#8221;  dicho atributo tiene el valor &#8220;<code>NULL</code>&#8220;, en tanto que en las filas &#8220;hijas&#8221;, dicho atributo contiene el valor de la clave primaria del padre.</p>
<p>Sin embargo, el código SQL de la estructura de la tabla &#8220;<code>ubicaciones</code>&#8221; es el siguiente:</p>
<blockquote><p><code>
<pre>CREATE TABLE ubicaciones (
    id_ubicacion character varying(12) NOT NULL,
    clase character varying(30) NOT NULL,
    descripcion character varying(100),
    sexo character(1),
    CONSTRAINT ubicaciones_sexo_check CHECK
          (((sexo = 'F'::bpchar) OR (sexo = 'M'::bpchar)))
);</pre>
<p></code></p>
</blockquote>
<p>No existe ningún atributo que oficie de clave foránea para representar la relación en cuestión. ¿Cómo es esto posible?</p>
<p>Para comprender cómo está representada esta relación, fue necesario analizar los datos de la tabla (algo que, después de discutirlo con varios amigos, no pudimos llegar a la conclusión de qué principios de la teoría de bases de datos violenta, quizás algún lector pueda echar algo de luz al respecto). Encontre lo siguiente:</p>
<div class="centerpic"><img src="/files/msa/ubicaciones_data.png" alt="Tabla ubicaciones" /></div>
<p>El &#8220;truco&#8221; consiste en utilizar parte de la clave primaria de una fila, para referenciar a la fila &#8220;padre&#8221;. Por ejemplo, las claves de todas las mesas pertenecientes a la escuela &#8220;Adolfo Alsina&#8221; (cuya clave es &#8220;<code>RC0101</code>&#8220;) comienzan con el prefijo &#8220;<code>RC0101</code>&#8220;. De la misma manera, las claves de todas las escuelas del circuito &#8220;165&#8243; (cuya clave es &#8220;<code>RC01</code>&#8220;) comienzan con el prefijo &#8220;<code>RC01</code>&#8220;.</p>
<p>Ingenioso, pero totalmente innecesario y contrario a la teoría de bases de datos relacionales. De hecho, la relación está presente en el DER, pero no se ve reflejada en la base de datos. (Este tipo de representación de relaciones basadas en códigos era una práctica común de los programadores <strong>Cobol</strong> de otras épocas.)</p>
<h3>Tipos de datos</h3>
<p>Siguiendo con la tabla &#8220;<code>ubicaciones</code>&#8220;, podemos apreciar la siguiente restricción de integridad:</p>
<blockquote><p><code>
<pre>CONSTRAINT ubicaciones_sexo_check CHECK
         (((sexo = 'F'::bpchar) OR (sexo = 'M'::bpchar)))</pre>
<p></code></p>
</blockquote>
<p>El objetivo de esta restricción es verificar que el valor del atributo &#8220;<code>sexo</code>&#8221; (de tipo &#8220;<code>character(1)</code>&#8221; sea &#8220;<code>F</code>&#8221; o &#8220;<code>M</code>&#8220;. Una forma más simple y eficiente, es definir un tipo de datos enumerado:</p>
<blockquote><p><code>
<pre>CREATE TYPE sexos AS ENUM ('F', 'M')</pre>
<p></code></p>
</blockquote>
<p>Esta misma situación se produce en otras tablas, incluyendo la reiteración de casos como el que sigue:</p>
<blockquote><p><code>
<pre>CONSTRAINT usuarios_carga_check CHECK
          (carga = 'SI'::bpchar OR carga = 'NO'::bpchar)</pre>
<p></code></p>
</blockquote>
<p>Aquí, evidentemente, hubiera bastado definir al atributo &#8220;<code>carga</code>&#8221; de tipo &#8220;<code>boolean</code>&#8220;. (Esta restricción se encuentra en la tabla &#8220;<code>usuarios</code>&#8220;, en donde es reiterada tres veces.)</p>
<p>La tabla &#8220;<code>estados</code>&#8221; contiene los distintos estados en que puede encontrarse una planilla a lo largo del proceso:</p>
<blockquote><p><code>
<pre>CREATE TABLE estados (
    id_estado character varying(16) NOT NULL,
    descripcion character varying(50),
    bg_color1 character varying(7),
    bg_color2 character varying(7),
    prioridad integer,
    orden integer,
    accion character varying(20),
    CONSTRAINT estados_id_estado_check CHECK
         ((((((id_estado)::text = 'Espera'::text)
          OR ((id_estado)::text = 'En Proceso'::text))
          OR ((id_estado)::text = 'Intranet'::text))
          OR ((id_estado)::text = 'Internet'::text)))
);</pre>
<p></code></p>
</blockquote>
<p>Aquí la restricción de integridad tiene como objetivo que restringir los valores de la clave &#8220;<code>id_estado</code>&#8220;, pero esto deja sólo cuatro posibilidades (de hecho, ¡los cuatro estados posibles de una planilla!). Codificar en el esquema de la base de datos los valores (fijos) de las claves de las cuatro entradas que luego tendrá la tabla no parece una buena idea. (Nuevamente, de juzgarse necesario, debería haberse utilizado un tipo enumerado.)</p>
<p>Otro punto cuestionable es la elección de los valores para codificar cada estado: varias palabras, uso de mayúsculas y minúsculas.</p>
<h3>Otros errores</h3>
<p>En el DER puede verse lo siguiente:</p>
<div class="centerpic"><img src="/files/msa/database_error_1.png" alt="Error en el Diagrama de Entidad-Relación de la Base de Datos 1" /></div>
<p>Esto parece indicar que no puede haber más de 2 planillas en el mismo estado, algo que es falso (de hecho, inicialmente todas las planillas están en el estado &#8220;Espera&#8221; y al finalizar están en el estado &#8220;Internet&#8221;.</p>
<p>El DER también muestra la siguiente entidad:</p>
<div class="centerpic"><img src="/files/msa/database_error_2.png" alt="Error en el Diagrama de Entidad-Relación de la Base de Datos 2" /></div>
<p>La tabla que se corresponde con dicha entidad se llama &#8220;<code>configuracion</code>&#8221; (en singular).</p>
<p>Estos errores pueden parecer simples detalles (de hecho, en un sistema con solamente 11 tablas posiblemente lo sean), pero en un sistema más complejo pueden dificultar sensiblemente la programación, propendiendo a la comisión de errores.</p>
<h3>Conclusión</h3>
<p>Sin ser un experto en bases de datos relacionales (las utilizo desde hace más de 10 años como parte de mi trabajo, pero no me considero un especialista en la materia), los errores detectados me dan la pauta de que no se ha puesto el suficiente cuidado en el diseño de la base de datos, fundamentalmente de cara a la escalabilidad del sistema para soportar volumenes de carga mayores a los generados por proceso electoral como el de Río Cuarto (2 cargos electivos, 6 listas y 352 mesas).</p>
<p>De más está decirlo, dejo abierto este espacio a críticas y sugerencias respecto de este tema.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2008/06/25/la-base-de-datos-del-sistema-de-escrutinio-de-msa/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>El secreto de Ruby: Las &#8220;clausuras&#8221;</title>
		<link>http://blog.smaldone.com.ar/2007/04/26/el-secreto-de-ruby-las-clausuras/</link>
		<comments>http://blog.smaldone.com.ar/2007/04/26/el-secreto-de-ruby-las-clausuras/#comments</comments>
		<pubDate>Thu, 26 Apr 2007 19:37:04 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2007/04/26/el-secreto-de-ruby-las-clausuras/</guid>
		<description><![CDATA[Uno de los mecanismos más potentes provistos por el lenguaje Ruby es la posibilidad de manipular bloques de código que pueden ser pasados como argumentos en la invocación a funciones. Este mecanismo se denomina &#8220;clausura&#8221; (&#8220;closure&#8220;, en inglés), y aunque no es exclusivo de Ruby (es un viejo conocido en Smalltalk y Lisp, encontrándose aún [...]]]></description>
			<content:encoded><![CDATA[<p>Uno de los mecanismos más potentes provistos por el lenguaje <strong>Ruby</strong> es la posibilidad de manipular bloques de código que pueden ser pasados como argumentos en la invocación a funciones.</p>
<p>Este mecanismo se denomina &#8220;<em>clausura</em>&#8221; (&#8220;<a href="http://en.wikipedia.org/wiki/Closure_(computer_science)"><em>closure</em></a>&#8220;, en inglés), y aunque no es exclusivo de <strong>Ruby</strong> (es un viejo conocido en <strong>Smalltalk</strong> y <strong>Lisp</strong>, encontrándose aún en <strong>Perl</strong> y <strong>Python</strong>), la simplicidad de su uso lo transforma en una herramienta muy poderosa y flexible.</p>
<p><span id="more-110"></span></p>
<p>Quienes hemos programado en lenguajes funcionales (como <strong>ML</strong> y <strong>Haskell</strong>), siempre hemos extrañado en los lenguajes imperativos la posibilidad de pasar funciones como parámetros de forma &#8220;natural&#8221; (no a la manera en que puede hacerse en <strong>Java</strong> o <strong>Pascal</strong>, sino incluyendo las ligaduras al <em>scope</em> actual).</p>
<h3>Un ejemplo simple</h3>
<p>Supongamos que deseamos escribir una función que tome como parámetro un arreglo de valores enteros y retorne otro arreglo, producto de multiplicar por 2 cada uno de los elementos del parámetro.</p>
<p>En <strong>Java</strong> podríamos escribir algo así:</p>
<blockquote><p><code><strong>public static</strong> ArrayList double(ArrayList x)<br />
<strong>{</strong><br />
&nbsp;&nbsp;ArrayList y = <strong>new</strong> ArrayList();<br />
&nbsp;&nbsp;<strong>for</strong> (Iterator i = x.iterator(); i.hasNext(); )<br />
&nbsp;&nbsp;&nbsp;&nbsp;y.add(i.next() * 2);<br />
&nbsp;&nbsp;<strong>return</strong> y;<br />
<strong>}</strong></code></p>
</blockquote>
<p><em>(Obviamente, olvidándonos de algunos casts para simplificar la lectura.)</em></p>
<p><!-- y.add(new Integer(((Integer)(i.next())).intValue() * 2 )); --></p>
<p>O, en <strong>PHP</strong>:</p>
<blockquote><p><code><strong>function</strong> double($x) <strong>{</strong><br />
&nbsp;&nbsp;$y = <strong>array</strong>();<br />
&nbsp;&nbsp;<strong>foreach</strong> ($x as $e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;array_push($y, $e*2);<br />
&nbsp;&nbsp;<strong>return</strong> $y;<br />
<strong>}</strong></code></p>
</blockquote>
<p>En tanto que en un lenguaje funcional, como <strong>Haskell</strong> podríamos escribir lo siguiente:</p>
<blockquote><p><code>double x = map (*2) x</code></p>
</blockquote>
<p>O, simplemente:</p>
<blockquote><p><code>double = map (*2)</code></p>
</blockquote>
<p>&#8220;<code>map</code>&#8221; toma como parámetro una función (en este caso, la función parcial &#8220;<code>*2</code>&#8220;) y un arreglo, devolviendo el resultado de aplicar dicha función a cada elemento del arreglo.</p>
<p>Veamos un ejemplo de la evaluación de <code>double</code>:</p>
<blockquote><p><code>double [1, 2, 3]<br />
[1*2, 2*2, 3*2]<br />
[2, 4, 6]</code></p>
</blockquote>
<p>Como podemos ver, la principal ventaja de los lenguajes funcionales es su expresividad, en el sentido de que el código del programa expresa de forma mucho más clara qué es lo que este hace (multiplicar por 2 cada elemento del arreglo).</p>
<p>En <strong>Ruby</strong>, aunque no es un lenguaje funcional, la posibilidad de pasar funciones (o, en general, bloques de código) como parámetro se presenta de forma igualmente sencilla:</p>
<blockquote><p><code><strong>def</strong> double(a)<br />
&nbsp;&nbsp;a.map {|e| e*2}<br />
<strong>end</strong></code></p>
</blockquote>
<p>&#8220;<code>map</code>&#8221; es un método de la clase &#8220;<code>Array</code>&#8221; que toma un bloque de código y lo ejecuta para cada elemento del arreglo, devolviendo la colección de los resultados producidos. El fragmento &#8220;<code>|e|</code>&#8221; establece la ligadura entre el identificador &#8220;<code>e</code>&#8221; y el elemento actual.</p>
<h3>Explotando la potencia de las clausuras</h3>
<p>Supongamos ahora que tenemos una clase &#8220;<code>Person</code>&#8220;, cuyo método &#8220;<code>age</code>&#8221; calcula la edad de la persona. Si tenemos una colección de personas en el arreglo &#8220;<code>people</code>&#8221; y deseamos obtener sólo aquellas que son mayores de 18 años, en <strong>Ruby</strong> podríamos escribir lo siguiente:</p>
<blockquote><p><code>people.select{|p| p.age &gt; 18}</code></p>
</blockquote>
<p><em>(El método &#8220;<code>select</code>&#8221; aplica una función lógica (booleana) a cada elemento y devuelve sólo aquellos para los cuales resultó verdadera.)</em></p>
<p>En tanto que si, además, quisiéramos mostrar el nombre de cada una de ellas, podríamos escribir:</p>
<blockquote><p><code>people.select{|p| p.age &gt; 18}.each{|p| puts p.name}</code></p>
</blockquote>
<p><em>(El método &#8220;<code>each</code>&#8221; aplica el bloque a cada elemento del arreglo.)</em></p>
<p>O bien, de forma más compacta (sin generar un segundo arreglo):</p>
<blockquote><p><code>people.each{|p| puts p.name if p.age &gt; 18}</code></p>
</blockquote>
<h3>Comparación con otros lenguajes</h3>
<h4>Problema 1</h4>
<p>Convertir las cadenas de un arreglo a mayúsculas.</p>
<p>En <strong>PHP</strong>:</p>
<blockquote><p><code><strong>for</strong> ($i = 0; $i &lt; count($a); $i++)<br />
&nbsp;&nbsp;$a[$i] = strtoupper($a[$i]);</code></p>
</blockquote>
<p>En <strong>Ruby</strong>:</p>
<blockquote><p><code>a.map!{|e| e.upcase}</code></p>
</blockquote>
<p><em>(El método &#8220;<code>map!</code>&#8220;, a diferencia de &#8220;<code>map</code>&#8220;, modifica el arreglo, en vez de retornar uno nuevo.)</em></p>
<h4>Problema 2</h4>
<p>Dado un arreglo de la forma <code>[["nombre", "apellido"]]</code>, obtener un arreglo de la forma <code>["apellido, nombre"]</code>. Por ejemplo, dado:</p>
<blockquote><p><code>[["Javier","Smaldone"],["Santiago","Lobos"],["Bon","Scott"]]</code></p>
</blockquote>
<p>obtener:</p>
<blockquote><p><code>["Smaldone, Javier", "Lobos, Santiago", "Scott, Bon"]</code></p>
</blockquote>
<p>En <strong>PHP</strong>:</p>
<blockquote><p><code>$b = array();<br />
<strong>foreach</strong> ($a as $e)<br />
&nbsp;&nbsp;array_push($b, $e[1] . ', ' . $e[0]);</code></p>
</blockquote>
<p>En <strong>Ruby</strong>:</p>
<blockquote><p><code>b = a.map{|e| e.reverse.join(', ')}</code></p>
</blockquote>
<h4>Problema 3</h4>
<p>Calcular la edad promedio de las personas mayores de edad (según el ejemplo planteado anteriormente).</p>
<p>En <strong>PHP</strong>:</p>
<blockquote><p><code>$sum = 0;<br />
$count = 0;<br />
<strong>foreach</strong> ($people as $p) <strong>{</strong><br />
&nbsp;&nbsp;<strong>if</strong> ($p-&gt;age &gt; 18) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;$sum += $p-&gt;age;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$count++;<br />
&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong><br />
<strong>if</strong> ($count) print $sum/$count;</code></p>
</blockquote>
<p>Una solución posible en <strong>Ruby</strong> sería:</p>
<blockquote><p><code>adults = people.select{|p| p.age &gt; 18}<br />
sum = 0<br />
adults.each {|p| sum += p.age }<br />
puts sum.to_f/adults.size if adults.size &gt; 0</code></p>
</blockquote>
<p>Pero usando todo el poder de las clausuras y las funciones &#8220;<code>map</code>&#8221; e &#8220;<code>inject</code>&#8220;, podríamos escribir:</p>
<blockquote><p><code>adults = people.select{|p| p.age > 18}<br />
sum = adults.map{|p| p.age}.inject{|ac,e| ac+e}<br />
puts sum.to_f/adults.size if sum</code></p>
</blockquote>
<p><em>( &#8220;<code>inject</code>&#8221; ejecuta el bloque, asignando la segunda variable a cada elemento del arreglo y asignando el resultado de la ejecución a la primera, manteniendo su valor a través de cada iteración.)</em></p>
<h4>Problema 4</h4>
<p>Dado un arreglo de cadenas, obtener la de mayor longitud.</p>
<p>En <strong>PHP</strong>:</p>
<blockquote><p><code>$max = '';<br />
<strong>foreach</strong> ($a as $e)<br />
&nbsp;&nbsp;<strong>if</strong> (strlen($e) &gt; strlen($max))<br />
&nbsp;&nbsp;&nbsp;&nbsp;$max = $e;</code></p>
</blockquote>
<p>En <strong>Ruby</strong>:</p>
<blockquote><p><code>max = a.inject {|m, e| e.length > m.length ? e : m }</code></p>
</blockquote>
<p><em>(Adicionalmente, hemos usado el operador de asignación condicional &#8220;<code>cond ? val1 : val2</code>&#8220;, que retorna &#8220;<em>val1</em>&#8221; si &#8220;<em>cond</em>&#8221; es verdadero y &#8220;<em>val2</em>&#8221; en caso contrario.)</em></p>
<h3>Conclusión</h3>
<p>La posibilidad de utilizar clausuras nos acerca a la simplicidad y la expresividad de la programación funcional. Este es uno de los puntos fuertes del lenguaje <strong>Ruby</strong> (y la envidia de otros lenguajes, como <strong>Java</strong>, cuya comunidad <a href="http://barrapunto.com/articles/07/04/25/2026232.shtml">está debatiendo</a> sobre la posibilidad de incorporarlo en la versión 7).</p>
<p><em>(Y sí, se lo que pensará mi amigo Ricardo cuando lea este artículo. Es cierto, <a href="http://kapcoweb.com/p/static/docs/la-venganza-de-los-nerds/la-venganza-de-los-nerds.html">los lenguajes tienden a <strong>Lisp</strong></a>.)</em></p>
<p>Algunas referencias:</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Closure_(computer_science)">Clausuras en Wikipedia (en inglés).</a></li>
<li><a href="http://www.martinfowler.com/bliki/Closure.html">Más sobre clausuras en <strong>Ruby</strong>.</a></li>
<li><a href="http://ivan.truemesh.com/archives/000392.html">Clausuras en <strong>Python</strong>.</a></li>
<li><a href="http://www.unix.org.ua/orelly/perl/advprog/ch04_03.htm">Clausuras en <strong>Perl</strong>.</a></li>
<li><a href="http://gafter.blogspot.com/2007/01/definition-of-closures.html">Historia de las clausuras.</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2007/04/26/el-secreto-de-ruby-las-clausuras/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>El silencio continúa</title>
		<link>http://blog.smaldone.com.ar/2007/04/10/el-silencio-continua/</link>
		<comments>http://blog.smaldone.com.ar/2007/04/10/el-silencio-continua/#comments</comments>
		<pubDate>Tue, 10 Apr 2007 21:48:14 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2007/04/10/el-silencio-continua/</guid>
		<description><![CDATA[Hace ya casi un mes dije que retomaría la actividad en mi blog, pero lamentablemente los compromisos laborales me lo han impedido. Y como no puedo conmigo mismo, decidí escribir este pequeño post para mostrar algo de lo que estoy haciendo. Se trata de una herramienta para generar diagramas de clases de aplicaciones Ruby on [...]]]></description>
			<content:encoded><![CDATA[<p>Hace ya casi un mes dije que retomaría la actividad en mi blog, pero lamentablemente los compromisos laborales me lo han impedido.</p>
<p>Y como no puedo conmigo mismo, decidí escribir este pequeño post para mostrar algo de lo que estoy haciendo. Se trata de una herramienta para generar diagramas de clases de aplicaciones <strong><em>Ruby on Rails</em></strong>.</p>
<p>Comencé a escribirla por necesidad (para los desarrollos en los que estoy trabajando), pero luego decidí extenderla un poco más (para, además, extender mis conocimientos de <strong><em>Ruby</em></strong>).</p>
<p>Por ahora, nada más (en los próximos días escribiré con mayor detalle sobre él). Les presento a mi nuevo juguete: <a href="http://railroad.rubyforge.org/"><strong><em>RailRoad</em></strong></a>.</p>
<p>(En <strong>InfoQ</strong> han publicado <a href="http://www.infoq.com/news/2007/04/rails-diagrams-railroad">una revisión sobre <em>RailRoad</em></a>.)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2007/04/10/el-silencio-continua/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Quién fue Edsger Dijkstra</title>
		<link>http://blog.smaldone.com.ar/2006/12/26/quien-fue-edsger-dijkstra/</link>
		<comments>http://blog.smaldone.com.ar/2006/12/26/quien-fue-edsger-dijkstra/#comments</comments>
		<pubDate>Tue, 26 Dec 2006 18:48:03 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Ciencia]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/12/26/quien-fue-edsger-dijkstra/</guid>
		<description><![CDATA[Muchos conocen a Edsger Wybe Dijkstra por sus aportes técnicos a las ciencias de la computación (algoritmos sobre grafos, semáforos, su crítica al &#8220;goto&#8220;, entre tantos otros). Otros quizás hayan leído sobre sus opiniones acerca de la programación como disciplina matemática y su lucha contra el avance de los intereses industriales sobre la ciencia. Pocos [...]]]></description>
			<content:encoded><![CDATA[<p>Muchos conocen a <a href="http://es.wikipedia.org/wiki/Edsger_Wybe_Dijkstra">Edsger Wybe Dijkstra</a> por sus aportes técnicos a las ciencias de la computación (algoritmos sobre grafos, semáforos, su crítica al &#8220;<em>goto</em>&#8220;, entre tantos otros). Otros quizás hayan leído sobre sus opiniones acerca de la programación como disciplina matemática y su lucha contra el avance de los intereses industriales sobre la ciencia.</p>
<p>Pocos saben, sin embargo, quién fue este gran hombre y su verdadero aporte a través de más de 40 años de actividad académica, científica e industrial. A continuación he traducido <a href="http://www.cs.utexas.edu/users/EWD/memorial/gries.html">el discurso</a> pronunciado por <a href="http://en.wikipedia.org/wiki/David_Gries">David Gries</a>, otro notable científico de la computación, en oportunidad de un homenaje a poco tiempo de su fallecimiento.</p>
<p><span id="more-96"></span></p>
<h3>¿Cómo podemos describir a Esdger W. Dijkstra a quienes no lo conocieron?</h3>
<h4>por David Gries</h4>
<p><strong>En el Acto en Honor a la Memoria de E. W. Dijkstra<br />
21 de noviembre de 2002</strong></p>
<p>Edsger Wybe Dijkstra nos dio mucho más que sólo material y terminología técnicos. Su profunda integridad, sabiduría y compasión nos llegaron de varias influentes y, a menudo, frescas y sorprendentes formas -algunas veces para la perplejidad de sus amigos y colegas. Encontraremos gran dificultad en explicar a otros cómo fue conocer a Edsger y trabajar con él. Aquellos que no tuvieron la fortuna de conocerlo nunca entenderán de lo que se perdieron.</p>
<p>¿Cómo explicas cómo era tomar un curso sobre metodología matemática de Edsger, verlo demostrar día tras día su destacable habilidad para resolver problemas que nosotros no podemos resolver, aún luego de que él hiciera explícitos los principios y las técnicas involucradas?</p>
<p>No puedes.</p>
<p>¿Cómo describes una reunión de su Club del Martes por la Tarde, donde los participantes resolvían un problema juntos, o leían un artículo en voz alta, frase por frase, comentando sobre la estructura de las frases, el estilo, la organización general, las pruebas, el contenido técnico; sobre cualquier cosa que pudiera mejorarse?</p>
<p>No puedes. El Club del Martes por la Tarde continúa, pero nunca será lo mismo sin su fundador, Edsger W. Dijkstra.</p>
<p>¿Cómo puedes explicar la experiencia, que cientos tuvieron pero ya no tendrán, de recibir una carta manuscrita de Edsger, llena de comentarios técnicos interesantes, opiniones sobre gente y su trabajo; siempre junto con al menos un juego de palabras, como así también con información sobre él y su familia?</p>
<p>No puedes. Al leer una carta así, uno sabía que Edsger te había brindado su atención de una manera en que pocos estaban dispuestos a hacerlo.</p>
<p>¿Cómo ilustras el estar teniendo una conversación con Edsger, experimentar un minuto o dos de silencio luego de una pregunta, preguntándote qué le pasa, pero luego verlo salir con una respuesta brillante y darte cuenta de era una de las pocas personas que pensaban antes de hablar?</p>
<p>No puedes.</p>
<p>¿Cómo puedes eplicar el enigma de que a muchos les pareciera distante y frío, cuando era realmente una persona amable y sociable, que era compasivo y servicial en el momento en que lo necesitabas y que caería a lo de sus amigos por la tarde con su esposa, sin previo aviso, para conversar durante una hora?</p>
<p>Imposible hacerlo. Sólo aquellos lo suficientemente afortunados en conocer a Edsger conocieron al verdadero Edsger.</p>
<p>¿Cómo explicas no sólo su absoluta integridad y habilidad en discriminar entre preocupaciones políticas e industriales, en oposición a las metas científicas; sino también su valor para expresar esas diferencias en público?</p>
<p>No puedes. Un científico de la computación menos valiente una vez dijo sobre un informe suyo, &#8220;Edsger tiene razón, ¡pero no puedes decir esas cosas!&#8221;. Bien, Edsger las dijo porque sentía que debían ser dichas.</p>
<p>¿Cómo es que Edsger se escribía con tantos pero compartió la autoría de artículos con tan pocos? ¿Y por qué uno de ellos -uno de sus estudiantes de doctorado y más cercano colega- Netty van Gasteren, nos dejó en la flor de su vida justo un mes antes de que falleciera Edsger? Ah, Netty, te extrañaremos tanto como a Edsger.</p>
<p>Si, cualquiera puede leer sus 1300 ensayos, reportes, informes técnicos y revisiones. Pero no es lo mismo que conocerlo, hablar con él, escribirse con él, socializar con él y trabajar con él; y quienes hemos tenido esas experiencias no podemos transportar la sensación a otros.</p>
<p>Rutger, Marcus y Femke, fueron muy sabios en elegir a Ria y Edsger como sus padres. A la vez que estoy seguro de que hubieron dificultades en crecer a la sombra de semejante hombre, así como hay dificultades en cualquier familia, esa experiencia los ha hecho mejores. Esperamos que nuestros caminos se crucen a menudo, porque además de apreciarlos por quienes son, vemos a Edsger en ustedes.</p>
<p>Querida Ria, tu y Edsger fueron uno. Estuviste con él en sus emprendimientos en la ciencia de la computación más que cualquier esposa de la ciencia de la computación. Viajaste con él y asististe a las conferencias, tomaste a sus colegas como tus amigos y les diste la bienvenida en tu hogar. Ahora, enfrentas la vida con sólo recuerdos de Edsger, esperamos que continúes siendo nuestra amiga y parte de nuestras vidas.</p>
<p>Edsger, tuviste tus debilidades físicas y mentales, tus fragilidades humanas, como todos las tenemos. Pero en muchos aspectos, fuiste un gigante entre nosotros. Tu memoria -la de tu integridad, sabiduría, amistad y compasión- nos inspirará por el resto de nuestras vidas.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/12/26/quien-fue-edsger-dijkstra/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Citas de Dijkstra</title>
		<link>http://blog.smaldone.com.ar/2006/12/24/citas-de-dijkstra/</link>
		<comments>http://blog.smaldone.com.ar/2006/12/24/citas-de-dijkstra/#comments</comments>
		<pubDate>Sun, 24 Dec 2006 22:31:00 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/12/24/citas-de-dijkstra/</guid>
		<description><![CDATA[Releyendo los agresivos comentarios de un personaje pseudo-anónimo respecto del artículo sobre UML, recordé un excelente manuscrito de Edsger Dijkstra llamado &#8220;Respuestas a preguntas de estudiantes de Ingeniería de Software&#8221; (EWD 1035). Para facilitar su difusión (y para tenerlo siempre a mano), decidí traducirlo. ¡Que lo disfrute! Respuestas a preguntas de estudiantes de Ingeniería de [...]]]></description>
			<content:encoded><![CDATA[<p>Releyendo los <a href="http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/#comment-4544">agresivos comentarios</a> de un personaje pseudo-anónimo respecto del <a href="http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/">artículo sobre <strong>UML</strong></a>, recordé un excelente manuscrito de <a href="http://es.wikipedia.org/wiki/Edsger_Wybe_Dijkstra">Edsger Dijkstra</a> llamado &#8220;<em>Respuestas a preguntas de estudiantes de Ingeniería de Software</em>&#8221; (<a href="http://www.cs.utexas.edu/users/EWD/transcriptions/EWD13xx/EWD1305.html">EWD 1035</a>).</p>
<p>Para facilitar su difusión (y para tenerlo siempre a mano), decidí traducirlo. ¡Que lo disfrute!</p>
<p><span id="more-95"></span></p>
<h3>Respuestas a preguntas de estudiantes de Ingeniería de Software</h3>
<p>[La reconstrucción aproximada de las preguntas se deja como ejercicio al lector.]</p>
<ul>
<li>Los aparatos no son necesariamente una mejora. Véase la sucesión<br/><br />
<em>Pizarrón -&gt; Proyector de transparencias -&gt; Power Point</em></li>
<li>Y no necesito perder mi tiempo con una computadora sólo porque soy un científico de la computación. [A los investigadores en medicina no se les exige sufrir las enfermedades que investigan.]</li>
<li>No es el negocio de la ciencia de la computación el promover la &#8220;computerización&#8221;, digamos, desarrollando aplicaciones exigentes para crear un mercado para la próxima generación de hardware. [A los investigadores en medicina no se les pide desarrollar nuevas enfermedades para crear un mercado para nuevos productos farmacéuticos.]</li>
<li>No es la tarea de la Universidad el ofrecer lo que la sociedad pide, sino lo que la sociedad necesita.<br />
[Las cosas que la sociedad pide son por lo general comprendidas, y no se necesita una universidad para eso. La universidad debe ofrecer lo que nadie más puede proveer.]</li>
<li>Estamos delineados por las herramientas que utilizamos, en particular: los formalismos que usamos definen nuestros hábitos de razonamiento, para mejor o peor, y esto significa que debemos ser muy cuidadosos en la elección de qué aprendemos y enseñamos, ya que desaprender no es posible.<br />
[Hace varios años, si pudiera usar un nuevo asistente, un prerrequisito sería "Sin exposición previa a FORTRAN". En las escuelas de Siberia, la enseñanza del BASIC no estaba permitida.]
</li>
<li>Un programador debe ser capaz de demostrar que su programa tiene las propiedades requeridas. Si esto se deja para más adelante, es seguro que no será capaz de cumplir con su obligación: sólo si permite que esta obligación influencie su diseño, existe la esperanza de que pueda cumplirla. Solamente verificar a posteriori le deniega esa sana influencia y por lo tanto es poner el carro delante del caballo, pero es exactamente lo que ocurre en las casas de software donde &#8220;programación&#8221; y &#8220;aseguramiento de la calidad&#8221; (&#8220;quality assurance&#8221;) son hechos por grupos distintos. [No hace falta decirlo, esas casas entregan productos sin garantía.]</li>
<li>Las técnicas requeridas para el razonamiento efectivo son bastante formales, pero mientras la programación sea hecha por gente que no las domina, la crisis del software permanecerá entre nosotros y será considerada una enfermedad incurable. Y ya sabe lo que provocan las enfermedades incurables: abren las puertas a curanderos y charlatanes, quienes en este caso toman la forma de gurús de la Ingeniería de Software.</li>
<li>Algunos de ustedes dudan que las ya mencionadas &#8220;técnicas del razonamiento efectivo&#8221;, agradables como lo son para programas pequeños, sean escalables -cito- &#8220;dado el desalentador tamaño y la escarpada complejidad de muchos programas&#8221;. Bien, serán inútiles si se usan para desenmarañar el lío horrendo producido por un grupo de programadores incompetentes y desorganizados. Su poder se manifiesta en la etapa de construcción, donde (i) tienden a producir textos mucho más cortos de los que se producirían de otra forma y (ii) la longitud de las derivaciones de programas tienden a crecer no mucho más que linealmente con la longitud de los programas derivados. Finalmente, los programas así producidos son infinitamente mejores que la basura usual.</li>
<li>No debemos olvidar que los programadores viven en un mundo de artefactos, un hecho que los distingue de la mayoría de los demás científicos. El programador no debe preguntar qué tan aplicables son las técnicas de buena programación, debe crear un mundo en el cual son aplicables; esta es la única manera de producir un diseño de alta calidad. A esto debo agregar una cita de <a  href="http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD898.html">EWD898</a> (1984):<br />
&#8220;<em>Las capacidades de las máquinas nos dan lugar en abundancia para hacer un lío. ¡Oportunidades ilimitadas para ensuciar las cosas! Desarrollar la disciplina intelectual austera de mantener las cosas lo suficientemente simple es, en este entorno, un desafío formidable, tanto técnica como educativamente.</em>&#8221;
</li>
<li>En respuesta a las preguntas de por qué enseñamos cosas inútiles que la industria ignora, los refiero a <a href="http://www.cs.utexas.edu/users/EWD/ewd09xx/EWD920.PDF">EWD920</a> (1985). Permítanme citar un párrafo:<br />
<br />
&#8220;<em>Volviendo a la pregunta original: ¿puede la ciencia de la computación salvar a la industria de la computación? Mi respuesta es &#8220;Si la industria de la computación puede ser salvada, sólo la ciencia de la computación puede hacerlo&#8221;. Pero puede tomar mucho tiempo antes de que la industria de la computación -en particular, las compañías bien establecidas- compartan esta visión. Casi con seguridad tomará más que el periodo limitado por el cual planifican sus futuros. Mientras tanto, el mundo académico -que tradicionalmente planifica mirando mucho más adelante- no tiene elección. Tiene que refinar y enseñar al máximo de sus habilidades cómo debe ser hecha la computación; si cediera a la presión de propagar las malas prácticas actuales, sería mejor que quebrara.</em>&#8220;</p>
<p>Pero para destacar cuánta paciencia necesitamos, déjenme darles otra cita antigua (de 1988)</p>
<p>&#8220;<em>Muy poca gente reconoce que la alta tecnología tan celebrada actualmente es esencialmente una tecnología matemática.</em>&#8220;</p>
<p>(del reporte &#8220;2nd David&#8221;, llamado así en honor al director del comité, Dr. E. E. David Jr.)</li>
<li>No, me temo que la ciencia de la computación ha sufrido la popularidad de Internet. Ha atraído a una cantidad creciente -¡por no decir abrumadora!- de estudiantes con muy poca inclinación científica, y en la investigación sólo ha consolidado la prevaleciente (y algo vulgar) obsesión con la velocidad y la capacitdad.</li>
<li>Si, comparto su preocupación: cómo programar bien -aunque un tema enseñable apenas se enseña. La situación es similar en las matemáticas, donde el plan de estudios explícito está confinado a los resultados matemáticos; cómo hacer matemáticas es algo que el estudiante debe absorber por ósmosis, como el hablar. Una razón para preferir argumentos de cálculo mediante la manipulación de símbolos, es que su diseño puede enseñarse mucho mejor el diseño de argumentos verbales/pictóricos. La introducción a gran escala de cursos de tal metodología de cálculo, sin embargo, encontría problemas políticos insalvables.</li>
<li>En el negocio del software hay empresas para las cuales no está claro que la ciencia pueda ayudarlas. Que la ciencia debería intentarlo tampoco está claro.</li>
</ul>
<p>Austin, 28 de noviembre de 2000</p>
<p><strong>prof. dr. Edsger W. Dijkstra</strong><br />
Departamento de Ciencias de Computación<br />
Universidad de Texas<br />
Austin, TX 78712 &#8211; 1188<br />
USA</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/12/24/citas-de-dijkstra/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>AJAX no es AJAX</title>
		<link>http://blog.smaldone.com.ar/2006/12/11/ajax-no-es-ajax/</link>
		<comments>http://blog.smaldone.com.ar/2006/12/11/ajax-no-es-ajax/#comments</comments>
		<pubDate>Mon, 11 Dec 2006 12:24:11 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/12/11/ajax-no-es-ajax/</guid>
		<description><![CDATA[AJAX es uno de los términos de moda en el mundo de la Web 2.0 y las aplicaciones web. Como usualmente ocurre en estos casos, aparece rodeado de un manto de misterio y mucho palabrerío. ¿Es una tecnología? ¿un lenguaje? ¿una librería? Tratar de explicar qué es AJAX me recuerda a un pasaje de la [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://es.wikipedia.org/wiki/AJAX"><strong>AJAX</strong></a> es uno de los términos de moda en el mundo de la <a href="http://es.wikipedia.org/wiki/Web_2.0">Web 2.0</a> y las aplicaciones web. Como usualmente ocurre en estos casos, aparece rodeado de un manto de misterio y mucho palabrerío. ¿Es una tecnología? ¿un lenguaje? ¿una librería?  Tratar de explicar qué es  <strong>AJAX</strong> me recuerda a un pasaje de la película &#8220;<em>The Matrix</em>&#8220;:</p>
<blockquote>
<p><em><strong>Niño:</strong> No trates de doblar la cuchara, eso es imposible. En cambio, sólo trata de darte cuenta de la verdad.</em></p>
<p><em><strong>Neo:</strong> ¿Qué verdad?</em></p>
<p><em><strong>Niño:</strong> La cuchara no existe.</em></p>
</blockquote>
<p><strong>AJAX</strong> es simplemente una técnica, o mejor dicho, la combinación de varias técnicas. Si bien su nombre significa &#8220;<em>JavaScript y XML asincrónicos</em>&#8220;, no tiene necesariamente que ver con <em>Javascript</em> ni con <em>XML</em>. De ahí el título de este artículo.</p>
<p><span id="more-90"></span></p>
<h3>Un ejemplo simple</h3>
<p>Supongamos que tenemos el siguiente <em>script</em> escrito en <strong>PHP</strong>, para realizar la búsqueda de personas por apellido:</p>
<h4>buscar.php</h4>
<blockquote>
<p><code><br />
<strong>&lt;?</strong><br />
$personas = <strong>array</strong> (<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>array</strong> ( "Javier", "Smaldone"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>array</strong> ( "John", "Smith"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>array</strong> ( "Santiago", "Lobos"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>array</strong> ( "Jorge", "Lobatto")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);<br />
$apellido = $_POST['apellido'];<br />
<strong>if</strong> ($apellido) <strong>{</strong><br />
&nbsp;&nbsp;$respuesta = '';<br />
&nbsp;&nbsp;<strong>foreach</strong> ($personas as $indice => $persona) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> (preg_match("/^$apellido/i", $persona[1]))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$respuesta .= "$persona[0] $persona[1]&lt;br&gt;";<br />
&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;<strong>if</strong> (!$respuesta)<br />
&nbsp;&nbsp;&nbsp;&nbsp;$respuesta = "&lt;i&gt;No hay coincidencias con '$apellido'&lt;/i&gt;";<br />
&nbsp;&nbsp;<strong>print</strong> $respuesta;<br />
<strong>}</strong><br />
<strong>?&gt;</strong><br />
</code></p>
</blockquote>
<p>Como podemos apreciar, este <em>script</em> produce como salida la lista de personas cuyo apellido comienza con el valor de la variable <strong>apellido</strong> recibida desde el requerimiento <strong>HTTP</strong>. Dicha salida no es un documento <em>HTML</em> completo, sino solamente un fragmento (formateado con <em>&lt;br&gt;</em> y <em>&lt;i&gt;</em>). (Por simplicidad los datos de personas están almacenados en un arreglo, pero podrían también estar en una base de datos.)</p>
<p>Por otra parte, tenemos el siguiente archivo <em>HTML</em>:</p>
<h4>buscar.html</h4>
<blockquote>
<p><code><br />
<strong>&lt;html&gt;</strong><br />
<strong>&lt;head&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;title&gt;</strong>Personas<strong>&lt;/title&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;script</strong> type="text/javascript" src="buscar.js"<strong>&gt;&lt;/script&gt;</strong><br />
<strong>&lt;/head&gt;</strong><br />
<strong>&lt;body&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;form</strong> name="formulario" onsubmit="return false"<strong>&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;input</strong> type="text" name="apellido" autocomplete="off"<strong>&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;input</strong> type="button" value="Buscar" onclick="JavaScript:buscar()"<strong>&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;/form&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;div</strong> id="resultado"<strong>&gt;&lt;/div&gt;</strong><br />
<strong>&lt;/body&gt;</strong><br />
<strong>&lt;/html&gt;</strong><br />
</code></p>
</blockquote>
<p>Podemos ver que este el documento <em>HTML</em> incluye el archivo <strong>buscar.js</strong> que contiene código <em>JavaScript</em>. Además, el formulario no tiene una acción asociada (el evento <strong>onsubmit</strong> siempre devolverá falso, por lo cual nunca se &#8220;<em>enviará</em>&#8220;). En cambio, el botón &#8220;<em>Buscar</em>&#8221; tiene asociado al evento <strong>onclick</strong> una llamada a la función <em>JavaScript</em> <strong>buscar()</strong>. Debemos notar, además, que el documento contiene un <em>div</em> vacío cuyo identificador es <strong>resultado</strong>.</p>
<p>A continuación, el contenido de <strong>buscar.js</strong>:</p>
<h4>buscar.js</h4>
<blockquote>
<p><code><br />
<strong>function</strong> buscar() <strong>{</strong><br />
&nbsp;&nbsp;<strong>var</strong> form = document.forms['formulario'];<br />
&nbsp;&nbsp;<strong>var</strong> resultado = document.getElementById("resultado");<br />
&nbsp;&nbsp;<strong>var</strong> contenttype = 'application/x-www-form-urlencoded';</p>
<p>&nbsp;&nbsp;<strong>var</strong> req;<br />
&nbsp;&nbsp;<strong>if</strong> (window.XMLHttpRequest) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;req = <strong>new</strong> XMLHttpRequest();<br />
&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;req = <strong>new</strong> ActiveXObject("Microsoft.XMLHTTP");<br />
&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;req.open('POST', 'buscar.php', true);<br />
&nbsp;&nbsp;req.setRequestHeader('Content-Type', contenttype);<br />
&nbsp;&nbsp;req.onreadystatechange =<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>function</strong>() <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> (req.readyState == 4)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultado.innerHTML = req.responseText;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;req.send('apellido=' + escape(form.apellido.value));<br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p>La función <strong>buscar()</strong> recupera los objetos <strong>formulario</strong> (el formulario) y <strong>resultado</strong> (el <em>div</em> inicialmente vacío). Luego crea el  objeto <strong>req</strong>, de clase <strong>HMLHttpRequest</strong> (o un controlador <em>ActiveX</em> del tipo <strong>Microsoft.XMLHTTP</strong> en caso del siempre incompatible <em>Internet Explorer</em>).</p>
<p>Los objetos de clase <strong>XMLHttpRequest</strong> permiten realizar requerimientos al servidor sin necesidad de tener que &#8220;<em>recargar</em>&#8221; toda la página. Una vez creado, se configura a <strong>req</strong> para llamar a <strong>buscar.php</strong> usando el método <strong>POST</strong>, con el <em>Content-Type</em> correspondiente al envío de un formulario (<em>application/x-www-form-urlencoded</em>). El valor &#8220;<em>true</em>&#8221; significa que se trata de un requerimiento asincrónico, permitiendo que continúe la ejecución del <em>script</em> aunque el mismo no haya finalizado (este es el &#8220;<em>truco</em>&#8221; que logra la sensación de interactividad).</p>
<p>Luego, se configura el objeto <strong>req</strong> mediante el método &#8220;<em>onreadystatechange</em>&#8221; para que al finalizar la ejecución del requerimiento se modifique el contenido del <em>div</em> <strong>resultado</strong>, asignándole el texto obtenido como respuesta del servidor (<em>responseText</em>). Esto se logra asociándole una función sin nombre, lo que usualmente se denomina &#8220;<em>callback</em>&#8220;.</p>
<p>Finalmente, se envía el requerimiento, asignando a la variable <em>CGI</em> <strong>apellido</strong> el valor del campo <strong>apellido</strong> del formulario.</p>
<p>Probemos a continuación la ejecución de este ejemplo:</p>
<p>  <script type="text/javascript" src="/files/ajax/buscar.js"></script></p>
<form name="formulario" onsubmit="return false">
 <strong>Apellido:</strong><br />
<input type="text" name="apellido" autocomplete="off">
<input type="button" class="formbutton" value="Buscar" onclick="JavaScript:buscar()">
  </form>
<div id="resultado"></div>
<p>&nbsp;</p>
<p>El resultado es claro: la función <em>JavaScript</em> asociada al evento <em>onclick</em> del botón <em>Buscar</em> realiza la llamada a <strong>buscar.php</strong>, enviándole el valor de la variable <strong>apellido</strong>, y escribiendo en el <em>div</em> <strong>resultado</strong> la salida producida.</p>
<p>Podemos ir un poco más allá, asociando la función <strong>buscar()</strong> al evento <em>onkeyup</em> del campo de texto <strong>apellido</strong>. Este evento ocurre cada vez que se &#8220;levanta&#8221; una tecla (o sea, cuando se ha ingresado un nuevo caracter). De esta manera obtendremos un comportamiento más &#8220;<em>interactivo</em>&#8220;.</p>
<p>El código de <strong>buscar.html</strong> quedaría cómo sigue:</p>
<h4>buscar2.html</h4>
<blockquote>
<p><code><br />
<strong>&lt;html&gt;</strong><br />
<strong>&lt;head&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;title&gt;</strong>Personas<strong>&lt;/title&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;script</strong> type="text/javascript" src="buscar.js"<strong>&gt;&lt;/script&gt;</strong><br />
<strong>&lt;/head&gt;</strong><br />
<strong>&lt;body&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;form</strong> name="formulario" onsubmit="return false"<strong>&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;input</strong> type="text" name="apellido" onkeyup="JavaScript:buscar()" autocomplete="off"<strong>&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;/form&gt;</strong><br />
&nbsp;&nbsp;<strong>&lt;div</strong> id="resultado"<strong>&gt;&lt;/div&gt;</strong><br />
<strong>&lt;/body&gt;</strong><br />
<strong>&lt;/html&gt;</strong><br />
</code></p>
</blockquote>
<p>Y el resultado sería el siguiente:</p>
<p>  <script type="text/javascript" src="/files/ajax/buscar2.js"></script></p>
<form name="formulario2" onsubmit="return false">
  <strong>Apellido:</strong><br />
<input type="text" name="apellido" onkeyup="JavaScript:buscar2()" autocomplete="off">
  </form>
<div id="resultado2"></div>
<p>&nbsp;</p>
<p>Si lo desea, puede <a href="http://blog.smaldone.com.ar/files/ajax/ejemplo_ajax.tgz">descargar el ejemplo completo</a> (también <a href="http://blog.smaldone.com.ar/files/ajax/ejemplo_ajax.zip">en formato <em>ZIP</em></a>).</p>
<h3>Conclusión</h3>
<p>Esto que hemos visto es <strong>AJAX</strong>. Si bien hemos utilizado <em>JavaScript</em> para realizar el requerimiento &#8220;asincrónico&#8221;, podríamos haber usado cualquier tecnología &#8220;<em>client-side</em>&#8221; (<em>VBScript</em>, <em>JScript</em>, etc.). Tampoco hemos usado <em>XML</em> para recibir el resultado desde el servidor (nuestro <em>script</em> <strong>buscar.php</strong> produce fragmentos de código <em>HTML</em>).</p>
<p>Un elemento muy importante (y que no forma parte de la sigla <strong>AJAX</strong>) es el <a href="http://es.wikipedia.org/wiki/Document_Object_Model"><strong>DOM</strong></a> (&#8220;<em>Document Object Model</em>&#8220;). Se trata de una <em>API</em> que nos permite modificar dinámicamente un documento HTML. Hemos usado esto en nuestro ejemplo, para modificar el contenido del <em>div</em> <strong>resultado</strong>.</p>
<p><strong>AJAX</strong> abre todo un mundo de posibilidades a la hora de construir aplicaciones web altamente interactivas pero, a pesar de toda la palabrería que se construye a su alrededor no es mucho más de lo que hemos podido apreciar en el simple ejemplo analizado.</p>
<p><strong>Una aclaración importante:</strong> como podrá apreciarse, realizar una aplicación compleja que maneje una gran cantidad de elementos (botones, <em>divs</em>, opciones, etc.) y una gran cantidad de eventos, sería extremadamente complejo si lo hiciéramos como en nuestro ejemplo. Es por esto que para la utilización de todo el potencial de <strong>AJAX</strong> en una aplicación, deberá utilizarse alguna biblioteca destinada a tal fin, que genere las funciones <em>JavaScript</em> necesarias para el control de los eventos y la realización de los requerimientos asincrónicos. Existe una gran cantidad de herramientas de este tipo, disponibles para distintos lenguajes de programación.</p>
<p>Lecturas recomendadas en <em>Wikipedia</em>:</p>
<ul>
<li><a href="http://es.wikipedia.org/wiki/AJAX">AJAX</a></li>
</li>
<li><a href="http://es.wikipedia.org/wiki/XMLHttpRequest">XMLHttpRequest</a></li>
<li><a href="http://es.wikipedia.org/wiki/Document_Object_Model">Document Object Model (DOM)</a></li>
</ul>
<p>Finalmente, <a href="http://ashko.blogspot.com/2006/11/best-top-ten-open-sourceajaxdhtml.html">un listado de 10 bibliotecas</a> para desarrollar aplicaciones usando <strong>AJAX</strong>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/12/11/ajax-no-es-ajax/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>No tener ni idea</title>
		<link>http://blog.smaldone.com.ar/2006/12/10/no-tener-ni-idea/</link>
		<comments>http://blog.smaldone.com.ar/2006/12/10/no-tener-ni-idea/#comments</comments>
		<pubDate>Mon, 11 Dec 2006 01:08:54 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Educación]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/12/10/no-tener-ni-idea/</guid>
		<description><![CDATA[Hace ya un tiempo algún alumno de la Universidad Tecnológica Nacional (Fac. Buenos Aires) elaboró un video basado en la grabación de una clase, en la cual el docente a cargo de la cátedra de &#8220;Diseño de sistemas&#8220;, el Lic. Rubén Fernandez Iriart, explicaba qué son los &#8220;Enterprise Java Beans&#8220;. A continuación, la transcripción de [...]]]></description>
			<content:encoded><![CDATA[<p>Hace ya un tiempo algún alumno de la <a href="http://www.frba.utn.edu.ar/">Universidad Tecnológica Nacional (Fac. Buenos Aires)</a> elaboró un video basado en la grabación de una clase,  en la cual el docente a cargo de la cátedra de &#8220;<em>Diseño de sistemas</em>&#8220;, el <strong>Lic. Rubén Fernandez Iriart</strong>, explicaba qué son los &#8220;<a href="http://es.wikipedia.org/wiki/Enterprise_JavaBeans"><em>Enterprise Java Beans</em></a>&#8220;.</p>
<div class="centerpic">
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/vFlTYRleZpU"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/vFlTYRleZpU" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>
</div>
<p><span id="more-91"></span></p>
<p>A continuación, la transcripción de la grabación (he quitado varias interjecciones y muletillas):</p>
<blockquote>
<p><em><strong>Alumno:</strong> ¿Qué es EJB?</em></p>
<p><em><strong>Docente:</strong> ¿EJB? Enterprise Java Beans, Enterprise Java Beans es el&#8230; la Enterprise Java Beans son&#8230;  dijéramos, los Beans, los Beans Java de las Empresas (risas). Pero&#8230; ¿qué es un Bean? Bean es un poroto (risas). No sirven para nada. Nos vuelven locos con estas porquerías (risas). </em></p>
<p><em>En realidad una Java Bean es una clase, que tiene métodos automatizados que lo definen. Hay métodos que&#8230; hay métodos&#8230; hay una clase que, que hace una clase, entonces hace de todo, van y le meten un método, un, un, un Java Bean Method. Entonces ¿un Java Bean Method qué es? Es un método, ustedes le ponen el nombre, les arma una serie de ventanas y les va a terminar agregando un método que ustedes podrían crear un método. Pero, dijéramos que toda la infraestructura les va a crear un método. Ese método, en realidad, es, podría ser un método para recuperar, o podría ser un método para escribir, o podría ser un método para grabar. O podría ser cualquiera de los otros métodos que los construirían ustedes. En general los que están construidos son los Enterprise Java Beans Service, que son métodos que uno directamente apretando botón derecho pin, pun, pun, pun, se van generando.</em></p>
<p><em>Beans es, es, es, es, dijéramos&#8230; y ustedes ven que hay&#8230; que hay Beans en todos lados, ¿no? Porque hay Beans en la parte de presentación, hay Beans en la parte de negocio y&#8230; y puede haber Beans en la parte de bases de datos también. Porque en realidad lo que son Beans son conceptos de clases, clases para ellos son Beans.</em></p>
<p><em><strong>Alumno:</strong> ¿Clases específicas, Profesor?</em></p>
<p><em><strong>Docente:</strong> Si, pero&#8230; es como que son objetos. Son específicas porque son clases que, te vuelvo a decir, que leen, o que graban, o que recuperan, o que&#8230; y entonces, en realidad, están propuestas con una serie de accesos rápidos. Fíjense que ustedes están todo dentro de un server, y ese server, o sea, eso se pone&#8230; se arman paquetes&#8230; tecnológicos&#8230; tecnológicos portes de&#8230; en donde están&#8230; interrelacionados con las otras capas, y las interrelaciones siempre las hacen a través de esos componentes J2EE. Los componentes, o sea, los software de  las clases implementadas son componentes, o sea, por eso, a ver: componente, componente, componente, siempre componentes de las tres capas. Entonces ¿los componentes qué son? Software. Pero el software ese ¿dónde fue pensado? Fue pensado en las clases. Pero ahora son componentes, o sea que son software. Yo voy armando software y cuando, digo, se genera hay una serie de métodos que se generan automaticamente, no necesariamente en el J2EE, sino también en Java, pero por ejemplo que son los métodos setter and getter. Los métodos setter and getter es como que son métodos que ya están definidos, que uno directamente lo único que hace es cambiarle la variable que usa.</em></p>
<p><em>También pueden hacer el proyecto con Enterprise Java Beans, con elementos de J2EE. Algunas partes del sistema pueden hacer. Si estás e una relación cliente/servidor. O sea, miren toda la tengología que podríamos llegar a tener y aplicar en un ejercicio. En líneas generales si quieren aplicar todo, está a disposición de ustedes, el software lo permite, pero van a tener que sentar el culo y trabajar mucho, lo cual dudo que estén predispuestos a hacerlo. Aunque eso les reditúe mucho.</em></p>
</blockquote>
<p>Como puede apreciarse, el <strong>Lic. Rubén Fernandez Iriart</strong> no tiene la más remota sospecha sobre tema que está tratando (y ni hablar de su uso del idioma castellano). Peor que eso, en vez de disculparse y comprometerse a responder en una clase posterior (como haría cualquier persona con un poco de vergüenza), encara hilvanando un sinfin de frases sin sentido, encadenando las palabras &#8220;<em>clase</em>&#8220;, &#8220;<em>objeto</em>&#8220;, &#8220;<em>componente</em>&#8220;, &#8220;<em>mensaje</em>&#8220;, &#8220;<em>capa</em>&#8220;, &#8220;<em>servicio</em>&#8220;, &#8220;<em>paquete</em>&#8220;, &#8220;<em>software</em>&#8220;, etc. Para terminar, quizás tratando de justificar su propia ignorancia y no quedar tan en evidencia, finaliza la explicación desmereciendo y rebajando a sus alumnos, dando a entender que si no logran comprender, es por su falta de disposición (y no por la ignorancia de quien habla).</p>
<p>No clasifico este artículo en la categoría &#8220;<em>humor</em>&#8221; porque yo también me encontré con este tipo de docentes (casualmente en la <strong>UTN Fac. Córdoba</strong>) y a punto estuve de dejar la informática por ello. Quizás el daño más grande fue que, al ver la ignorancia de este tipo de &#8220;<em>profesores</em>&#8220;, sobrevalué mis conocimientos. Me llevó años darme cuenta de que, aunque sabía mucho más que varios de ellos, en realidad sabía poco y nada. Claro que este tipo de &#8220;<em>docente</em>&#8221; no es patrimonio exclusivo, ni mucho menos, de la <strong>UTN</strong> (me los he encontrado en varias universidades, con el atenuante de que al menos en algunas de ellas no son mayoría).</p>
<p>Pero la cosa no termina ahí. El <strong>Lic. Fernandez Iriart</strong>, que parece haberse <a href="http://commons.wikimedia.org/wiki/Rub%C3%A9n_Fern%C3%A1ndez_Iriart">ganado la antipatía</a> de muchos de sus alumnos (y escuchando su clase se entiende el por qué), es el <a href="http://www.cpci.org.ar/autoridades.html"><strong>Presidente</strong></a> <strong>del</strong> <a href="http://www.cpci.org.ar/"><strong>Consejo Profesional de Ciencias Informáticas</strong></a> de la ciudad de Buenos Aires (¡con el &#8220;<em>honor</em>&#8221; de tener la primera matrícula, número <strong>0001</strong>!).</p>
<p>Desde ese Consejo Profesional, que supuestamente &#8220;<em>avala la idoneidad de los profesionales de las ciencias informáticas</em>&#8220;, este señor se dedica <a href="http://www.cpci.org.ar/newsletters/ee2/objetos.html">a dictar cursos</a> sobre análisis y diseño orientados a objetos, <a href="http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/"><strong>UML</strong></a>, arquitecturas basadas en &#8220;<em>componentes</em>&#8220;, etc.</p>
<p>Si este es el tipo de personas que se encarga de representar ante la comunidad a los profesionales de la computación, creo que sumo un punto más <a href="http://blog.smaldone.com.ar/2006/08/24/contra-la-matriculacion-obligatoria/">en contra de la matriculación obligatoria</a>.</p>
<p>Ver a personas así, arruinando la educación (y quizás la cabeza) de sus alumnos, cobrando un sueldo del estado y, a la vez, representando a los profesionales informáticos, da mucha pena, bronca y, sobre todo, mucha vergüenza&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/12/10/no-tener-ni-idea/feed/</wfw:commentRss>
		<slash:comments>61</slash:comments>
		</item>
		<item>
		<title>Tutorial sobre TCP/IP</title>
		<link>http://blog.smaldone.com.ar/2006/11/21/tutorial-sobre-tcpip/</link>
		<comments>http://blog.smaldone.com.ar/2006/11/21/tutorial-sobre-tcpip/#comments</comments>
		<pubDate>Tue, 21 Nov 2006 08:00:29 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Redes]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/11/21/tutorial-sobre-tcpip/</guid>
		<description><![CDATA[En este tutorial realizaremos la &#8220;disección&#8221; de una comunicación TCP/IP muy simple, con el fin de analizar qué ocurre a cada nivel. A través de este sencillo experimento, podremos recorrer los conceptos fundamentales de las redes TCP/IP, para reafirmar la idea de que este protocolo es muy simple. El objetivo es lograr, sin demasiados conocimientos [...]]]></description>
			<content:encoded><![CDATA[<p>En este tutorial realizaremos la &#8220;<em>disección</em>&#8221; de una comunicación <strong>TCP/IP</strong> muy simple, con el fin de analizar qué ocurre a cada nivel.</p>
<p>A través de este sencillo experimento, podremos recorrer los conceptos fundamentales de las redes <strong>TCP/IP</strong>, para reafirmar la idea de que este protocolo <a href="http://blog.smaldone.com.ar/2006/11/02/tcpip-es-simple/">es muy simple</a>. El objetivo es lograr, sin demasiados conocimientos previos, una comprensión profunda de sus mecanismos.</p>
<p><span id="more-69"></span></p>
<p><em>También puede <a href="http://blog.smaldone.com.ar/files/tcpip/tutorial_tcpip.tgz">descargar este tutorial en otros formatos</a>  (HTML sin decoraciones y PDF).</em></p>
<h4>Requisitos previos</h4>
<p>No se requieren conocimientos de programación, aunque sí una base de conocimientos informáticos en general. Para continuar experimentando más allá de los expuesto aquí, se recomienda la utilización del programa <a href="http://www.wireshark.org/"><strong>Wireshark</strong></a> (antes conocido como <strong>Ethereal</strong>), disponible para <strong>GNU/Linux</strong>, <strong>Microsoft Windows</strong>, <strong>Mac OS/X</strong> y <strong>Solaris</strong> .</p>
<p>Quizás el requisito más importante sean las ganas de aprender, investigar, jugar y divertirse con redes <strong>TCP/IP</strong>.</p>
<h4>Referencias</h4>
<p>Cada vez que se introduzca un término relevante, el mismo contendrá un enlace a una página con mayor información, por lo general de la <a href="http://es.wikipedia.org/"><strong>Wikipedia</strong></a> y, de ser posible, en español (aunque se recomienda ampliamente visitar su equivalente en inglés).</p>
<p>Un libro muy recomendable y claro (en inglés) es &#8220;<a href="http://www.kohala.com/start/tcpipiv1.html"><em>TCP/IP Illustrated Volume 1</em></a>&#8221; de W. Richard Stevens.  Finalmente, la fuente de consulta definitiva, para conocer tanto los aspectos técnicos como la evolución historica de cada uno de los protocolos y normas, son los <a href="http://www.rfc-editor.org/rfc.html"><strong>Request for Comments</strong></a> (<strong>RFC</strong>), algunos de los cuales han sido <a href="http://www.rfc-es.org/">traducidos al español</a>.</p>
<h3>Nuestro experimento</h3>
<p>Estableceremos una conexión <a href="http://es.wikipedia.org/wiki/TCP/IP"><strong>TCP/IP</strong></a> entre un cliente y un servidor que intercambiarán información. Para ello, utilizaremos el servidor desarrollado en el <a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">tutorial sobre programación en redes</a> (no es necesario que lo lea completamente, si no le interesa la programación, pero sería conveniente que revise los conceptos introductorios y el protocolo definido).</p>
<p>Ejecutamos entonces el servidor en el <a href="http://es.wikipedia.org/wiki/Host"><em>host</em></a> <strong>100.0.0.1</strong>, utilizando el puerto <strong>2222</strong> (cualquiera de las versiones desarrolladas en el tutorial anterior sirve, ya que el protocolo es idéntico) y usamos el comando <strong>telnet</strong> para actuar como cliente desde el <em>host</em> <strong>200.0.0.1</strong> (el texto en <em>cursiva</em> es introducido por nosotros y el texto en <strong>negrita</strong> es la respuesta del servidor):</p>
<p><code>javier@200.0.0.1:~$ <em>telnet 100.0.0.1 2222</em><br />
Trying 100.0.0.1...<br />
Connected to 100.0.0.1.<br />
Escape character is '^]'.<br />
<strong>Bienvenido.</strong><br />
<em>salir</em><br />
<strong>Adios.</strong><br />
Connection closed by foreign host.<br />
javier@200.0.0.1:~$</code></p>
<p>Esto es todo. Ahora procederemos a analizar cómo se ha llevado a cabo esta comunicación  a distintos niveles de abstracción.</p>
<h3>Nivel de aplicación</h3>
<p>A &#8220;<a href="http://es.wikipedia.org/wiki/Nivel_de_aplicaci%C3%B3n"><em>nivel de aplicación</em></a>&#8221; (lo que &#8220;<em>ven</em>&#8221; los programas), la comunicación se ha desarrollado de la siguiente manera:</p>
<ol>
<li>El cliente se conecta al servidor.</li>
<li>El servidor envía el mensaje &#8220;<strong>Bienvenido.</strong>&#8220;.</li>
<li>El cliente envía el comando &#8220;<strong>salir</strong>&#8220;.</li>
<li>El servidor responde con el mensaje &#8220;<strong>Adios.</strong>&#8220;.</li>
<li>El servidor cierra la conexión.</li>
<li>El cliente cierra la conexión.</li>
</ol>
<p>Esto es prácticamente lo mismo que podemos observar de la salida del comando <strong>telnet</strong> utilizado, lo cual no es casual; intencionalmente a este nivel se ocultan todos los detalles de implementación, que aparecerán cuando analicemos los niveles inferiores.</p>
<h3>Nivel de transporte</h3>
<p>El protocolo utilizado a &#8220;<a href="http://es.wikipedia.org/wiki/Nivel_de_transporte"><em>nivel de transporte</em></a>&#8221; es <strong>TCP</strong>. Este protocolo es el encargado de establecer la conexión y dividir la información en <em>paquetes</em>, garantizando que los mismos son entregados correctamente (sin pérdidas y en el orden apropiado).</p>
<p>Cabe resaltar aquí que el otro protocolo de transporte de <strong>TCP/IP</strong>, <a href="http://es.wikipedia.org/wiki/User_Datagram_Protocol"><strong>UDP</strong></a> no garantiza ni el arribo de todos los paquetes enviados, ni el orden en que estos llegan a destino. Por esto es mucho más simple, no incluyendo algunas de las características de <strong>TCP</strong> como números de secuencia y asentimientos.</p>
<p>A continuación analizaremos algunos aspectos del protocolo <a href="http://es.wikipedia.org/wiki/Transmission_Control_Protocol"><strong>TCP</strong></a>.</p>
<h4>Puertos y direcciones</h4>
<p>El protocolo <strong>TCP</strong> se basa en <a href="http://es.wikipedia.org/wiki/Direcci%C3%B3n_IP"><strong>direcciones IP</strong></a> para identificar los equipos (<em>hosts</em>) desde donde provienen y hacia donde se envían los paquetes.</p>
<p>Los <a href="http://es.wikipedia.org/wiki/TCP#Puertos_TCP"><strong>puertos</strong></a> (<em>ports</em>) son valores numéricos (entre 0 y 65535) que se utilizan para identificar a los procesos que se están comunicando. En cada extremo, cada proceso interviniente en la comunicación utiliza un puerto único para enviar y recibir datos.</p>
<p>En conjunción, dos pares de puertos y direcciones IP identifican univocamente a dos procesos en una red <strong>TCP/IP</strong>.</p>
<h4>Números de secuencia</h4>
<p><strong>TCP</strong> garantiza que la información es recibida en orden. Para ello, cada paquete enviado tiene un <em>número de secuencia</em>. Cada uno de los dos procesos involucrados mantiene su propia secuencia, que se inicia con un valor aleatorio y luego va incrementándose según la cantidad de bytes enviados.</p>
<p>Por ejemplo, si un paquete tiene número de secuencia <em>x</em> y contiene <em>k</em> bytes de datos, el número de secuencia del siguiente paquete emitido será <em>x</em> + <em>k</em>. (<em>Sí, el número de secuencia va contando la cantidad de bytes enviados por cada host</em>.)</p>
<h4>Paquetes y acuses de recibo</h4>
<p><strong>TCP</strong> también asegura que toda la información emitida es recibida. Para ello, por cada paquete emitido, debe recibirse un <a href="http://es.wikipedia.org/wiki/ACK">asentimiento</a> (en inglés &#8220;<em>acknowledgement</em>&#8220;, abreviado <strong>ACK</strong>). Si pasado determinado tiempo no se recibe el <strong>ACK</strong> correspondiente, la información será retransmitida.</p>
<p>El <strong>ACK</strong> hace referencia al número de secuencia (que ha su vez involucra la cantidad de bytes enviados). Por ejemplo, para comunicar que se ha recibido correctamente el paquete cuyo número de secuencia es <em>x</em>, que contiene <em>k</em> bytes, se enviará un <strong>ACK</strong> con el valor <em>x</em> + <em>k</em> (que coincide con el próximo número de secuencia a utilizar por parte del emisor del paquete en cuestión). Si el número de secuencia inicial es <em>x</em>, un valor de <strong>ACK</strong> <em>t</em> significa que el receptor ha recibido correctamente los primeros <em>t</em>-<em>x</em> bytes (en este sentido, el <strong>ACK</strong> es acumulativo).  </p>
<p>El <strong>ACK</strong> no es un paquete especial, sino un campo dentro de un paquete <strong>TCP</strong> normal. Por esto, puede ocurrir que se envíe un paquete a solo efecto de asentir una determinada cantidad de bytes, o como parte de un paquete de otro tipo (por ejemplo, aprovechando el envío de nuevos datos, para comunicar la recepción de datos anteriores). De hecho, aunque ya se haya enviado un paquete exclusivamente de <strong>ACK</strong> con un valor <em>t</em>, si luego se envía un paquete de datos, puede repetirse en él el <strong>ACK</strong> con el mismo valor <em>t</em>, sin que esto confunda al emisor de los datos que se están asintiendo. (Por simplicidad, en nuestro ejemplo hemos eliminado esta información redundante).</p>
<h4>Otros campos de un paquete TCP</h4>
<p>El protocolo <strong>TCP</strong> incorpora mecanismos tales como control de integridad de los datos (<em>checksum</em>), prevención de congestiones, entre otros, que no serán mencionados aquí por la simplicidad de este tutorial.</p>
<h4>Inicio y fin de la conexión</h4>
<p>Para dar comienzo a la conexión, el cliente envía un paquete <a href="http://es.wikipedia.org/wiki/SYN"><strong>SYN</strong></a> al puerto e <strong>IP</strong> en donde &#8220;<em>escucha</em>&#8221; el servidor, con un número de secuencia inicial aleatorio. Este último, responde con otro paquete <strong>SYN</strong>, con un número de secuencia inicial aleatorio y un <strong>ACK</strong> con el número de secuencia del paquete <strong>SYN</strong> recibido, más uno. El cliente  envía un paquete con el <strong>ACK</strong> del <strong>SYN</strong> recibido, y una vez hecho esto la conexión se encuentra establecida y puede darse comienzo a la transmisión de datos (iniciada por cualquiera de las partes, según el protocolo de aplicación que utilicen).</p>
<p>La razón por la cual se intercambian números de secuencia aleatorios es para evitar que se confunda el inicio de dos conexiones diferentes y algunos ataques que se basan en falsear el comienzo de una conexión (<a href="http://es.wikipedia.org/wiki/Spoofing"><em>spoofing</em></a>).</p>
<p>La siguiente figura ilustra el establecimiento de una conexión <strong>TCP</strong>, llamada &#8220;<em>negociación de tres pasos</em>&#8221; o &#8220;<em>3-way handshake</em>&#8220;:</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/tcpip/inicio.png" alt="Inicio de una conexión TCP" /></div>
<p>Para finalizar la conexión, uno de los dos procesos envía un paquete <strong>FIN</strong>, a lo que el otro responderá con un <strong>ACK</strong>. A su vez, el otro proceso puede enviar un paquete <strong>FIN</strong> (recibiendo también un <strong>ACK</strong>) y la conexión quedará cerrada definitivamente.</p>
<p>Nótese que el segundo proceso puede no enviar el paquete <strong>FIN</strong>. Esto significa que ese extremo de la conexión no se cerrará, pudiendo aún enviar datos a través de la misma. De lo contrario, en caso de desear terminar la conexión, puede combinar el <strong>ACK</strong> y el <strong>FIN</strong> en un solo paquete (esta es la situación más común).</p>
<p>La siguiente figura ilustra la forma general de terminación de una conexión <strong>TCP</strong>:</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/tcpip/fin.png" alt="Fin de una conexión TCP" /></div>
<h4>Análisis a nivel de transporte</h4>
<p>Habiendo revisado los conceptos más relevantes, analizaremos ahora la conexión realizada desde el punto de vista del <a href="http://es.wikipedia.org/wiki/TCP">protocolo <strong>TCP</strong></a>. Supondremos que el puerto utilizado por el cliente es <strong>4683</strong> (el del servidor, recordemos, es <strong>2222</strong>).</p>
<p>La siguiente figura muestra una visión simplificada de esta conexión:</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/tcpip/sesion.png" alt="Sesión TCP" /></div>
<p><a href="http://blog.smaldone.com.ar/files/tcpip/sesion_big.png">(Ver imagen ampliada)</a></p>
<p>Veamos qué función cumple cada paquete:</p>
<ol>
<li>El cliente envía un <strong>SYN</strong> al servidor, con número de secuencia <em>x<sub>0</sub></em>.</li>
<li>El servidor responde con un paquete <strong>SYN</strong>, con número de secuencia <em>y<sub>0</sub></em> y un <strong>ACK</strong> con <em>x<sub>0</sub>+1</em> (en adelante, <em>x<sub>1</sub></em>).</li>
<li>El cliente envía un paquete <strong>ACK</strong> del <strong>SYN</strong> que acaba de recibir, con valor <em>y<sub>0</sub>+1</em> (en adelante, <em>y<sub>1</sub></em>). (A partir de este momento la conexión se encuentra establecida y puede comenzar el intercambio de datos entre las aplicaciones.)</li>
<li>El servidor envía un paquete <strong>PSH</strong> (&#8220;<em>push</em>&#8220;) conteniendo la cadena &#8220;<strong>Bienvenido.\n</strong>&#8221; (12 bytes), con número se secuencia <em>y<sub>1</sub></em>. (El caracter &#8220;<strong>\n</strong>&#8220;, <em>newline</em>, representa el fin de línea.)</li>
<li>El cliente envía un <strong>ACK</strong> por la correcta recepción del paquete anterior. El número de <strong>ACK</strong> es <em>y<sub>1</sub>+12</em> (que llamaremos <em>y<sub>2</sub></em> y será el próximo número de secuencia utilizado por el servidor).</li>
<li>El cliente envía la cadena &#8220;<strong>salir\r\n</strong>&#8220;, con número de secuencia <em>x<sub>1</sub></em>. (Los caracteres &#8220;<strong>\r\n</strong>&#8221; representan el fin de línea. Ver nota al final de la sección.)</li>
<li>El servidor envía el <strong>ACK</strong> correspondiente, con el valor <em>x<sub>1</sub></em> más la longitud de &#8220;<strong>salir\r\n</strong>&#8221; (<em>7</em>), que llamaremos <em>x<sub>3</sub></em>.</li>
<li>El servidor envía la cadena &#8220;<strong>Adios.\n</strong>&#8221; (7 bytes), con número de secuencia <em>y<sub>2</sub></em>.</li>
<li>El cliente envía el <strong>ACK</strong> con el valor <em>y<sub>2</sub>+7</em> (en adelante, <em>y<sub>3</sub></em>).</li>
<li>El servidor cierra su lado de la conexión enviando un paquete <strong>FIN</strong>, con secuencia <em>y<sub>3</sub></em>.</li>
<li>El cliente, que también cierra su conexión, envía un paquete <strong>FIN</strong> con secuencia <em>x<sub>3</sub></em>, con el <strong>ACK</strong> del paquete anterior (<em>y<sub>3</sub>+1</em>).</li>
<li>El servidor envía el <strong>ACK</strong> del anterior paquete <strong>FIN</strong> (con valor <em>x<sub>3</sub>+1</em>), con lo cual la conexión finaliza.</li>
</ol>
<p><em><strong>Nota:</strong> En este ejemplo podemos ver un error en el diseño del protocolo de aplicación, ya que el servidor representa los fines de línea con el caracter &#8220;<strong>\n</strong>&#8221; (&#8220;newline&#8221;, código ASCII 10), en tanto que el cliente está usando los caracteres &#8220;<strong>\r\n</strong>&#8221; (&#8220;newline&#8221; y &#8220;carriage return&#8221;, códigos ASCII 10 y 13, respectivamente). Esta última es la forma de representación más utilizada en aplicaciones <strong>TCP/IP</strong>.</em></p>
<h3>Nivel de red</h3>
<p>Antes de realizar el análisis a &#8220;<a href="http://es.wikipedia.org/wiki/Nivel_de_red"><em>nivel de red</em></a>&#8221; (<a href="http://es.wikipedia.org/wiki/Protocolo_de_Internet">protocolo <strong>IP</strong></a>) vamos a suponer que tenemos la siguiente topología:</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/tcpip/red_ip.png" alt="Red IP" /></div>
<p>El cliente se ejecuta en el <em>host</em> cuya dirección IP es <strong>200.0.0.1</strong>. El mismo está conectado a la red local <strong>200.0.0.0/24</strong> y su <a href="http://es.wikipedia.org/wiki/Router"><em>gateway</em></a> (&#8220;<em>router</em>&#8220;, &#8220;<em>enrutador</em>&#8221; o &#8220;<em>puerta de enlace</em>&#8220;) es el <em>host</em> <strong>200.0.0.21</strong>.</p>
<p>La dirección de red local está compuesta por la <em>dirección de red</em> propiamente dicha, <strong>200.0.0.0</strong>, y la <a href="http://es.wikipedia.org/wiki/M%C3%A1scara_de_red"><em>máscara de red</em></a>, <strong>24</strong> (que también puede ser representada como <strong>255.255.255.0</strong>). Esto significa que los primeros <strong>24</strong> bits de cualquier dirección serán interpretados como identificador de la red, en tanto que los últimos <strong>8</strong> identificarán a cada <em>host</em> (recordemos que, aunque la notación usual es escribir las direcciones IP como cuatro números decimales separados por puntos, en realidad se componen de 32 bits).</p>
<p>Por ejemplo, en el contexto de esta red, la dirección <strong>200.0.0.45</strong> será considerada una dirección local (por coincidir los primeros <strong>24</strong> bits con los de la dirección de red). Esto significa que cualquier paquete destinado a dicha dirección IP, será entregado &#8220;<em>localmente</em>&#8221; (o sea, directamente a través del <em>protocolo de enlace</em>, como veremos más adelante).</p>
<p>En el caso de una dirección de destino &#8220;no-local&#8221;, los paquetes serán entregados al <em>gateway</em> <strong>200.0.0.21</strong> (usando el <em>protocolo de enlace</em>), quien será luego el encargado de entregar el paquete al <em>host</em> de destino (si este perteneciera a alguna red local a la que dicho <em>gateway</em> esté conectado), o reenviarlo a través de otro <em>gateway</em> (en caso contrario).</p>
<p>De la misma manera el servidor, cuya <strong>dirección IP</strong> es <strong>100.0.0.1</strong>, pertenece a la red <strong>100.0.0.0/24</strong>, cuyo <em>gateway</em> es <strong>100.0.0.35</strong>.</p>
<h4>Análisis a nivel de red</h4>
<p>A nivel IP no hay demasiado que agregar. Aquí se realiza el &#8220;<em>ruteo</em>&#8221; (o &#8220;<em>encaminamiento</em>&#8220;) de los paquetes provenientes de la capa de transporte (que en este nivel se denominan &#8220;<em>datagramas</em>&#8220;) desde la dirección IP de origen hasta la de destino (alternando estos roles <strong>100.0.0.1</strong> y <strong>200.0.0.1</strong> según sea el emisor el servidor o el cliente, respectivamente).</p>
<p>Un campo interesante que se añade es el <a href="http://es.wikipedia.org/wiki/Tiempo_de_Vida"><strong>TTL</strong></a> (&#8220;<em>tiempo de vida</em>&#8221; o &#8220;<em>time to live</em>&#8220;), que tiene un valor inicial en el <em>host</em> que produce el paquete (generalmente <strong>64</strong>) y luego es decrementado por cada <em>gateway</em> por el que pasa. Si un <em>gateway</em> recibe un paquete con el campo <strong>TTL</strong> en <strong>cero</strong>, el mismo es descartado y se genera un paquete del protocolo <a href="http://es.wikipedia.org/wiki/Internet_Control_Message_Protocol"><strong>ICMP</strong></a> (usado para control y mensajes de error) dirigido a su emisor, indicando que dicho paquete ha excedido la cantidad máxima de saltos. Esta medida es implementada para evitar que, quizás por un error de ruteo, un paquete quede indefinidamente &#8220;dando vueltas&#8221; por la red.</p>
<p>En este ejemplo supondremos que el enrutamiento de paquetes es simple (estático), para facilitar las explicaciones. Existen casos complejos en donde se utiliza enrutamiento dinámico, en los cuales paquetes pertenecientes a la misma comunicación pueden enviarse por caminos distintos, alterando inclusive el orden en que llegan al destino (y hasta pudiendo producirse pérdidas).</p>
<p>Debemos tener en cuenta que estamos usando el llamado <a href="http://es.wikipedia.org/wiki/IPv4"><strong>IPv4</strong></a> (IP versión 4), ya que también existe el <a href="http://es.wikipedia.org/wiki/IPv6"><strong>IPv6</strong></a> (IP versión 6), cuya aplicación se está difundiendo rápidamente en Internet y que soluciona muchos inconvenientes que presenta el anterior.</p>
<h3>Nivel de enlace</h3>
<p>El &#8220;<a href="http://es.wikipedia.org/wiki/Nivel_de_enlace_de_datos"><em>nivel de enlace</em></a>&#8221; es el nivel más cercano al &#8220;<a href="http://es.wikipedia.org/wiki/Nivel_f%C3%ADsico"><em>nivel físico</em></a>&#8221; y es totalmente independiente del protocolo <strong>TCP/IP</strong>. Existen gran variedad de tecnologías de este tipo; siendo las más comunes <a href="http://es.wikipedia.org/wiki/Ethernet"><strong>Ethernet</strong></a>, <a href="http://es.wikipedia.org/wiki/Wi-Fi"><strong>WiFi</strong></a>, <a href="http://es.wikipedia.org/wiki/Point-to-Point_Protocol"><strong>PPP</strong></a>, <a href="http://es.wikipedia.org/wiki/Frame_Relay"><strong>Frame Relay</strong></a>, <a href="http://es.wikipedia.org/wiki/Asynchronous_Transfer_Mode"><strong>ATM</strong></a>, entre otras.</p>
<p>Supondremos el caso más común: una red <a href="http://es.wikipedia.org/wiki/Ethernet">Ethernet</a>. Este protocolo se basa en el uso de direcciones de 6 bytes (48 bits), que no son &#8220;<em>ruteables</em>&#8221; (aquí no existen <em>gateways</em>), por lo cual se utiliza en <a href="http://es.wikipedia.org/wiki/LAN">redes de área local</a> (<strong>LANs</strong>). Cada dispositivo <strong>Ethernet</strong> tiene asociada una dirección única (asignada por el fabricante del mismo), la que usualmente se denomina <a href="http://es.wikipedia.org/wiki/MAC_address"><strong>MAC Address</strong></a>.</p>
<h4>Resolución de direcciones</h4>
<p>Supongamos que el <em>host</em> <strong>200.0.0.1</strong> quiere enviar un paquete IP al <em>host</em> <strong>200.0.0.33</strong>. Como ambos están en la misma red local (los primeros 24 bits de sus direcciones coinciden), lo único que debe hacer es averiguar cuál es la dirección <strong>Ethernet</strong> de este último. Para ello, se utiliza el protocolo <a href="http://es.wikipedia.org/wiki/Protocolo_de_resoluci%C3%B3n_de_direcciones"><strong>ARP</strong></a> (&#8220;<em>Address Resolution Protocol</em>&#8220;).</p>
<p>El funcionamiento es muy simple. El host <strong>200.0.0.1</strong> envía un paquete (en terminología de <strong>Ethernet</strong> se denomina &#8220;<em>frame</em>&#8220;) a la dirección <strong>ff:ff:ff:ff:ff:ff</strong> (las direcciones <strong>Ethernet</strong> se denotan con seis bytes en <a href="http://es.wikipedia.org/wiki/Hexadecimal">hexadecimal</a> separados por dos puntos), que es la dirección de <em>broadcast</em> (que llega a todos los <em>hosts</em> de la red) preguntando quién tiene la dirección IP <strong>200.0.0.33</strong>. Dicha solicitud <strong>ARP</strong> tiene como origen la dirección <strong>Ethernet</strong> del emisor (supongamos, <strong>00:30:b8:80:dd:11</strong>). </p>
<p>El poseedor de esa dirección IP le responderá con otro <em>frame</em> con su dirección <strong>Ethernet</strong> como origen (supongamos, <strong>01:4e:bb:a1:01:8b</strong>). De ahora en más, cada vez que el <em>host</em> <strong>200.0.0.1</strong> quiera enviar un paquete IP al <em>host</em> <strong>200.0.0.33</strong>, enviará un <em>frame</em> <strong>Ethernet</strong> proveniente de la dirección <strong>00:30:b8:80:dd:11</strong> a la dirección  <strong>01:4e:bb:a1:01:8b</strong>, conteniendo el paquete original. (La información sobre las direcciones <strong>ARP</strong> se almacenan en cada <em>host</em> en una tabla que tiene una duración de algunos minutos.)</p>
<h4>Fragmentación</h4>
<p>Puede ocurrir que el tamaño de los paquetes producidos por la capa de red (<strong>IP</strong>, en nuestro caso) sean de un tamaño mayor al máximo que puede transmitir el medio físico utilizado (<a href="http://es.wikipedia.org/wiki/MTU"><strong>MTU</strong></a> o &#8220;<em>unidad máxima de transferencia</em>&#8220;. Por esto, puede ocurrir que los paquetes se <em>fragmenten</em>, para acomodarse a esta limitación.</p>
<p>Esta situación puede volver a presentarse a lo largo del camino &#8220;físico&#8221; que recorra la información, siendo responsabilidad de cada <em>gateway</em> el fragmentar y reensamblar los paquetes para preservar los datos.</p>
<h4>Análisis a nivel de enlace</h4>
<p>Volviendo ahora a nuestro experimento, el <em>host</em> donde se ejecuta la aplicación cliente (<strong>200.0.0.1</strong>) debe enviar paquetes IP al <em>host</em> en donde se ejecuta el servidor (<strong>100.0.0.1</strong>). Claramente, éste último no pertenece a su red local, por lo cual deberá enviarlo a través del <em>gateway</em>.</p>
<p>Para ello, usando el protocolo <strong>ARP</strong> averigua la dirección <strong>Ethernet</strong> del <em>gateway</em> (cuya dirección IP, recordemos, es <strong>200.0.0.21</strong>), que supondremos es <strong>00:01:02:ed:41:61</strong>. Una vez hecho esto, envía un frame <strong>Ethernet</strong> (con origen<br />
<strong>00:30:b8:80:dd:11</strong> y destino <strong>00:01:02:ed:41:61</strong>), conteniendo el paquete IP cuyo origen es <strong>200.0.0.1</strong> y con destino a <strong>100.0.0.1</strong>.</p>
<p>El <em>gateway</em> recibirá el <em>frame</em> (puesto que la dirección <strong>Ethernet</strong> de destino es la suya) y dentro de él encontrará un paquete IP dirigido a <strong>100.0.0.1</strong>. Decrementará el campo <strong>TTL</strong> y, en base a las reglas definidas en su &#8220;<em>tabla de <a href="http://es.wikipedia.org/wiki/Enrutamiento">enrutamiento</a></em>&#8221; (o &#8220;<em>tabla de ruteo</em>&#8220;), lo reenviará hacia el <em>gateway</em> correspondiente (usando el protocolo asociado al medio físico mediante el cual esté conectado con éste).</p>
<p>Una situación similar se presenta considerando los paquetes emitidos por el <em>host</em> <strong>100.0.0.1</strong> hacia <strong>200.0.0.1</strong>.</p>
<p>De esta manera, el mismo paquete IP va atravesando distintos <em>gateways</em> a través de distintos medios físicos (que involucran diferentes protocolos de enlace), hasta llegar al <em>host</em> de destino. Por ejemplo, el paquete original puede llegar al primer <em>gateway</em> a través de un <em>frame</em> <strong>Ethernet</strong>, ser enviado por este al <em>gateway</em> del proveedor de Internet a través de un paquete <strong>PPP</strong>, atravesar una red <strong>ATM</strong> en Internet, luego llegar por un enlace <strong>Frame Relay</strong> al <em>gateway</em> de la red de destino, y ser entregado en otro <em>frame</em> <strong>Ethernet</strong> al <em>host</em> de destino.</p>
<h3>El camino de la información a través de las distintos niveles</h3>
<p>La siguiente figura ilustra un ejemplo del camino que sigue la información desde la aplicación que la produce, a través de los distintos niveles o capas de cada protocolo.</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/tcpip/pila.png" alt="Las distintas capas" /></div>
<p><a href="http://blog.smaldone.com.ar/files/tcpip/pila_big.png">(Ver imagen ampliada)</a></p>
<p><em><strong>Nota:</strong> Este ejemplo es una simplificación de la realidad. Se han omitido muchos otros datos que forman parte de cada protocolo (controles de error, delimitadores, indicadores de longitud, etc.), que no son relevantes en el contexto de este tutorial.</em></p>
<ol>
<li><strong>Nivel de aplicación</strong>: La aplicación (en este caso, el programa cliente), escribe la cadena &#8220;<strong>salir\n</strong>&#8220;.</li>
<li><strong>Nivel de transporte</strong>: En la capa <strong>TCP</strong> forma un paquete agregando el número de secuencia (<strong>20</strong>), puerto de origen (<strong>4781</strong>) y puerto de destino (<strong>2222</strong>).</li>
<li><strong>Nivel de red</strong>: En la capa <strong>IP</strong> se forma un paquete (<em>datagrama</em>) añadiendo la dirección IP origen (<strong>200.0.0.1</strong>), destino (<strong>100.0.0.1</strong>), el <strong>TTL</strong> (<strong>64</strong>) y un valor que identifica el protocolo del paquete encapsulado (<strong>0&#215;06</strong>, valor hexadecimal que representa al protocolo <strong>TCP</strong>).</li>
<li><strong>Nivel de enlace</strong>: En la capa <strong>Ethernet</strong> se forma un nuevo paquete (<em>frame</em>) agregando las direcciones <strong>Ethernet</strong> de origen (<strong>00:30:b8:80:dd:11</strong>) y destino (<strong>00:01:02:ed:41:61</strong>, la dirección del gateway, cuya IP es <strong>200.0.0.21</strong>). Se añade además el identificador del tipo de protocolo del paquete contenido (el valor <strong>0&#215;800</strong> corresponde al protocolo <strong>IP</strong>).</li>
<li><strong>Nivel físico</strong>: El <em>frame</em> formado es enviado a través del medio físico que vincula los <em>hosts</em> de la red local (típicamente, <a href="http://es.wikipedia.org/wiki/Cable_de_par_trenzado">cable de par trenzado</a>).</li>
</ol>
<p>Como puede apreciarse, tanto el protocolo <strong>IP</strong> como <strong>Ethernet</strong> &#8220;<em>encapsulan</em>&#8221; a otros protocolos. Esto permite realizar distintas combinaciones, creando &#8220;<em>túneles</em>&#8221; (como en el caso del ampliamente difundido y utilizado <a href="http://es.wikipedia.org/wiki/PPPoE"><strong>PPPoE</strong></a>).</p>
<h3>Software, modelo conceptual y hardware</h3>
<p>El siguiente diagrama muestra la relación entre cada uno de los componentes lógicos analizados, su implementación y la división entre <em>hardware</em> y <em>software</em>.</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/tcpip/capas.png" alt="Relación entre los componentes" /></div>
<h3>Para finalizar</h3>
<p>En este tutorial hemos recorrido cada uno de los principales componentes del protocolo <strong>TCP/IP</strong> y analizado su función a través de un ejemplo concreto (aunque simple).</p>
<p>Deliberadamente, hemos omitido algunos puntos importantes, como el sistema <a href="http://es.wikipedia.org/wiki/Domain_Name_System">DNS</a> (que posibilita la utilización de nombres en vez de direcciones IP), la asignación automática de direcciones IP (a través del protocolo <a href="http://es.wikipedia.org/wiki/DHCP">DHCP</a>), y algunos detalles sobre <a href="http://es.wikipedia.org/wiki/Protocolo_de_Internet#Direccionamiento_IP_y_enrutamiento">direccionamiento y enrutamiento</a> IP. (Quizás algunos de ellos sean motivo de próximos tutoriales.)</p>
<p>Es el deseo del autor que a través de la lectura del presente tutorial se haya podido lograr un panorama general acerca del funcionamiento de las redes <strong>TCP/IP</strong>, posibilitando al lector su avance hacia temas (y, por qué no, experimentos) más complejos e interesantes.</p>
<p>Las críticas, sugerencias y comentarios son bienvenidos y agradecidos.</p>
<p><em>(Recuerde que puede <a href="http://blog.smaldone.com.ar/files/tcpip/tutorial_tcpip.tgz">descargar este tutorial en otros formatos</a>  (HTML sin decoraciones y PDF).)</em></p>
<p><em><strong>Actualización del 5 de diciembre de 2006</strong>: He publicado un artículo sobre <a href="http://blog.smaldone.com.ar/2006/12/05/como-funciona-el-dns/">el funcionamiento del <strong>DNS</strong></a>, uno de los temas no cubiertos por el presente tutorial.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/11/21/tutorial-sobre-tcpip/feed/</wfw:commentRss>
		<slash:comments>54</slash:comments>
		</item>
		<item>
		<title>Por qué UML no sirve</title>
		<link>http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/</link>
		<comments>http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/#comments</comments>
		<pubDate>Fri, 17 Nov 2006 09:48:59 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/</guid>
		<description><![CDATA[En el año 1999 cursé una materia de ingeniería de software y tuve la desdicha de encontrarme con el &#8220;lenguaje&#8221; de moda para el análisis y diseño (supuestamente) orientado a objetos: UML (&#8220;Unified Modelling Language&#8221; o &#8220;Lenguaje Unificado de Modelado&#8220;) y su metodología asociada: el &#8220;Proceso Unificado&#8220;. Ya desde el comienzo (al leer el manual [...]]]></description>
			<content:encoded><![CDATA[<p>En el año 1999 cursé una materia de ingeniería de software y tuve la desdicha de encontrarme con el &#8220;lenguaje&#8221; de moda para el análisis y diseño (supuestamente) orientado a objetos: <strong>UML</strong> (&#8220;<em>Unified Modelling Language</em>&#8221; o &#8220;<a href="http://es.wikipedia.org/wiki/Lenguaje_Unificado_de_Modelado"><em>Lenguaje Unificado de Modelado</em></a>&#8220;) y su metodología asociada: el &#8220;<a href="http://es.wikipedia.org/wiki/Proceso_Unificado"><em>Proceso Unificado</em></a>&#8220;.</p>
<p><span id="more-66"></span></p>
<p>Ya desde el comienzo (al leer el manual de referencia de este lenguaje visual y el libro que describe la metodología), me resultó bastante incómodo, poco expresivo y hasta inconsistente en algunos aspectos. Con el correr del tiempo (al conocer otras metodologías de diseño de sistemas y al adquirir mayor experiencia en el campo), fui afianzando mi opinión sobre <strong>UML</strong>: es totalmente inútil y contraproducente.</p>
<p>Se que quizás estas afirmaciones suenen un poco fuertes, pero realmente no puedo utilizar términos más suaves para describir a este &#8220;lenguaje visual&#8221; y su metodología asociada. Reconozco que algunos de sus diagramas pueden ser útiles para modelar algunos aspectos de algunos tipos de sistemas, pero en general, es una pésima idea utilizar <strong>UML</strong> para documentar el proceso de desarrollo de software y el <strong>Proceso Unificado</strong> para conducirlo.</p>
<p>Para empeorar la situación, es tal la campaña publicitaria que se ha montado alrededor de <strong>UML</strong> desde su aparición, que en la actualidad es la notación preferida por muchos que dicen utilizar metodologías orientadas a objetos (entre ellos el <a href="http://www.omg.org/">Object Management Group</a>). El por qué del éxito y la inutilidad de <strong>UML</strong> parecen estar explicados en un sólo párrafo (extraído del artículo &#8220;<a href="http://www.cse.yorku.ca/techreports/1999/CS-1999-03.pdf">A Comparison of the Business Object Notation and the Unified Modeling Language</a>&#8221; de  Richard Paige y Jonathan Ostroff, cuya lectura recomiendo ampliamente):</p>
<blockquote><p>En 1995, existían entre 20 y 50 notaciones y lenguajes de este tipo. A menudo, los usuarios tenían que elegir entre varios lenguajes de modelado similares con diferencias menores en poder expresivo global. Pero en un encuentro decisivo en Silicon Valley en 1995, metodistas y productores de herramientas acordaron que los usuarios necesitaban un estándar mundial para el metamodelado y la notación. En ese momento nació UML, y ha sido adoptado por desarrolladores de software principales.</p>
</blockquote>
<p>Está claro entonces que <strong>UML</strong> nació de la necesidad de unificar un gran número de notaciones diferentes, y más como un acuerdo comercial que como el producto de una investigación profunda. Esto también queda en evidencia por el hecho que sus tres principales autores (Grady Booch, Ivar Jacobson y James Rumbaugh) se asociaron para crear una empresa (<strong>Rational Software</strong>, luego adquirida por <strong>IBM</strong>). Nada demasiado bueno puede surgir de la unión apresurada de un montón de cosas incompletas guiada por objetivos comerciales. <strong>UML</strong> y el <strong>Proceso Unificado</strong> nunca fueron más que eso.</p>
<p>Afirmo esto sin importarme que <strong>UML</strong> sea quizás la notación más utilizada en la industria actualmente. Como bien dijo <a href="http://es.wikipedia.org/wiki/Edsger_Dijkstra">Dijkstra</a>, &#8220;<em>los errores industriales no son sacrosantos sólo porque se cometan a gran escala</em>&#8220;. (Si lo que usted busca es trabajo, probablemente necesite conocer <strong>UML</strong>, pero eso no lo transforma en una mejor herramienta.)</p>
<p>En 1997 Bertrand Meyer (creador del lenguaje <a href="http://es.wikipedia.org/wiki/Lenguaje_de_programaci%C3%B3n_Eiffel">Eiffel</a> que se apega al <a href="http://www.bon-method.com/index_normal.htm">método BON</a>) escribío un artículo de tono gracioso (en el que un supuesto estudiante escribe a su profesor) llamado &#8220;<a href="http://archive.eiffel.com/doc/manuals/technology/bmarticles/uml/page.html">UML: The positive spin</a>&#8221; en el que desnuda las principales falencias de <strong>UML</strong> (en un tono para nada respetuoso, debo decir).</p>
<p>Después de años de tenerlo en mi lista de artículos a traducir, finalmente me he decidido a hacerlo. Ojalá sirva de utilidad a quienes hayan sido víctimas de la campaña publicitaria en favor de <strong>UML</strong>.</p>
<p><em>(También puede descargar <a href="/files/uml/uml_giro_positivo.pdf">la versión en PDF</a> de este artículo.)</em></p>
<h3>UML: El giro positivo</h3>
<p><strong><em>Por Bertrand Meyer</em></strong></p>
<p>&nbsp;</p>
<p><strong>De</strong>: Cándido Smith, alumno de OO-101<br />
<strong>A</strong>: Profesor Severa Stern<br />
<strong>Asunto</strong>: Mi solicitud para el cambio de calificación
</p>
<h4>Estimado Profesor Stern:</h4>
<p>El auxiliar de cátedra de su clase OO-101 me ha indicado que le escriba directamente sobre la calificación D-menos que obtuve en mi artículo &#8220;<em>Una evaluación sobre el Lenguaje de Modelado Unificado (UML) propuesto</em>&#8220;. Espero que usted considere cambiarla por una mejor (¿quizás una D?), ya que sería un alivio para mi promedio de calificaciones, ya debilitado luego del &#8220;Reprobado&#8221; que me puso en su última clase. (Quizás recuerde que en el examen final escribí &#8220;<em>debe haber otras cosas entre las rebanadas de pan y Java</em>&#8220;. Ahora me doy cuenta de lo poco aconsejable que fue ese comentario, y sinceramente pido disculpas si herí los sentimientos de alguien.)</p>
<p>Por supuesto, me doy cuenta del motivo de mi D-menos y aprecio su generosidad en no ser haber sido más duro. Como el auxiliar de cátedra me indicó, ¡no hay nada positivo sobre UML en mi artículo! Seguramente no puede ser correcto. Todo el mundo sabe que UML abre una brecha en la ingeniería de software, ¿y quién soy yo para cuestionar esto? Este es el por qué no le pido que cambie mi nota sólo por el efecto en mi promedio, aunque espero entenderá que no es agradable perder mi seguro de Buen Estudiante, por no mencionar novias y cosas por el estilo. No, admito que estaba equivocado y quiero enmendar mi error. Debe haber algo bueno que decir sobre UML.</p>
<p>Y puedo asegurarle que aprenderé la lección: ser positivo. Cualquiera sea el tema, siempre es posible darle un giro positivo. ¡El diario de esta mañana hasta imprimió un adjetivo que puede ser interpretado como no poco favorecedor en un artículo sobre Newt Ginrich! ¿Por qué entonces no sobre UML?</p>
<h4>¡Se positivo!</h4>
<p>Por lo tanto he seguido el consejo del auxiliar de cátedra. Por ejemplo, podría haber cosas buenas que decir sobre la notación misma. Podría ser simple, usable, convincente, fácil de aprender. Y hay de hecho tal notación para el análisis y el diseño orientados a objetos, cuyo conjunto completo de símbolos gráficos cabe en una página y cubre todas las técnicas básicas de descripción de sistemas Orientadas a Objetos, y el cual es particularmente bueno al escalar hasta la descripción de sistemas de gran envergadura: la Notación de Objetos de Negocios (BON) de Waldén y Nerson, como está descripta en su libro [3]. Por supuesto UML no tiene ninguna de esas propiedades. En su intento de mostrar que ha incluido las ideas de todos, es un desborde de símbolo tras símbolo bizarros. Solo el &#8220;<strong>Resumen de la notación</strong>&#8221; ocupa 60 páginas y ¡tiene su propio índice! UML es, de hecho, tan complejo como un lenguaje de programación grande y críptico, con un uso generoso de &#8220;<strong>$</strong>&#8221; y &#8220;<strong>#</strong>&#8221; y &#8220;<strong>-</strong>&#8221; y &#8220;<strong>*</strong>&#8221; y &#8220;<strong>triángulos sólidos sin cola</strong>&#8221; y rectángulos y diamantes y líneas sólidas y líneas punteadas y elipses sólidas y elipses punteadas y flechas de todo tipo y palabras reservadas tomo &#8220;<strong>const</strong>&#8221; y &#8220;<strong>sorted</strong>&#8221; (no confundir con &#8220;<strong>ordered</strong>&#8220;) y semánticas distintas para una clase dependiendo si su nombre aparece en letra &#8220;romana&#8221; o &#8220;itálica&#8221;. ¡Pero al menos un lenguaje de programación, incluso el peor de ellos, es ejecutable! Aquí hay que aprender toda esa complejidad monstruosa para construir diagramas de un posible sistema futuro.</p>
<p>Lo cual nos lleva a la pregunta del desarrollo continuo (&#8220;seamless&#8221;). Una vez que usted tiene su hermoso (o no tan hermoso) diagrama, querrá construir un sistema, salvo que el presupuesto ya se haya gastado en herramientas CASE (algo común para empresas que toman demasiado enserio la publicidad exagerada de la &#8220;metodología&#8221; y terminan sin dinero para el desarrollo real). Pero entonces debe recomenzar con un lenguaje de programación para hacer el trabajo real. ¿Y cómo mantiene la consistencia entre el programa y los diagramas? Waldén y Nerson convincentemente discuten el objetivo de continuidad (proveer un proceso único y continuo) y reversibilidad (ser capaces de moverse hacia atrás y hacia adelante entre el análisis, el diseño y la implementación). Herramientas como EiffelBench y EiffelCase soportan esta inquietud, pero no parece ser para nada una inquietud con UML. Pero por supuesto cualquiera que use UML debe ser listo -no solamente para aprender todos los símbolos- y por lo tanto hará un análisis correcto desde la primera vez, luego un diseño correcto desde la primera vez y luego nunca tendrá que cambiar nada en los próximos diez años de la vida del proyecto. O quizás UML es para proyectos cuyas especificaciones nunca cambian. Mi abuela me contó que una vez escuchó sobre un proyecto así en su juventud. Creo que dijo que tenía algo que ver con calcular el 6to. número de Fibonacci.</p>
<h4>¿Orientado a Objetos?</h4>
<p>Así es que tengo que buscar en otra parte para encontrar algo positivo que decir sobre UML. Estoy tratando con esfuerzo, y espero que lo tome en cuenta antes de enviar las notas finales a la administración. (Recuerde, sólo estoy pidiendo una D, aunque por supuesto una C-menos sería más que apreciada.) Por ejemplo, he intentado ver si podría caracterizar a UML como &#8220;orientado a objetos&#8221;; todos sabemos que este es un gran logro. Gran oportunidad. Por supuesto los autores hacen uso de los requeridos &#8220;<strong>objeto</strong>&#8220;, &#8220;<strong>herencia</strong>&#8220;, etc. Pero una simple mirada a los diagramas muestra a UML como lo que es: una extensión del modelado de entidad-relación. Los ejemplos básicos muestran asociaciones binarias y ternarias, tales como (página 16 de [1]) asociaciones entre &#8220;<strong>vuelo</strong>&#8220;, &#8220;<strong>asiento</strong>&#8221; y &#8220;<strong>persona</strong>&#8220;; esto es diametralmente opuesto al diseño orientado a objetos, donde, como todos sabemos, el mundo está estructurado en clases, construido sobre tipos de objetos y cada operación o propiedad pertenece a una clase. Seguramente en diseño orientado a objetos, ¡no podrá tener un enlace &#8220;<strong>pasajero</strong>&#8221; que trate simétricamente a &#8220;<strong>asiento</strong>&#8220;, &#8220;<strong>vuelo</strong>&#8221; y &#8220;<strong>persona</strong>&#8220;! En un sistema orientado a objetos, pertenecerá a una de las clases; así es como se obtiene la consistencia, simplicidad, modularidad y reusabilidad de la arquitectura OO. Fíjese en BON o Eiffel y disfrute los resultados. Los autores de UML saben esto, por supuesto; para entender por qué llaman a UML orientado a objetos debemos apreciar su famoso sentido del humor. Obviamente lo dicen en broma. (¿Es esto lo suficientemente positivo?)</p>
<p>La evidencia posterior de la broma es provista por la referencia frecuente a los &#8220;casos de uso&#8221; como un elemento central del método. Cuando &#8220;caso de uso&#8221; era la consigna de la Web, traté de entender de qué se trataba, y me fue difícil hasta que le pregunté a mi abuelo, quien me lo explicó todo: es el nuevo nombre para el análisis funcional descendente (top-down) de su adolescencia. Uno se fija en qué debe hacer el sistema (&#8220;casos de uso&#8221;) y deduce su arquitectura de ese análisis. Esto es diametralmente opuesto al diseño orientado a objetos, el cual conscientemente rechaza prestar demasiada atención, durante las primeras etapas, a las funciones principales del sistema, porque están muy sujetas a cambios, porque reproducen el comportamiento de sistemas previos (aquellos que estamos tratando de reemplazar con el nuevo sistema), porque llevan a un compromiso temprano con el orden de operación (¿el aspecto más volátil del software?) y porque se enfocan en las cualidades superficiales del sistema -su interfaz con el resto del mundo- en vez de en sus propiedades fundamentales. En cambio, el diseño OO se concentra en el tipo de los objetos manipulados por el sistema, y define cada uno de esos tipos a través de la lista de todas las operaciones aplicables y sus propiedades abstractas -contratos- sin importar el orden de aplicación. Mi abuelo agregó que estaba agradecido de que los casos de uso estuvieran presentes en UML, porque le traían recuerdos de los buenos viejos tiempos y que nunca le gustaron los objetos. (Creo que este <em>es</em> un comentario positivo.)</p>
<h4>Encontrando un uso para UML</h4>
<p>Obviamente UML no será útil a ningún desarrollador de software. ¿Podría quizás ser útil a alguien más? ¿Líderes de proyectos, quizás? ¿Ejecutivos de empresas? ¿Las empresas mismas? Por supuesto, no. De hecho, la documentación no hace ninguna pretensión de tal utilidad, y por buenas razones. Es difícil de imaginar qué benericio podría obtener un negocio de páginas de diagramas crípticos sobre propiedades confusas de un sistema pobremente comprendido.</p>
<p>Por lo tanto, busco más allá. A veces una propuesta ofrece una solución fallida, pero tiene el mérito de plantear el problema correcto. ¿Podemos decir esto de UML? Espero que sí. Aunque sea podría ayudar a mi promedio y, haciendo esto, me ayudaría a levantar mi autoestima, por no mencionar las novias y cosas por el estilo, pero me estoy desviando. Dicho sea de paso, ¿a qué problema apunta UML? He tratado con empeño, en los interminables documentos del sitio web de Rational, de encontrar una descripción del problema (lo cual se nos enseñó en nuestros cursos de ingeniería, debería aparecer en la introducción de cualquier documento técnico). Me temo que no he encontrado nada útil que reportar.</p>
<h4>Amanda al rescate</h4>
<p>Para ver qué metas debería haber perseguido, revisé el artículo de mi amiga Amanda Suertuda, la única que obtuvo un A-mas:<br />
&#8220;<em>Los desafíos de la industria del software</em>&#8220;. (De paso, profesor, no logro comprender por qué Amanda siempre consigue los temas interesantes.) Su reporte, muy agradable debo confesar, hace un buen trabajo al describir los obstáculos técnicos que los desarrolladores deben superar. El buen software debe ser correcto; olvidemos una aserción en una rutina, y tendremos un desastre de u$s500 millones como la reciente explosión del Ariane 5, resultado de un intento equivocado de reutilizar una rutina de Ada sin un mecanismo de aserciones como el de Eiffel (ver la edición de enero de 1997 de IEEE Computer). No veo nada en UML que ayude a la corrección; sólo algo de palabrería dedicada a la noción de contrato, pero los autores muestran que no entienden nada de la idea  del Diseño por Contratos (por ejemplo, ¡mezclan requerimientos semánticos con simples declaraciones de tipos!). El buen software debe ser robusto. ¿Cómo ayuda UML? El buen software debe ser fácil de modificar (Amanda llama a esto &#8220;<em>extensibilidad</em>&#8220;); pero el aparato pesado de UML garantiza cualquier cosa menos que los desarrolladores serán capaces de producir alguna descripción del sistema en la cual no será horroroso cambiar algo. El buen software debe ser reutilizable; nada en UML apunta a los desafíos de la reutilización, tales como las convenciones de estandarización de interfaces, separación de comandos y opciones, separación de comandos y consultas, etcétera. El buen software debe ser eficiente -oh, lo lamento, esto está relacionado a la implementación, y no hablamos sobre implementación en compañía educada. (Si UML se ocupara de la implementación, debería ocuparse de cuestiones relacionadas con el software; lo bueno de las burbujas y flechas, en oposición a los programas, es que nunca se cuelgan.)</p>
<p>En cambio, los documentos de UML parecen tener un único objetivo: una y otra vez convencer al lector de que no ha omitido ninguna consigna, como en este extracto, página 31 de [2], que intenta explicar que los patrones (patterns) son interesantes pero están cubiertos por el concepto de los autores de &#8220;<em>diagrama de interacción</em>&#8220;, cualquier cosa que esto sea (no puedo imaginarlo):</p>
<blockquote><p>El aspecto interesante de muchos patrones es su comportamiento dinámico. Ciertamente podemos mostrar esto con diagramas de interacción, pero sólo en el contexto de un modelo específico. Es difícil mantener la &#8220;patronidad&#8221; de los patrones abierta. A fin de cuentas, los patrones son plantillas (en algún sentido), en los cuales las clases, asociaciones, atributos y operaciones pueden estar asignadas a distintos nombres, manteniendo la esencia del patrón. Necesitamos una forma de parametrizar claramente los patrones. Sin embargo, creemos que tenemos suficientes formas de capturar patrones en UML expresándolos en términos de interacciones.</p>
</blockquote>
<p>Francamente, ¿quién está interesado en esta farsa? ¿Cómo puede alguien creer que tiene algo que ver (en algún sentido) con la industria del software? Y ni siquiera he citado cosas sobre el &#8220;<strong>metamodelo</strong>&#8220;. Quizás usted podría pedirle a Amanda que dedique su próximo artículo a la &#8220;<strong>patronidad</strong>&#8220;. Hablando de desafíos.</p>
<p>Un amigo que asistió recientemente a una conferencia sobre orientación a objetos me contó sobre las bromas sobre UML que los expertos en OO -algunas de las personas más respetadas en el área- intercambiaron durante los intervalos y en el fondo de las salas. Él no podía creer el contraste entre el bombo público y el menosprecio privado. Pero los CEOs y otros tomadores de decisiones sólo escuchan el bombo publicitario; se les dice que UML es orientado a objetos o, más a menudo, que orientación a objetos significa UML. Cuando no pueda ayudar al desarrollo de software, UML le dará un mal nombre a todo el campo de la OO. Dado los bombardeos de mercadotecnia a su alrededor, UML, al no cumplir con sus promesas, tiene el potencial de retrasar el progreso de la tecnología de objetos por diez años.</p>
<h4>¿La respuesta al final?</h4>
<p>Así pues, profesor, ¿qué puedo decir, qué puedo decir? Fui con el auxiliar de cátedra y le pregunté si decir &#8220;<em>La página principal de Rational tiene lindos colores</em>&#8221; ayudaría. Pero me dijo que no, que tendría que encontrar algo más sustancioso. Intenté con &#8220;<em>al menos en su última revisión han dejado de llamar a sus cosas &#8220;método&#8221;, lo que muestra que tienen algún sentido del ridículo</em>&#8220;. Tampoco le bastó. Así que busqué y busqué y, finalmente -estoy tan excitado- ¡lo encontré! ¡Sí, UML tiene un propósito después de todo!</p>
<p>La razón por la cual no lo descubrí antes es que sólo aparece en la conclusión. Un lugar extraño para describir sobre qué uno estuvo escribiendo, pero mejor que no hacerlo en ninguna parte. Por supuesto que lo que encontré no es un objetivo técnico (UML, como ya dije, no sirve a ningún propósito relacionado con el software, lo cual está bien para mí -algunas personas tienen mejores cosas que hacer con sus vidas que tratar de mejorar la tecnología del software). No es un objetivo gerencial, al menos no para personas que administren proyectos de software. No es un objetivo de negocios, al menos no para negocios que usen UML. Pero es un objetivo.</p>
<p>Cuando finalmente descubrí el objetivo, casi rompo en llanto por el espíritu generoso y noble de los autores. Sería injusto decir que las parvas de documentación de UML están vacías de toda substancia, cuando de hecho contienen una idea genuina. La encontré en el último párrafo del último reporte que describe la revisión 0.91 [2]. Allí estaba, con toda franqueza, explicando todo lo que había malinterpretado en mi joven inocencia. ¿Cómo podría criticar el método por no ayudar a los desarrolladores de software o gerentes, cuando no se ocupa en absoluto del desarrollo de software, sino sólo de desarrollar un mercado para consultores y capacitadores? Todo comenzó a tener sentido: la complejidad y rareza de la notación, lo que tontamente tomé como una deficiencia, son de hecho unas de sus cualidades más atractivas, dado que crean infinitas oportunidades de negocios, para Rational y quizás también para otros; así como también su tibia adopción de las ideas de la orientación a objetos, lo que significa que un consultor no tiene que conocer ni apreciar la tecnología de objetos para sacar provecho de UML.</p>
<p>Mi larga búsqueda no ha sido en vano. Me ha llevado a una total comprensión de UML, su admirable máquina auto-alimentada, dedicada de la A a la Z a la creación de un nuevo mercado, libre de todas las dificultades asociadas al desagradable negocio del desarrollo de software: ¡Libros de UML! ¡Cursos de UML! ¡Cursos sobre los libros! ¡Libros sobre los cursos! ¡Revisiones! ¡Journals de UML! ¡Conferencias! ¡Workshops! ¡Tutoriales! ¡Estándares! ¡Comités! ¡Remeras! Mientras más se piensa en las posibilidades, más deslumbrante aparece. Y para cualquier lector valiente o lo suficientemente aburrido como para leer la documentación hasta el final, el esquema global está allí, presentado en el párrafo final.</p>
<p>Todo empezaba a encajar. Con el aire de inevitabilidad que revela una auténtica obra maestra, en toda la gloria del estilo inimitable del documento, las últimas seis líneas repentinamente dieron sentido a los centenares de páginas anteriores:</p>
<blockquote><p>Hay varios cursos públicos, y tenemos conocimiento de que están siendo escritos varios otros libros, que se ocupan de distintos aspectos de UML, todos basados en nuestras publicaciones preliminares previas. Esperamos una amplia difusión de herramientas de soporte, cursos de capacitación y uso de consultores en el futuro. Alentamos fuertemente este tipo de soporte <strong>[¡Bien por ustedes al alentar el soporte de sus propios productos! ¡Qué desinteresado!]</strong> y trabajaremos conjuntamente con autores, capacitadores y consultores para asegurar que sus consultas sean atendidas, de manera de lograr una difusión y soporte consistentes para UML.</p>
</blockquote>
<p>A la espera de una respuesta favorable a mi pedido,</p>
<p><em>Respetuosamente suyo,</em></p>
<p>Cándido Smith</p>
<h4>Referencias</h4>
<p>[1] Rational Software Corporation: <em>0.8 version of the Unified Method: Notation Summary</em>, en <a href="http://www.rational.com">http://www.rational.com</a>.</p>
<p>[2] Rational Software Corporation: <em>0.91 Addendum to the Unified Modeling Language</em>, en <a href="http://www.rational.com">http://www.rational.com</a>. </p>
<p>[3] Kim Waldén and Jean-Marc Nerson: <em>Seamless Object-Oriented Software Architecture: Analysis and Design of Reliable Systems</em>, Prentice Hall, 1995.</p>
<h3>Mi conclusión</h3>
<p>Más allá de la excelente ironía de <strong>Meyer</strong>, a modo de resumen me gustaría destacar los siguientes puntos sobre <strong>UML</strong>:</p>
<ul>
<li><strong>Es por demás complejo.</strong></li>
<li><strong>No es orientado a objetos.</strong></li>
<li><strong>Carece totalmente de una semántica formal.</strong></li>
<li><strong>Dificulta extremadamente la reversibilidad.</strong></li>
</ul>
<p>Para finalizar, una famosa frase de <a href="http://es.wikipedia.org/wiki/C._A._R._Hoare">C.A.R. Hoare</a>:</p>
<blockquote>
<p>. . . Hay dos maneras de construir un diseño de software: una es hacerlo tan<br />
simple que obviamente no tenga deficiencias, y la otra es hacerlo tan complicado<br />
que no tenga deficiencias obvias.</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/11/17/por-que-uml-no-sirve/feed/</wfw:commentRss>
		<slash:comments>103</slash:comments>
		</item>
		<item>
		<title>Programación para redes y concurrencia (IV)</title>
		<link>http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/</link>
		<comments>http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/#comments</comments>
		<pubDate>Sun, 12 Nov 2006 07:11:29 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/</guid>
		<description><![CDATA[En esta última entrega analizaremos algunos de los problemas que se plantean a la hora de desarrollar programas concurrentes. La programación concurrente (implementada a través de procesos separados o de threads) plantea una serie de inconvenientes respecto del uso de recursos o datos compartidos, que abren un campo de investigación interesantísimo y con muchos puntos [...]]]></description>
			<content:encoded><![CDATA[<p>En esta última entrega analizaremos algunos de los problemas que se plantean a la hora de desarrollar programas concurrentes.</p>
<p>La programación concurrente (implementada a través de procesos separados o de <em>threads</em>) plantea una serie de inconvenientes respecto del uso de recursos o datos compartidos, que abren un campo de investigación interesantísimo y con muchos puntos aún no resueltos. Presentaremos aquí solamente una introducción a esta problemática, ejemplificando cada situación con nuestro servidor concurrente.</p>
<p><em><strong>Nota del 19/11/2006:</strong> Ahora puede descargar el <a href="/files/sockets/programacion_redes.tgz">tutorial completo</a>.</em></p>
<p><span id="more-61"></span></p>
<h3>Entregas anteriores</h3>
<p>Esta es la última parte de una serie de cuatro artículos.</p>
<ul>
<li><a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">Primera parte</a>: Planteo del problema, implementación de un servidor secuencial y de un cliente simple.</li>
<li><a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/">Segunda parte</a>: Implementación de un servidor concurrente mediante procesos y mediante <em>threads</em>.</li>
<li><a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/">Tercera parte</a>: Un cliente en PHP para el acceso a múltiples servidores.</li>
</ul>
<h3>Condiciones de carrera</h3>
<p>Supongamos que tenemos un trozo de código como el siguiente:</p>
<blockquote>
<p><code><br />
$x = 10;</p>
<p><strong>sub</strong> decrementar <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($x>0) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$x--<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p>En un entorno de ejecución secuencial, la variable <strong>$x</strong> nunca tomaría valores negativos, independientemente de cuántas veces sea ejecutada la función <strong>decrementar</strong>.</p>
<p>La situación cambia sensiblemente si la función <strong>decrementar</strong> se ejecuta en varios <em>threads</em> distintos y <strong>$x</strong> es una variable compartida. Supongamos que tenemos dos <em>threads</em> llamados <strong>t1</strong> y <strong>t2</strong>. el valor actual de <strong>$x</strong> es &#8220;<strong>1</strong>&#8221; y se presenta la siguiente situación:</p>
<ul>
<li>El <em>thread</em> <strong>t1</strong> evalúa la condición del <strong>if</strong>. Esta resulta verdadera, por lo tanto procederá a la ejecución del cuerpo.</li>
<li>En ese momento se interrumpe <strong>t1</strong> y comienza la ejecución de <strong>t2</strong>.</li>
<li><strong>t2</strong> también evalúa la condición del <strong>if</strong> como verdadera y procede a la ejecución del cuerpo.</li>
<li>Se interrumpe la ejecución de <strong>t2</strong>, continuando la de <strong>t1</strong>.</li>
<li><strong>t1</strong> decrementa el valor de <strong>$x</strong>, quedando este en &#8220;<strong>0</strong>&#8220;.</li>
<li>Se interrumpe la ejecución de <strong>t1</strong>, continuando la de <strong>t2</strong>.</li>
<li><strong>t2</strong> decrementa el valor de <strong>$x</strong>, quedando este en &#8220;<strong>-1</strong>&#8220;.</li>
</ul>
<p>El resultado de esta secuencia de ejecución es que <strong>$x</strong> queda con el valor &#8220;<strong>-1</strong>&#8220;. Como podemos ver, el valor final es dependiente de la forma en que se van ejecutando las sentencias de los distintos <em>threads</em>.</p>
<p>Cuando el resultado de la ejecución concurrente de varios procesos o <em>threads</em> depende de la secuencia que se siga, se dice que existe una &#8220;<em>condición de carrera</em>&#8221; (&#8220;<a href="http://en.wikipedia.org/wiki/Race_condition"><em>race condition</em></a>&#8220;, en inglés).</p>
<p>La solución, como veremos más adelante, es garantizar la <a href="http://es.wikipedia.org/wiki/Exclusi%C3%B3n_mutua_%28inform%C3%A1tica%29">exclusión mutua</a>, impidiendo que un proceso sea interrumpido en medio de un bloque que deba ser considerado &#8220;<em>atómico</em>&#8220;. En el ejemplo anterior, la sentencia <strong>if</strong> completa (su condición y su cuerpo) debería ser ejecutada completamente para garantizar la corrección del resultado.</p>
<h3>Bloqueo mutuo (deadlock)</h3>
<p>Esta es una situación (también llamada &#8220;<a href="http://es.wikipedia.org/wiki/Bloqueo_mutuo">abrazo mortal</a>&#8220;) en la cual un conjunto de procesos se encuentran bloqueados (ninguno puede avanzar en su ejecución) debido a que todos esperan recursos que están tomados por otro proceso del conjunto. Este problema es planteado de forma genérica de varias maneras (la variante más conocida es la de los &#8220;<a href="http://es.wikipedia.org/wiki/Problema_de_los_fil%C3%B3sofos_cenando">filósofos comensales</a>&#8221; o &#8220;<em>cena de los filósofos</em>&#8220;).</p>
<p>La siguiente representación gráfica (aunque lejana a la programación) ilustra de forma muy clara este tipo de situaciones:</p>
<div class="centerpic"><img src="http://blog.smaldone.com.ar/files/sockets/bloqueo.png" alt="Bloqueo mutuo de automóviles" /></div>
<p>Supongamos, por ejemplo, que al abrir un archivo para escritura este queda bloqueado, impidiendo a cualquier otro proceso su apertura. Si tenemos dos programas como los siguientes:</p>
<h4>Programa p1</h4>
<blockquote>
<p><code><br />
open(ARCH1, ">archivo1");<br />
open(ARCH2, ">archivo2");<br />
<em># Hacer algo con ARCH1 y ARCH2</em><br />
close(ARCH2);<br />
close(ARCH1);<br />
</code></p>
</blockquote>
<h4>Programa p2</h4>
<blockquote>
<p><code><br />
open(ARCH2, ">archivo2");<br />
open(ARCH1, ">archivo1");<br />
<em># Hacer algo con ARCH1 y ARCH2</em><br />
close(ARCH1);<br />
close(ARCH2);<br />
</code></p>
</blockquote>
<p>Puede ocurrir que la ejecución de <strong>p1</strong> se detenga inmediatamente después de abrir el <strong>archivo1</strong> y comience la ejecución de <strong>p2</strong>. Este último podrá abrir el <strong>archivo2</strong>, pero al intentar abrir el <strong>archivo1</strong>, como este está bloqueado por <strong>p1</strong>, quedará suspendido hasta que este se libera (pero permanecerá bloqueando a <strong>archivo1</strong>).</p>
<p>La ejecución de <strong>p1</strong> no podrá continuar, porque necesita abrir un archivo que está siendo bloqueado por <strong>p2</strong>, y viceversa).</p>
<p>(<em>Una nota curiosa: aunque parezca muy poco probable la ocurrencia de este tipo de situaciones, suelen darse en la realidad, generalmente ante la incredulidad del desprevenido programador que ve cómo todas las estaciones de trabajo que acceden a su sistema se quedan congeladas hasta que se reinicia una de ellas&#8230;</em>)</p>
<p>Aunque en este ejemplo la forma de evitar el <em>bloqueo mutuo</em> es que ambos programas abran los archivos <strong>archivo1</strong> y <strong>archivo2</strong> en el mismo orden, en general este tipo de situaciones es bastante difícil de evitar. Para ello existen diversas técnicas, tales como el <a href="http://es.wikipedia.org/wiki/Algoritmo_del_banquero">algoritmo del banquero</a>, propuesto por <a href="http://es.wikipedia.org/wiki/Dijkstra">Edsger W. Dijkstra</a>.</p>
<h3>Complicando nuestro problema: añadiendo un recurso compartido</h3>
<p>Volviendo a nuestro problema original, vamos a añadir un recurso compartido para ver cómo resolvemos el problema de su acceso por parte de los distintos procesos o <em>threads</em>, según el caso.</p>
<p>Añadiremos un archivo en el cuál se irán contando las conexiones atendidas por el servidor. Supondremos que el conteo debe hacerse al finalizar la conexión, por lo cual deberá ser realizado por el proceso o <em>thread</em>  encargado del tratamiento de la misma.</p>
<p>Podríamos definir una función llamada <strong>registrar</strong> como sigue:</p>
<blockquote>
<p><code><br />
<strong>sub</strong> registrar <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;open (REG, "+&lt;accesos.log");<br />
&nbsp;&nbsp;&nbsp;&nbsp;$accesos = &lt;REG&gt;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$accesos++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;seek(REG, 0, 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> REG $accesos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;close REG;<br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p>Esta función abre el archivo &#8220;<strong>accesos.log</strong>&#8220;, lee su contenido (inicialmente debería ser <strong>0</strong>) asignándoselo a la variable <strong>$accesos</strong>. Luego incrementa el valor de esta última, posiciona el archivo al comienzo y escribe en él el nuevo valor.</p>
<p>Si añadiéramos a nuestra función  <a href="/files/sockets/procesar.pl"><strong>atender</strong></a> invocación a <strong>registrar</strong>, podría darse el caso de que dos o más procesos (o <em>threads</em>) accedieran al contenido del archivo leyendo el mismo valor. Esto causaría un error en el valor posteriormente escrito (vulgarmente se dice que los procesos  lo &#8220;<em>pisarían</em>&#8220;). Esto es, evidentemente una <em>condición de carrera</em>.</p>
<p>A continuación analizaremos las dos versiones de nuestro servidor concurrente, ejemplificando dos soluciones diferentes para nuestro problema.</p>
<h3>Solución en el servidor con <em>fork</em></h3>
<p>La solución más simple aplicable a nuestro servidor concurrente basado en procesos independientes, es la utilización de la función de bloqueo de archivos que nos provee el sistema operativo. A través de ella, podemos lograr que cuando un proceso requiera la apertura del archivo &#8220;<strong>accesos.log</strong>&#8220;,lo bloquee impidiendo su apertura por parte de otro proceso (el que será suspendido hasta que el primero lo libere).</p>
<p>De esta forma, el servidor quedaría como puede verse a continuación:</p>
<h4>servidor_fork_lock</h4>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl -w</em></p>
<p><strong>use</strong> IO::Socket;<br />
<strong>use</strong> Fcntl ':flock';<br />
<strong>require</strong> "procesar_lock.pl";<br />
inic_reg();<br />
<strong>require</strong> "procesar.pl";<br />
<em># Ignora la señal de terminación de los hijos</em><br />
$SIG{CHLD} = 'IGNORE';<br />
$puerto = 2222;<br />
$servidor = IO::Socket::INET->new(Proto&nbsp;&nbsp;&nbsp;&nbsp;=>'tcp',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalPort=>$puerto,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Listen&nbsp;&nbsp;&nbsp;=>SOMAXCONN,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reuse&nbsp;&nbsp;&nbsp;&nbsp;=>1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or die "Error al iniciar el servidor";</p>
<p><strong>print</strong> "[Aceptando conexiones en puerto $puerto]\n";</p>
<p><strong>while</strong> ($conexion = $servidor->accept()) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> (fork() == 0) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em># Proceso hijo</em><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close $servidor;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;atender($conexion);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em># Proceso padre</em><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close $conexion;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p><a href="/files/sockets/servidor_fork_lock">Descargar el código fuente</a></p>
<h4>procesar_lock.pl</h4>
<blockquote>
<p><code><br />
<strong>sub</strong> inic_reg <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;open (REG, "&gt;accesos.log");<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> REG "0";<br />
&nbsp;&nbsp;&nbsp;&nbsp;close REG;<br />
<strong>}</strong></p>
<p><strong>sub</strong> registrar <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;open (REG, "+&lt;accesos.log");<br />
&nbsp;&nbsp;&nbsp;&nbsp;flock(REG, LOCK_EX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;$accesos = &lt;REG&gt;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$accesos++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;seek(REG, 0, 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> REG $accesos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;flock(REG, LOCK_UN);<br />
&nbsp;&nbsp;&nbsp;&nbsp;close REG;<br />
<strong>}</strong></p>
<p><strong>sub</strong> ejecutar($$) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;($comando, $conexion) = @_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$resultado = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($comando eq 'fecha')  <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$resultado = localtime() . "\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} elsif</strong> ($comando eq 'usuarios')  <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$resultado = `who`;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($resultado) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "OK\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion $resultado;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "FIN\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} elsif</strong> ($comando eq 'salir') <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "Adios.\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "ERR\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong></p>
<p><strong>sub</strong> atender($) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;$conexion = shift;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$ip = $conexion->peerhost;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "[Conexión establecida desde $ip]\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "Bienvenido.\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>do {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($comando = &lt;$conexion&gt;) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$comando =~ s/\r\n|\n//g;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ejecutar($comando, $conexion);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$comando = 'salir';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} until</strong> ($comando eq 'salir');<br />
&nbsp;&nbsp;&nbsp;&nbsp;$conexion->shutdown(2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;registrar;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "[Conexión finalizada desde $ip]\n";<br />
<strong>}</strong></p>
<p>1;<br />
</code></p>
</blockquote>
<p><a href="/files/sockets/procesar_lock.pl">Descargar el código fuente</a></p>
<p>La sentencia &#8220;<strong>flock(REG, LOCK_EX)</strong>&#8221; solicita un bloqueo exclusivo (<strong>LOCK_EX</strong>). De ejecutarse exitosamente, el sistema operativo denegará a cualquier otro proceso un bloqueo sobre este archivo. En caso contrario, el proceso será suspendido hasta que pueda realizarse el bloqueo exitosamente. Por contraparte, la sentencia &#8220;<strong>flock(REG, LOCK_UN)</strong>&#8221; libera el bloqueo del archivo (<strong>LOCK_UN</strong>), permitiendo su obtención por parte de otro proceso.</p>
<p>De esta manera nos aseguramos que, una vez que un proceso ha abierto el archivo y obtiene un bloqueo sobre él, ningún otro pueda leerlo hasta que el primero haya escrito el nuevo valor y lo libere.</p>
<h3>Uso de semáforos para la sincronización</h3>
<p>Si bien el bloqueo de archivos es una buena solución, hay otros casos (como el de variables compartidas u operaciones complejas) para los que no es aplicable. Una alternativa muy utilizada es el uso de <a href="http://es.wikipedia.org/wiki/Sem%C3%A1foro_%28programaci%C3%B3n%29">semáforos</a>.</p>
<p>Un <em>semáforo</em> es una variable especial protegida que se utiliza para permitir o denegar el acceso a recursos compartidos. Esta técnica fue inventada por <a href="http://es.wikipedia.org/wiki/Sem%C3%A1foro_%28programaci%C3%B3n%29">Edsger Dijkstra</a>.</p>
<p>En su forma más simple (<em>semáforo binario</em>) el semáforo puede estar en dos estados: alto o bajo. Incialmente el semáforo se encuentra en estado <em>alto</em>. Un proceso que desee acceder al recurso compartido, debe <em>bajarlo</em> (si ya se encuentra <em>bajo</em> el proceso será suspendido hasta que vuelva a estar <em>alto</em>). Cuando ha terminado de utilizar el recurso compartido, el proceso deberá <em>levantar</em> nuevamente el semáforo.</p>
<p>(Si pensamos en el ejemplo del cruce de 2 calles, la idea resulta evidente.)</p>
<h3>Solución en el servidor con <em>threads</em></h3>
<p>Aunque aquí también podríamos utilizar el bloqueo de archivos, vamos a ejemplificar la solución utilizando un <em>semáforo</em>. El código del servidor con <em>threads</em> quedaría como sigue:</p>
<h4>servidor_threads_sem</h4>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl -w</em></p>
<p><strong>use</strong> threads;<br />
<strong>use</strong> Thread::Semaphore;<br />
<strong>use</strong> IO::Socket;<br />
<strong>require</strong> "procesar_sem.pl";</p>
<p>$puerto = 2222;<br />
$servidor = IO::Socket::INET->new(Proto&nbsp;&nbsp;&nbsp;&nbsp;=>'tcp',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalPort=>$puerto,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Listen&nbsp;&nbsp;&nbsp;=>SOMAXCONN,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reuse&nbsp;&nbsp;&nbsp;&nbsp;=>1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or die "Error al iniciar el servidor";</p>
<p><strong>print</strong> "[Aceptando conexiones en puerto $puerto]\n";<br />
inic_reg();<br />
$semaforo = new Thread::Semaphore;</p>
<p><strong>while</strong> ($conexion = $servidor->accept()) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; threads->create(\&amp;atender, $conexion, $semaforo)<br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p><a href="/files/sockets/servidor_threads_sem">Descargar el código fuente</a></p>
<h4>procesar_sem.pl</h4>
<blockquote>
<p><code><br />
<strong>sub</strong> inic_reg <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;open (REG, "&gt;accesos.log");<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> REG "0";<br />
&nbsp;&nbsp;&nbsp;&nbsp;close REG;<br />
<strong>}</strong></p>
<p><strong>sub</strong> registrar($) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;$semaforo = shift<br />
&nbsp;&nbsp;&nbsp;&nbsp;$semaforo->down;<br />
&nbsp;&nbsp;&nbsp;&nbsp;open (REG, "+&lt;accesos.log");<br />
&nbsp;&nbsp;&nbsp;&nbsp;flock(REG, LOCK_EX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;$accesos = &lt;REG&gt;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$accesos++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;seek(REG, 0, 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> REG $accesos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;flock(REG, LOCK_UN);<br />
&nbsp;&nbsp;&nbsp;&nbsp;close REG;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$semaforo->up;<br />
<strong>}</strong></p>
<p><strong>sub</strong> ejecutar($$) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;($comando, $conexion) = @_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$resultado = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($comando eq 'fecha')  <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$resultado = localtime() . "\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} elsif</strong> ($comando eq 'usuarios')  <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$resultado = `who`;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($resultado) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "OK\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion $resultado;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "FIN\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} elsif</strong> ($comando eq 'salir') <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "Adios.\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "ERR\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong></p>
<p><strong>sub</strong> atender($$) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;($conexion, $semaforo) = @_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$ip = $conexion->peerhost;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "[Conexión establecida desde $ip]\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "Bienvenido.\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>do {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($comando = &lt;$conexion&gt;) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$comando =~ s/\r\n|\n//g;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ejecutar($comando, $conexion);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$comando = 'salir';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} until</strong> ($comando eq 'salir');<br />
&nbsp;&nbsp;&nbsp;&nbsp;$conexion->shutdown(2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;registrar($semaforo);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "[Conexión finalizada desde $ip]\n";<br />
<strong>}</strong></p>
<p>1;<br />
</code></p>
</blockquote>
<p><a href="/files/sockets/procesar_sem.pl">Descargar el código fuente</a></p>
<p>El módulo de <strong>Perl</strong> &#8220;<strong>Thread::Semaphore</strong>&#8221; nos permite utilizar semáforos. El programa principal inicializa el semáforo &#8220;<strong>$semaforo</strong>&#8220;, que luego es pasado como parámetro a la función &#8220;<strong>atender</strong>&#8221; en cada uno de los <em>threads</em> disparado por este. Como todos los <em>threads</em> comparten el mismo semáforo, cada vez que uno de ellos proceda a actualizar el contenido del archivo &#8220;<strong>accesos.log</strong>&#8220;, impedirá a los otros la ejecución de esta tarea, hasta haberla finalizado.</p>
<h3>Concluyendo</h3>
<p>En esta última entrega, hemos realizado una breve introducción  a algunos de los problemas que se presentan a la hora de construir programas concurrentes o paralelos.</p>
<p>Hemos añadido un recurso compartido a nuestro sistema y explorado dos técnicas que nos permiten sincronizar los procesos o <em>threads</em> para evitar condiciones de carrera.</p>
<h3>Nota final del autor</h3>
<p>Es mi deseo que esta serie de artículos haya contribuido a iniciar a lector en dos áreas muy útiles e interesantes de la programación como lo son el desarrollo de programas utilizando <em>sockets</em> como mecanismo de comunicación y la construcción de aplicaciones concurrentes.</p>
<p>Todos los comentarios (sobre todo críticas y sugerencias) son más que bienvenidos. Espero, en un futuro no muy lejano, seguir ampliando algunos temas específicos de estas áreas.</p>
<p>Desde ya, mi más sincero agradecimiento por el tiempo dedicado a la lectura de este artículo.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Programación para redes y concurrencia (III)</title>
		<link>http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/</link>
		<comments>http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/#comments</comments>
		<pubDate>Tue, 07 Nov 2006 18:10:43 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/</guid>
		<description><![CDATA[Luego de que en la entrega anterior complicáramos un poco las cosas introduciendo concurrencia en el servidor, un amigo me hizo notar que quizás sería mejor dar un ejemplo un tanto más &#8220;real&#8221; de lo que estábamos haciendo. Es por eso que dejaremos por un rato el camino planificado para jugar un poco explorando las [...]]]></description>
			<content:encoded><![CDATA[<p>Luego de que en la <a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/">entrega anterior</a> complicáramos un poco las cosas introduciendo concurrencia en el servidor, <a href="http://www.slobos.com.ar/">un amigo</a> me hizo notar que quizás sería mejor dar un ejemplo un tanto más &#8220;<em>real</em>&#8221; de lo que estábamos haciendo.</p>
<p>Es por eso que dejaremos por un rato el camino planificado para jugar un poco explorando las posibilidades con un cliente en <strong>PHP</strong> (en una especie de &#8220;<em>recreo</em>&#8220;).</p>
<p><span id="more-60"></span></p>
<h3>Entregas anteriores</h3>
<ul>
<li><a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">Primera parte</a>: Planteo del problema, implementación de un servidor secuencial y de un cliente simple.</li>
<li><a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/">Segunda parte</a>: Implementación de un servidor concurrente mediante procesos y mediante <em>threads</em>.</li>
</ul>
<p><em><strong>Nota del 19/11/2006:</strong> Ahora puede descargar el <a href="/files/sockets/programacion_redes.tgz">tutorial completo</a>.</em></p>
<h3>Monitoreo remoto de servidores</h3>
<p>Si bien el servicio que hemos diseñado en las entregas anteriores (<a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">I</a> y <a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-ii/">II</a>) es muy limitado (después de todo, es sólo un ejemplo para desarrollar los temas), podemos utilizarlo para realizar un pequeño sistema de monitoreo remoto.</p>
<p>Supongamos que tenemos el servidor corriendo en varios servidores físicos distintos. Vamos a construir un programa <strong>PHP</strong> (para satisfacer los deseos de quienes querían ver algo en este lenguaje) que nos permita conectarnos a cualquiera de los servidores y ver la hora del sistema y los usuarios activos.</p>
<p>Recordemos que esto es sólo un ejemplo simple, por lo cual no tendremos en cuenta la seguridad del sistema. (<em>Además, ¡estamos de recreo!</em>).</p>
<p>El uso de <em>sockets</em> en <strong>PHP</strong> es un poco más complejo que en <strong>Perl</strong>, por lo cual nos conviene definir funciones auxiliares para establecer la conexión, leer y escribir:</p>
<div class="centerpic"><img src="/files/sockets/monitoreo1.png" alt="Funciones de sockets" /></div>
<p>Los <em>sockets</em> en <strong>PHP</strong> son genéricos. Por esto debemos indicar que usaremos direccionamiento IP (<strong>AF_INET</strong>), que realizaremos una comunicación bidireccional (<strong>SOCK_STREAM</strong>) y que usaremos el protocolo <em>TCP</em> (<strong>SOL_TCP</strong>). Al leer de un <em>socket</em> debemos indicar cuál es la longitud máxima (asumimos <strong>1024</strong>). <strong>PHP_NORMAL_READ</strong> significa que lo leído deberá interpretarse como texto. Al escribir, debemos proporcionar la longitud de la salida.</p>
<p>El programa se conectará al servidor especificado por el usuario y ejecutará los comandos <strong>fecha</strong> y <strong>usuarios</strong>, obteniendo las respuestas correspondientes del servidor. Debemos recordar en todo momento cómo funciona el protocolo que hemos definido (ver la <a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">primera entrega</a>). En definitiva, no estamos haciendo más que implementar un nuevo cliente.</p>
<p>Por simplicidad, hemos utilizado un puerto fijo para todos los servidores (el <strong>2222</strong>), aunque este podría variar, y la lista de servidores solo incluye al <em>localhost</em> (<strong>127.0.0.1</strong>).</p>
<div class="centerpic"><img src="/files/sockets/monitoreo2.png" alt="Interacción con el servidor" /></div>
<p>Si comparamos el código <strong>PHP</strong> con el del <a href="http://blog.smaldone.com.ar/files/sockets/cliente">cliente original</a> desarrollado en <strong>Perl</strong> notaremos que hay grandes similitudes entre ambos.</p>
<p>Finalmente, la parte del script que produce la salida HTML con la lista de servidores que pueden ser monitoreados y los datos obtenidos desde el servidor actual.</p>
<div class="centerpic"><img src="/files/sockets/monitoreo3.png" alt="Salida HTML" /></div>
<p>(Por comodidad y facilidad de visualización he puesto imágenes con el código <strong>PHP</strong>, pero si lo desea puede <a href="/files/sockets/monitoreo.php.txt">descargar el código fuente</a> completo.)</p>
<p>A continuación, una muestra de cómo se vería la interfaz una vez consultado el servidor corriendo en <strong>127.0.0.1</strong>:</p>
<div class="centerpic"><img src="/files/sockets/monitoreo4.png" alt="Vista del sistema" /></div>
<h3>Concluyendo</h3>
<p>Hemos tomado un pequeño desvío para ver la implementación de un cliente en <strong>PHP</strong>, acercándonos a un ejemplo un poco más real del posible uso del servicio que hemos diseñado.</p>
<p>Más allá de los detalles propios de cada lenguaje, hemos podido apreciar las grandes similitudes a la hora de implementar un cliente.</p>
<p>En la próxima entrega retomaremos el tratamiento de los problemas relacionados con la ejecución de procesos concurrentes y <em>threads</em>. (<em>¡Fin del recreo!</em>)</p>
<h3>Próxima entrega</h3>
<ul>
<li><a href="http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/">Cuarta parte</a>: El uso de recursos compartidos y los problemas<br />
de la concurrencia.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Programación para redes y concurrencia (II)</title>
		<link>http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/</link>
		<comments>http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/#comments</comments>
		<pubDate>Tue, 07 Nov 2006 15:31:43 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/</guid>
		<description><![CDATA[En la entrega anterior abordamos el diseño de un protocolo y la implementación de un servidor secuencial muy simple y un cliente. Haciendo esto, inspeccionamos los conceptos fundamentales de la programación de aplicaciones usando sockets. En esta segunda parte, desarrollaremos un servidor capaz de recibir conexiones de varios clientes de manera concurrente (en paralelo). Para [...]]]></description>
			<content:encoded><![CDATA[<p>En la <a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">entrega anterior</a> abordamos el diseño de un protocolo y la implementación de un servidor secuencial muy simple y un cliente. Haciendo esto, inspeccionamos los conceptos fundamentales de la programación de aplicaciones usando <em>sockets</em>.</p>
<p>En esta segunda parte, desarrollaremos un servidor capaz de recibir conexiones de varios clientes de manera concurrente (en paralelo). Para ello utilizaremos dos técnicas distintas: procesos múltiples e hilos de ejecución (<em>threads</em>).</p>
<p><em><strong>Nota del 19/11/2006:</strong> Ahora puede descargar el <a href="/files/sockets/programacion_redes.tgz">tutorial completo</a>.</em></p>
<p><span id="more-59"></span></p>
<h3>Entrega anterior</h3>
<ul>
<li><a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">Primera parte</a>: Planteo del problema, implementación de un servidor secuencial y de un cliente simple.</li>
</ul>
<h3>Procesos y concurrencia</h3>
<p>Como vimos anteriormente, un proceso es una instancia en ejecución de un programa. Cada proceso es identificado por un valor numérico único asignado por el sistema operativo en el momento de su creación, llamado <strong>PID</strong> (por &#8220;<em>process identifier</em>&#8220;). En los sistemas operativos derivados de <em>UNIX</em>, la creación de un nuevo proceso se realiza a través de la <a href="http://es.wikipedia.org/wiki/Llamada_al_sistema">llamada al sistema</a> <strong>fork</strong>.</p>
<p>Cuando un proceso invoca la llamada al sistema <strong>fork</strong> el sistema operativo crea un nuevo proceso, idéntico al anterior (tanto en su código ejecutable como en el valor de sus datos). Luego, <strong>fork</strong> devolverá como resultado el valor <strong>0</strong> en el nuevo proceso creado (que comenzará su ejecución en la instrucción inmediata a la invocación de <strong>fork</strong>) y el valor del <strong>PID</strong> del nuevo proceso en el proceso invocante.</p>
<p>Veamos un ejemplo muy sencillo:</p>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl</em><br />
$pid = fork();<br />
<strong>if</strong> ($pid == 0) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "Soy el proceso hijo\n";<br />
<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "Soy el proceso padre\n";<br />
<strong>}</strong><br />
<strong>print</strong> "Fork devolvió: $pid\n";<br />
</code></p>
</blockquote>
<p>La salida de la ejecución de este programa sería similar a la siguiente:</p>
<blockquote>
<p><code><br />
Soy el proceso hijo<br />
Fork devolvió: 0<br />
Soy el proceso padre<br />
Fork devolvió: 3839<br />
</code></p>
</blockquote>
<p>Como podemos ver, la primera línea ejecuta <strong>fork</strong>. A partir de allí se crea un nuevo proceso (hijo) idéntico al anterior (padre). Cuando comenza la ejecución del proceso hijo, se asigna a <strong>$pid</strong> el valor devuelto por <strong>fork</strong> (0). Al retomarse la ejecución del padre, se asigna a <strong>$pid</strong> el valor del <strong>PID</strong> del proceso hijo. (Nótese cómo cada proceso ejecuta la rama correspondiente de la sentencia <strong>if</strong> y ambos ejecutan el último <strong>print</strong>.)</p>
<p>En rigor este ejemplo es incorrecto. Al finalizar el proceso hijo, enviará una señal a través del sistema operativo, notificando al padre de este evento para posibilitar que este último pueda examinar su estado final. Por esto el proceso padre debería esperar la finalización del hijo, ya que en caso contrario este último quedaría en un estado llamado &#8220;<em>zombie</em>&#8221; (se denomina así a un proceso que ha finalizado su ejecución, pero todavía ocupa lugar en la tabla de procesos del sistema operativo). Esto no afecta demasiado en este ejemplo (el proceso padre finaliza enseguida), pero puede impactar seriamente en sistemas de gran envergadura.</p>
<h3>Segunda solución: un servidor concurrente</h3>
<p>Modificaremos ahora el servidor desarrollado inicialmente (ver el <a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">artículo anterior</a>), para que al recibir una conexión cree un nuevo proceso que se encargue de atender los requerimientos del cliente conectado.</p>
<p>Las funciones agrupadas en el archivo <a href="/files/sockets/procesar.pl">procesar.pl</a> (que realizan el tratamiento y la ejecución de los comandos del cliente) no cambian, ya que no hemos alterado el protocolo. El programa principal del servidor sería el siguiente:</p>
<h4>servidor_fork</h4>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl -w</em></p>
<p><strong>use</strong> IO::Socket;<br />
<strong>require</strong> "procesar.pl";<br />
<em># Ignora la señal de terminación de los hijos</em><br />
$SIG{CHLD} = 'IGNORE';<br />
$puerto = 2222;<br />
$servidor = IO::Socket::INET->new(Proto&nbsp;&nbsp;&nbsp;&nbsp;=>'tcp',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalPort=>$puerto,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Listen&nbsp;&nbsp;&nbsp;=>SOMAXCONN,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reuse&nbsp;&nbsp;&nbsp;&nbsp;=>1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or die "Error al iniciar el servidor";</p>
<p><strong>print</strong> "[Aceptando conexiones en puerto $puerto]\n";</p>
<p><strong>while</strong> ($conexion = $servidor->accept()) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> (fork() == 0) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em># Proceso hijo</em><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close $servidor;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;atender($conexion);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em># Proceso padre</em><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close $conexion;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p><a href="/files/sockets/servidor_fork">Descargar el código fuente</a></p>
<p>La sentencia &#8220;<strong>$SIG{CHLD} = &#8216;IGNORE&#8217;</strong>&#8221; permite que el proceso padre ignore las señales de finalización enviadas por los hijos, para no generar procesos &#8220;<em>zombies</em>&#8220;.</p>
<p>Al recibir una conexión, se invoca a <strong>fork</strong>. El proceso hijo cierra su copia de <strong>$servidor</strong> (ya que no permanecerá esperando conexiones), atiende la conexión del cliente (como en la <a href="/files/sockets/servidor_secuencial">versión anterior</a>, a través de la función <strong>atender</strong>) y luego finaliza su ejecución. El proceso padre cierra su copia del <em>socket</em> <strong>$conexion</strong> (la conexión será atendida por el hijo) y vuelve al comienzo del ciclo, esperando nuevas conexiones.</p>
<p>De esta forma, tan pronto es creado el proceso hijo, el servidor vuelve a estar disponible para aceptar y procesar nuevas conexiones.</p>
<p>Una forma de observar el nuevo comportamiento es intentar dos o más conexiones simultaneas usando un cliente <em>telnet</em>, tal como lo hicimos en la <a href="http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/">primera parte</a>.</p>
<h3>Algunas consideraciones sobre la concurrencia</h3>
<p>El uso de <strong>fork</strong> puede resultar muy costoso. La creación de un nuevo proceso, idéntico al anterior, involucra la copia de los segmentos de código y datos de este. Si el proceso a clonar es demasiado grande, esto puede suponer una gran carga para el sistema (en nuestro ejemplo, además, un retraso en la recepción de la próxima conexión).</p>
<p>Una alternativa al uso de <strong>fork</strong> para lograr la ejecución concurrente es el uso de los llamados &#8220;<em>hilos de ejecución</em>&#8221; o &#8220;<em>threads</em>&#8220;.  Los <em>threads</em> son secuencias de ejecución de un mismo proceso y, como tales, comparten sus datos (variables, archivos abiertos, <em>sockets</em>, etc.). Esto significa que, por ejemplo, si en un <em>thread</em> se modifica el valor de una variable, los demás <em>threads</em> accederán al valor modificado.</p>
<p>Esta alternativa representa un ahorro considerable de recursos e incrementa sensiblemente la eficiencia en muchos casos, pero puede requerir de grandes cuidados para evitar los problemas del acceso a recursos compartidos. (En la próxima entrega de este artículo entraremos en mayor detalle respecto de esto.)</p>
<p>Por citar un caso real, el servidor web <a href="http://httpd.apache.org/">Apache</a> utiliza, en su versión <em>1.x</em>, un esquema basado en el uso de <strong>fork</strong> (es decir, para cada nueva conexión, dispara un nuevo proceso). Esto es inaceptable en sistemas de gran escala, por lo cual en la versión <em>2.x</em> existe la posibilidad de atender cada requerimiento usando <em>threads</em>.</p>
<h3>Tercera solución: un servidor concurrente usando threads</h3>
<p>Implementaremos ahora una tercera versión de nuestro servidor, esta vez usando <em>threads</em> para lograr el procesamiento concurrente de las conexiones. Nuevamente, el archivo <a href="/files/sockets/procesar.pl">procesar.pl</a> no se verá alterado. A continuación, el código del servidor:</p>
<h4>servidor_threads</h4>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl -w</em></p>
<p><strong>use</strong> threads;<br />
<strong>use</strong> IO::Socket;<br />
<strong>require</strong> "procesar.pl";</p>
<p>$puerto = 2222;<br />
$servidor = IO::Socket::INET->new(Proto&nbsp;&nbsp;&nbsp;&nbsp;=>'tcp',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalPort=>$puerto,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Listen&nbsp;&nbsp;&nbsp;=>SOMAXCONN,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reuse&nbsp;&nbsp;&nbsp;&nbsp;=>1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or die "Error al iniciar el servidor";</p>
<p><strong>print</strong> "[Aceptando conexiones en puerto $puerto]\n";</p>
<p><strong>while</strong> ($conexion = $servidor->accept()) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp; threads->create(\&amp;atender, $conexion)<br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p><a href="/files/sockets/servidor_threads">Descargar el código fuente</a></p>
<p>Lo primero que podemos apreciar es que esta versión es muy similar al <a href="/files/sockets/servidor_secuencial">servidor secuencial</a> desarrollado inicialmente. Las diferencias radican en que se ha incluido el módulo <strong>threads</strong> de <strong>Perl</strong> y que al recibir una nueva conexión, en vez de ejecutar la función <strong>atender</strong> se crea un nuevo <em>thread</em> mediante el método <strong>threads->create</strong>. Este recibe como parámetros una referencia a la función a ejecutar (<strong>\&amp;atender</strong>) y el parámetro con que se invocará a esta última (<strong>$conexion</strong>).</p>
<p>La ejecución de la función <strong>atender</strong> como un nuevo <em>thread</em> continua paralelamente a la ejecución del resto del proceso (que vuelve a esperar una nueva conexión), hasta la finalización de la misma.</p>
<h3>Concluyendo</h3>
<p>Hemos reimplementado nuestro servidor para aceptar conexiones de forma simultánea usando procesos concurrentes. Haciendo esto, hemos analizado los conceptos principales asociados a la creación y ejecución de procesos mediante la llamada al sistema <strong>fork</strong>.</p>
<p>Luego hemos reimplementado el servidor concurrente, esta vez usando <em>threads</em>, para obtener un programa más eficiente.</p>
<p>En ningún momento ha sido necesaria la modificación de las funciones desarrolladas para procesar los requerimientos de los clientes, ni el pequeño cliente construido anteriormente, ya que no hemos modificado el protocolo ni añadido nuevas funciones al servidor.</p>
<p>Aunque hemos comenzado a explorar el mundo de la programación concurrente, aún no nos hemos topado con niguno de sus problemas. Esto abre las puertas a la próxima entrega del presente tutorial&#8230;</p>
<h3>Próximas entregas</h3>
<ul>
<li><a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/">Tercera parte</a>: Un cliente en PHP para el acceso a múltiples servidores.</li>
<li><a href="http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/">Cuarta parte</a>: El uso de recursos compartidos y los problemas<br />
de la concurrencia.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Programación para redes y concurrencia (I)</title>
		<link>http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/</link>
		<comments>http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/#comments</comments>
		<pubDate>Mon, 06 Nov 2006 09:12:44 +0000</pubDate>
		<dc:creator>Javier</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/</guid>
		<description><![CDATA[Este es el inicio de una serie de artículos introductorios sobre programación para redes (usando sockets) y programación concurrente. El objetivo es presentar una serie de conceptos que iremos explorando progresivamente: Programación cliente/servidor usando sockets: Cómo desarrollar programas que se comuniquen a través de la red para realizar distintas tareas. Protocolos de comunicación: Explorar los [...]]]></description>
			<content:encoded><![CDATA[<p>Este es el inicio de una serie de artículos introductorios sobre programación para redes (usando <em>sockets</em>) y programación concurrente. El objetivo es presentar una serie de conceptos que iremos explorando progresivamente:</p>
<ul>
<li><strong>Programación cliente/servidor usando <em>sockets</em></strong>: Cómo desarrollar programas que se comuniquen a través de la red para realizar distintas tareas.</li>
<li><strong>Protocolos de comunicación</strong>: Explorar los principios básicos de los protocolos de aplicación (alto nivel) utilizados para intercambiar información.</li>
<li><strong>Concurrencia</strong>: Cómo construir programas que se ejecuten paralelamente y resolver algunos de los problemas que nos plantea la concurrencia.</li>
</ul>
<p><em><strong>Nota del 19/11/2006:</strong> Ahora puede descargar el <a href="/files/sockets/programacion_redes.tgz">tutorial completo</a>.</em></p>
<p><span id="more-58"></span></p>
<p>En los ejemplos utilizaremos el lenguaje <strong>Perl</strong>, por su simplicidad para este tipo de tareas, pero el objetivo es que estos artículos sean de utilidad aún para quienes no conozcan dicho lenguaje. Por esto no nos limitaremos a exponer ejemplos, sino que desarrollaremos cada concepto independientemente del lenguaje de programación.</p>
<h3>El problema</h3>
<p>Implementaremos un servidor que reciba conexiones de clientes a través de la red y permita ejecutar comandos para realizar distintas tareas.</p>
<p>Por simplicidad, definiremos solamente los siguientes comandos:</p>
<ul>
<li><strong>fecha</strong>: Muestra la fecha y hora actual del servidor.</li>
<li><strong>usuarios</strong>: Muestra los usuarios activos del sistema (asumiremos que el servidor se ejecuta en un sistema compatible con <em>UNIX</em> y que posee el comando <strong>who</strong>).</li>
</ul>
<h3>El protocolo</h3>
<p>Como primera medida, deberemos diseñar el <a href="http://es.wikipedia.org/wiki/Protocolo_de_red">protocolo</a> que seguirán el cliente y el servidor para comunicarse e interactuar. Un protocolo puede verse como una secuencia de pasos y convenciones, de acuerdo a las cuales se llevará a cabo la comunicación.</p>
<p>En este caso, estamos diseñando un <a href="http://es.wikipedia.org/wiki/Nivel_de_aplicaci%C3%B3n#Capa_de_aplicaci.C3.B3n_.28Capa_7.29">protocolo de aplicación</a> (de alto nivel, distinto a los protocolos de enlace o transporte de información), que permita que ambas aplicaciones (el cliente y el servidor) intercambien información, ejecuten comandos, etc. En esta situación, siempre es recomendable que el protocolo sea lo más cercano a la comunicación humana: usando mensajes de texto y sin codificaciones extrañas.</p>
<p>La comunicación se llevará a cabo de la siguiente manera (cada envío, del servidor o del cliente, finalizará con un salto de línea):</p>
<ol>
<li>Al recibir la conexión del cliente, el servidor enviará el mensaje &#8220;<strong>Bienvenido.</strong>&#8220;.</li>
<li>El cliente enviará el comando deseado (&#8220;<strong>fecha</strong>&#8221; o &#8220;<strong>usuarios</strong>&#8220;).</li>
<li>En caso de tratarse de un comando válido, el servidor responderá con &#8220;<strong>OK</strong>&#8221; seguido del resultado de la ejecución del mismo (el cual puede constar de varias líneas) y luego el mensaje &#8220;<strong>FIN</strong>&#8220;.</li>
<li>En caso de recibir un comando erróneo, el servidor responderá con el mensaje &#8220;<strong>ERR</strong>&#8220;.</li>
<li>En cualquier caso, el servidor volverá a esperar otro comando.</li>
<li>Si el cliente envía el comando &#8220;<strong>salir</strong>&#8221; el servidor responderá con el mensaje de despedida &#8220;<strong>Adios.</strong>&#8221; y cerrará la conexión.</li>
<li>La conexión puede ser cerrada por el cliente en cualquier momento, sin que esto afecte el estado del servidor.</li>
</ol>
<h3>Algunos conceptos preliminares</h3>
<p>Se llama <em>proceso</em> a una instancia en ejecución de un <em>programa</em>. El concepto de <em>programa</em> es estático: se refiere al archivo ejecutable, en tanto que el de <em>proceso</em> es dinámico. De esta forma, puede haber varias instancias en ejecución (<em>procesos</em>) del mismo programa.</p>
<p>Un <em>socket</em> es un mecanismo utilizado para comunicar dos procesos a través de la red, usando algún <a href="http://es.wikipedia.org/wiki/Nivel_de_transporte">protocolo de transporte</a>. Para la comunicación entre el servidor y el cliente, utilizaremos el protocolo <a href="http://es.wikipedia.org/wiki/Transmission_Control_Protocol">TCP</a>, por ser el más adecuado para este tipo de sistema (a diferencia de <a href="http://es.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a>, es orientado a conexiones).</p>
<p>La comunicación a través de <em>sockets</em> con <strong>TCP</strong> se realiza de la siguiente forma:</p>
<ul>
<li>El proceso servidor abre un <em>socket</em> asociado a determinado <strong>puerto TCP</strong>.</li>
<li>El servidor permanece &#8220;<em>escuchando</em>&#8221; en dicho puerto, a la espera de la apertura de una conexión.</li>
<li>Un cliente abre un <em>socket</em> conectándose al servidor haciendo referencia a su <a href="http://es.wikipedia.org/wiki/Direcci%C3%B3n_IP">dirección IP</a> y al número de puerto.</li>
<li>Una vez establecida la conexión entre ambos procesos, ya no existe más diferencia entre cliente y servidor.</li>
<li>El socket puede ser visto, por ambos procesos, como un archivo de lectura/escritura abierto.</li>
<li>Ambos procesos intercambian información de acuerdo al protocolo de aplicación establecido.</li>
<li>Para finalizar la conexión, cualquiera de los dos procesos cierra el socket.</li>
</ul>
<h3>Primera solución: un servidor secuencial</h3>
<p>Nuestra primera solución al problema será a través  de un servidor secuencial. El programa servidor abrirá un <em>socket</em> en un puerto <em>TCP</em> determinado y quedará a la espera de conexiones entrantes. Al recibir una conexión, interactuará con el cliente según el protocolo que hemos definido, hasta la desconexión de este último. Una vez finalizada la conexión, volverá al estado de espera hasta recibir una nueva solicitud.</p>
<p>Si el servidor se encuentra interactuando con un cliente y un segundo cliente intenta realizar otra conexión, esta será demorada hasta finalizada la actual. En casi cualquier situación real esto sería inaceptable, pero momentaneamente vamos a asumir esta limitación. (En la segunda parte de este artículo implementaremos un servidor que pueda recibir varias conexiones concurrentes.)</p>
<h4>servidor_secuencial</h4>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl -w</em></p>
<p><strong>use</strong> IO::Socket;<br />
<strong>require</strong> "procesar.pl";</p>
<p>$puerto = 2222;<br />
$servidor = IO::Socket::INET->new(Proto&nbsp;&nbsp;&nbsp;&nbsp;=>'tcp',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalPort=>$puerto,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Listen&nbsp;&nbsp;&nbsp;=>SOMAXCONN,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reuse&nbsp;&nbsp;&nbsp;&nbsp;=>1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or die "Error al iniciar el servidor";</p>
<p><strong>print</strong> "[Aceptando conexiones en puerto $puerto]\n";</p>
<p><strong>while</strong> ($conexion = $servidor->accept()) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;atender($conexion);<br />
<strong>}</strong><br />
</code></p>
</blockquote>
<p><a href="/files/sockets/servidor_secuencial">Descargar el código fuente</a></p>
<p>El programa utiliza el módulo (biblioteca) <strong>IO::Socket</strong> que provee una implementación de <em>sockets</em> para el lenguaje <strong>Perl</strong>.</p>
<p>La variable <strong>$servidor</strong> es un <em>socket TCP</em> abierto en el puerto indicado por <strong>$puerto</strong> (en este ejemplo, <em>2222</em>). En los sistemas <em>UNIX</em> para utilizar puertos menores a 1025 se requiere permisos de <em>root</em>. La constante <strong>SOMAXCONN</strong> representa la máxima cantidad de conexiones aún no atendidas que puede &#8220;<em>encolar</em>&#8221; el sistema operativo.</p>
<p>El cuerpo del ciclo principal se ejecuta cada vez que se recibe una conexión. Esto se logra mediante la ejecución de <strong>$servidor->accept()</strong>. La función <strong>atender</strong> está definida en el script <strong>procesar.pl</strong>, que podemos ver a continuación.</p>
<h4>procesar.pl</h4>
<blockquote>
<p><code><br />
<strong>sub</strong> ejecutar($$) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;($comando, $conexion) = @_;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$resultado = '';<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($comando eq 'fecha')  <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$resultado = localtime() . "\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} elsif</strong> ($comando eq 'usuarios')  <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$resultado = `who`;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($resultado) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "OK\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion $resultado;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "FIN\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} elsif</strong> ($comando eq 'salir') <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "Adios.\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "ERR\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>}</strong></p>
<p><strong>sub</strong> atender($) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;$conexion = shift;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$ip = $conexion->peerhost;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "[Conexión establecida desde $ip]\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $conexion "Bienvenido.\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>do {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> ($comando = &lt;$conexion&gt;) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$comando =~ s/\r\n|\n//g;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ejecutar($comando, $conexion);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$comando = 'salir';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>} until</strong> ($comando eq 'salir');<br />
&nbsp;&nbsp;&nbsp;&nbsp;$conexion->shutdown(2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "[Conexión finalizada desde $ip]\n";<br />
<strong>}</strong></p>
<p>1;<br />
</code></p>
</blockquote>
<p><a href="/files/sockets/procesar.pl">Descargar el código fuente</a></p>
<p>La función <strong>atender</strong> toma como parámetro la conexión recibida. Muestra la <em>dirección IP</em> del cliente conectado, envía a este el mensaje de bienvenida e inicia un ciclo que finaliza al recibir el comando &#8220;<strong>salir</strong>&#8221; o al cerrarse la conexión. (Nótese que aunque según el protocolo diseñado el cliente debe enviar el comando &#8220;<strong>salir</strong>&#8221; antes de finalizar la conexión, debemos tener en cuenta que esta puede cerrarse subrepticiamente.)</p>
<p>La sentencia &#8220;<strong>$comando = &lt;$conexion&gt;</strong>&#8221; lee una línea desde el <em>socket</em> de la conexión y la asigna a la variable <strong>$comando</strong>, devolviendo verdadero si la lectura fue exitosa y falso si ocurrió algún error (por ejemplo, en caso de haberse cerrado la conexión).</p>
<p>La sentencia &#8220;<strong>$comando =~ s/\r\n|\n//g</strong>&#8221; utiliza una expresión regular para eliminar el fin de    línea al final de la cadena leída (este puede constar del caracter &#8220;<strong>\n</strong>&#8221; o de los caracteres &#8220;<strong>\r\n</strong>&#8220;).</p>
<p>Una vez obtenido el comando a ejecutar, se llama a la función <strong>ejecutar</strong> que implementa los comandos que hemos definido para nuestro servicio. Si desearamos añadir nuevas funciones a nuestro servidor, bastaría con extender esta función para implementarlas.</p>
<p>Finalmente, la sentencia &#8220;<strong>$conexion->shutdown(2)</strong>&#8221; cierra el <em>socket</em>, finalizando la conexión del lado del servidor.</p>
<p><strong>Una aclaración importante</strong>: Cuando utilizamos la función <strong>print</strong> seguida de una cadena, esta es enviada a la salida estándar (normalmente, la consola desde donde ejecutamos el programa), en tanto que si anteponemos a la cadena el socket (la variable &#8220;<strong>$conexion</strong>&#8220;, en nuestro caso), la cadena es enviada (escrita) a través de él.</p>
<h3>Probando nuestro servidor</h3>
<p>Si ejecutamos el servidor (para lo cual previamente deberemos darle permisos de ejecución, por ejemplo &#8220;<strong>755</strong>&#8220;), veremos el siguiente texto en la consola:</p>
<p><code>[Aceptando conexiones en puerto 2222]</code></p>
<p>La forma más simple de conectarnos al servidor para ejecutar algunos comandos es utilizando un cliente de <em>telnet</em> (si bien no usaremos el servicio <em>telnet</em>, un cliente de este tipo sirve para realizar conexiones <em>tcp</em> a cualquier puerto). En una nueva consola ejecutamos:</p>
<p><code>telnet localhost 2222</code></p>
<p>En la consola del servidor veremos el mensaje:</p>
<p><code>[Conexión establecida desde 127.0.0.1]</code></p>
<p>En tanto que en la consola donde ejecutamos el cliente <em>telnet</em> veremos lo siguiente:</p>
<p><code>Trying 127.0.0.1...<br />
Connected to localhost.<br />
Escape character is '^]'.<br />
Bienvenido.</code></p>
<p>Las primeras líneas son producidas por el cliente <em>telnet</em>, en tanto que el mensaje &#8220;<strong>Bienvenido.</strong>&#8221; es el enviado por el servidor al iniciarse la conexión. Podemos probar la ejecución de comandos, por ejemplo (el texto resaltado en <em>itálica</em> es el ingresado por nosotros):</p>
<p><code><em>fecha</em><br />
OK<br />
Mon Nov  6 05:04:02 2006<br />
FIN<br />
<em>usuarios</em><br />
OK<br />
javier&nbsp;&nbsp;&nbsp;tty1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-11-05 15:55<br />
javier&nbsp;&nbsp;&nbsp;pts/0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-11-05 16:00 (:0.0)<br />
javier&nbsp;&nbsp;&nbsp;pts/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-11-05 16:41 (:0.0)<br />
FIN<br />
<em>noexiste</em><br />
ERR<br />
<em>salir</em><br />
Adios.<br />
Connection closed by foreign host.<br />
</code></p>
<p>Observamos que todo se desarrolla de acuerdo al protocolo de aplicación que hemos diseñado (inclusive la respuesta de error ante un comando inexistente). Si en medio de la sesión intentamos el acceso con otro cliente de telnet, veremos que el servidor no responde hasta que se haya finalizado la conexión actual, tal como lo habíamos previsto.</p>
<h3>Un cliente simple para nuestro protocolo</h3>
<p>Desarrollaremos ahora un cliente que implemente el protocolo definido y que haga uso del servidor.</p>
<blockquote>
<p><code><br />
<em>#!/usr/bin/perl</em></p>
<p><strong>use</strong> IO::Socket;</p>
<p><strong>if</strong> (! defined $ARGV[2]) <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> STDERR "Uso: cliente &lt;servidor&gt; &lt;puerto&gt; &lt;comando&gt;\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;exit 1;<br />
<strong>}</strong></p>
<p>($servidor, $puerto, $comando) = @ARGV;</p>
<p>$conexion = new IO::Socket::INET(PeerAddr => $servidor,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PeerPort => $puerto,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Proto => 'tcp')<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or die "Error al conectar con $servidor";</p>
<p>$mensaje = &lt;$conexion&gt;;<br />
<strong>print</strong> $conexion "$comando\n";<br />
$estado = &lt;$conexion&gt;;<br />
<strong>if</strong> ($estado eq "OK\n") <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;$salida = &lt;$conexion&gt;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>while</strong> ($salida ne "FIN\n") <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> $salida;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$salida = &lt;$conexion&gt;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>}</strong><br />
<strong>} elsif</strong> ($estado eq "ERR\n") <strong>{</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "Comando no válido\n";<br />
<strong>} else {</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>print</strong> "Error en el servidor\n";<br />
<strong>}</strong><br />
<strong>print</strong> $conexion "salir\n";<br />
$mensaje = &lt;$conexion&gt;;<br />
$conexion->shutdown(2);<br />
</code></p>
</blockquote>
<p><a href="/files/sockets/cliente">Descargar el código fuente</a></p>
<p>Nuestro cliente toma como parámetros la dirección IP del servidor, el puerto a utilizar y el comando a ejecutar. Primero controla que se le hayan proporcionado los parámetros correctos y luego abre un <em>socket TCP</em> (como cliente) hacia la IP y el puerto especificados.</p>
<p>De aquí en más, el cliente debe seguir la especificación del protocolo. De otra manera no podrá sincronizarse con el servidor para llevar adelante la secuencia de pasos apropiada. Por ejemplo, antes de enviar el comando a ejecutar, deberá recibir el mensaje de bienvenida (aunque luego no haga nada con él). De la misma forma, luego de enviar el comando, deberá leer el estado que informa el servidor (&#8220;<strong>OK</strong>&#8221; o &#8220;<strong>ERR</strong>&#8220;), para saber si luego deberá leer la salida de su ejecución.</p>
<p>Si el comando produjo el mensaje &#8220;<strong>OK</strong>&#8220;, luego seguirá el resultado del mismo, hasta la recepción del texto &#8220;<strong>FIN</strong>&#8220;. En cualquier caso, el cliente deberá cerrar la conexión enviando el comando &#8220;<strong>salir</strong>&#8221; y recibiendo el mensaje de despedida (aunque, como ya hemos visto al implementar el servidor, el no cumplimiento de esto no debería significar ningún inconveniente).</p>
<p>A continuación, un ejemplo de la ejecución del cliente con el comando &#8220;<strong>usuarios</strong>&#8220;:</p>
<p><code>javier@obelix:~$ <em>./cliente localhost 2222 usuarios</em><br />
javier&nbsp;&nbsp;&nbsp;tty1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-11-05 15:55<br />
javier&nbsp;&nbsp;&nbsp;pts/0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-11-05 16:00 (:0.0)<br />
javier&nbsp;&nbsp;&nbsp;pts/1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2006-11-05 16:41 (:0.0)<br />
javier@obelix:~$</code></p>
<h3>Concluyendo</h3>
<p>Hasta aquí hemos avanzado sobre los conceptos básicos de la programación de aplicaciones cliente/servidor usando <em>sockets</em> usando el protocolo <em>TCP</em>.</p>
<p>Hemos diseñado un pequeño protocolo de aplicación que se ajusta a los requerimientos de nuestro problema, y construido un servidor secuencial y un cliente muy simples que lo implementan.</p>
<p>Una lección adicional que debemos rescatar: el servidor debe implementar el protocolo a seguir, previendo que el cliente puede no respetarlo (en nuestro caso, por ejemplo, este último podría cerrar la conexión en cualquier momento). A la hora de construir un cliente, debemos apegarnos al protocolo tanto como nos sea posible.</p>
<p>En el próximo artículo de esta serie, implementaremos un servidor capaz de atender a varios clientes de forma simultanea. Con esto nos introduciremos en el mundo de las aplicaciones concurrentes o paralelas.</p>
<h3>Próximas entregas</h3>
<ul>
<li><a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-ii/">Segunda parte</a>: Implementación de un servidor concurrente mediante procesos y mediante <em>threads</em>.</li>
<li><a href="http://blog.smaldone.com.ar/2006/11/07/programacion-para-redes-y-concurrencia-iii/">Tercera parte</a>: Un cliente en PHP para el acceso a múltiples servidores.</li>
<li><a href="http://blog.smaldone.com.ar/2006/11/12/programacion-para-redes-y-concurrencia-iv/">Cuarta parte</a>: El uso de recursos compartidos y los problemas<br />
de la concurrencia.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.smaldone.com.ar/2006/11/06/programacion-para-redes-y-concurrencia-i/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
	</channel>
</rss>

