Desarrollo de una aplicación BitTorrent (en Pharo-Smalltalk) Trabajo Final de Grado David Gracia Celemendi Director: Jordi Delgado Pin Departamento de Ciencias de la Computación (CS) Fecha de defensa: octubre de 2015 Titulación: Grado en Ingeniería Informática Especialidad: Computación Facultad de Informática de Barcelona (FIB) página en blanco 1 Resumen Desde principios del 2000, el uso de redes peer-to-peer ha experimentado un gran crecimiento. Gran parte del tráfico de Internet lo generan las aplicaciones BitTorrent. BitTorrent especifica un protocolo para el intercambio de ficheros usando un modelo peer-to-peer que se caracteriza por su gran escalabilidad y robustez. Este proyecto consiste en el desarrollo de una aplicación BitTorrent desde cero con el sistema Pharo-Smalltalk. Primero se hace un repaso a la historia del intercambio de ficheros desde sus inicios hasta la actualidad. Después se comparan las redes cliente-servidor con las redes peer-to-peer , y se distingue dentro de éstas últimas entre redes estructuradas y redes no estructuradas. Se da una explicación del funcionamiento de BitTorrent y, por último, se profundiza en el diseño y la implementación de la aplicación. Abstract Since 2000 peer-to-peer networks use has increased very fast. Most Internet traffic is generated by BitTorrent applications. BitTorrent specify a file sharing protocol over peer-to-peer model whose strength is scalability and robustness. This project is about developing a BitTorrent application from scratch with Pharo-Smalltalk system. First of all, a file sharing history review is done from beginning up to now. Next, client-server and peerto-peer models are compared, and peer-to-peer networks are classified in unstructured and structured. A brief summary of BitTorrent operation is done and, at the end, report go into detail about design and implementation of the application. Keywords. peer-to-peer, p2p, bittorrent, swarming, smalltalk, pharo. Agradecimientos Gracias a Jordi Delgado por su ayuda durante el desarrollo del proyecto y en la elaboración de la memoria. 2 Índice 1. Introducción 1.1. Formulación del problema . . . . . 1.2. Actores implicados . . . . . . . . . 1.3. Objetivos generales . . . . . . . . . 1.4. Objetivos técnicos . . . . . . . . . . 1.5. Sobre el proyecto y el formato de la . . . . . . . . . . . . . . . . . . . . memoria . . . . . . . . . . . . . . . . . . . . . . . . . 2. Contexto 2.1. Redes peer-to-peer . . . . . . . . . . . . . . . . . . . 2.1.1. Descubrimiento de recursos . . . . . . . . . . 2.1.2. Aplicaciones . . . . . . . . . . . . . . . . . . . 2.2. Historia del intercambio de ficheros . . . . . . . . . . 2.3. BitTorrent . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1. Funcionamiento . . . . . . . . . . . . . . . . . 2.3.2. Implementaciones existentes y últimos avances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 8 8 8 9 . . . . . . . 10 10 12 14 15 19 19 21 3. Metodología 22 3.1. Seguimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.2. Estilo de programación . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.3. Validación del software . . . . . . . . . . . . . . . . . . . . . . . . . 23 4. Visión global de la aplicación 23 5. Diseño e implementación 5.1. Bencoding . . . . . . . . . . . . . 5.2. Los metadatos . . . . . . . . . . . 5.3. Acceso físico a los torrents . . . . 5.4. Los nodos de la red . . . . . . . . 5.5. Descubrimiento de peers . . . . . 5.5.1. Tracker HTTP . . . . . . 5.5.2. Tracker UDP . . . . . . . 5.5.3. La clase BtMultitracker 5.6. Control de las piezas . . . . . . . 5.7. Comunicación peer-to-peer . . . . 5.7.1. Mensajes . . . . . . . . . . 5.7.2. Sistema de colas . . . . . . 5.7.3. La clase BtRemotePeer . . 26 26 28 29 31 31 31 34 40 41 42 42 46 47 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.8. Piezas temporales . . . . . . . . . . . . . . . . . . . . 5.9. Algoritmo de bloqueo . . . . . . . . . . . . . . . . . . 5.10. Algoritmo de selección de piezas . . . . . . . . . . . . 5.10.1. Rarest first . . . . . . . . . . . . . . . . . . . 5.10.2. Random . . . . . . . . . . . . . . . . . . . . . 5.10.3. Most common first . . . . . . . . . . . . . . . 5.10.4. Lower first . . . . . . . . . . . . . . . . . . . . 5.11. End Game . . . . . . . . . . . . . . . . . . . . . . . . 5.12. Conteo de las piezas en la red . . . . . . . . . . . . . 5.13. La clase BtRemotePeerCollection . . . . . . . . . . 5.14. La clase BtTorrent . . . . . . . . . . . . . . . . . . . 5.14.1. Proceso: Validación incial . . . . . . . . . . . 5.14.2. Proceso: Tracker requesting . . . . . . . . . . 5.14.3. Proceso: Choking . . . . . . . . . . . . . . . . 5.14.4. Proceso: Optimistic Choking . . . . . . . . . . 5.14.5. Proceso: Tratamiento de mensajes entrantes . 5.14.6. Proceso: Petición de bloques . . . . . . . . . . 5.15. La clase BtLocalPeer . . . . . . . . . . . . . . . . . 5.15.1. Proceso: Escucha de puerto . . . . . . . . . . 5.15.2. Proceso: Gestión de torrents . . . . . . . . . . 5.15.3. Justificación de la necesidad de BtLocalPeer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 50 51 51 51 52 52 52 52 53 55 56 56 57 58 59 60 62 63 64 65 6. Planificación 66 6.1. Planificación inicial . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 6.2. Modificaciones a la planificación inicial . . . . . . . . . . . . . . . . 69 7. Presupuesto y sostenibilidad 7.1. Identificación de recursos y estimación de costes . . . . . . . . . . . 7.2. Viabilidad económica . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3. Impacto social y ambiental . . . . . . . . . . . . . . . . . . . . . . . 69 69 70 70 8. Conclusiones 71 9. Posibles ampliaciones 71 Adenda 73 A. Manual de usuario 73 A.1. Instalación de Pharo . . . . . . . . . . . . . . . . . . . . . . . . . . 73 A.2. Ejecución de Pharo . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4 A.3. Importación de la librería A.4. Pruebas . . . . . . . . . . A.4.1. Pruebas unitarias . A.4.2. Prueba global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 76 76 77 Acrónimos 80 Glosario 81 Referencias 88 5 Índice de figuras 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. BitTorrent. Proceso que siguen las propuestas de ampliación . . . Comparación entre las arquitecturas cliente-servidor y peer-to-peer En Freenet, backtracking usado para encontrar un recurso . . . . . Comparación topológica. Red peer-to-peer no estructurada. Red peer-to-peer estructurada . . . . . . . . . . . . . . . . . . . . . . . Amiexpress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Jerarquía de warez scene . . . . . . . . . . . . . . . . . . . . . . . Usenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funcionamiento de una red BitTorrent . . . . . . . . . . . . . . . Perspectiva del peer local en el enjambre . . . . . . . . . . . . . . Diagrama de clases simplificado de la aplicación . . . . . . . . . . Ejemplo de colección de ficheros de un torrent. . . . . . . . . . . . Jerarquía de clases de los nodos de la red . . . . . . . . . . . . . . Jerarquía de clases de los paquetes UDP . . . . . . . . . . . . . . Diagrama de Gantt del proyecto . . . . . . . . . . . . . . . . . . . Selección de imagen en Pharo (Linux Mint) . . . . . . . . . . . . Menú global de Pharo . . . . . . . . . . . . . . . . . . . . . . . . Añadir un repositorio HTTP público a Pharo . . . . . . . . . . . Exploración del paquete BitTalk en Pharo . . . . . . . . . . . . . Captura de imágen de Pharo mostrando cómo ejecutar todas las pruebas unitarias de una vez . . . . . . . . . . . . . . . . . . . . . Ejecución del código de la prueba global . . . . . . . . . . . . . . . 9 . 11 . 12 . . . . . . . . . . . . . . . 13 15 16 17 19 20 24 30 32 36 68 74 75 75 76 . 77 . 78 Índice de tablas 1. 2. 3. Características. Redes no estructuradas y estructuradas . . . . . . . 14 Planificación de las tareas . . . . . . . . . . . . . . . . . . . . . . . 67 Presupuesto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6 1. 1.1. Introducción Formulación del problema Este Trabajo de Fin de Grado (TFG) consiste en el desarrollo de una aplicación BitTorrent1 en el sistema Pharo-Smalltalk. BitTorrent es un protocolo diseñado para el intercambio de ficheros sobre una red peer-to-peer (P2P) que se usa para distribuir gran cantidad de información por Internet. Smalltalk es un estándar, públicamente disponible desde 1980[4], que define las especificaciones de un sistema computacional que dispone de una biblioteca (generalmente grande) de objetos que viven dentro del sistema y se comunican mediante mensajes; un fichero llamado «imagen» que contiene el estado del sistema; un lenguaje de programación (también llamado Smalltalk) completamente orientado a objetos, con tipado dinámico y reflexivo; un entorno de desarrollo, compilación y ejecución; y una máquina virtual que se encarga de interpretar todos los mensajes y actualizar el estado del sistema y los objetos. Se puede considerar a un sistema Smalltalk como un mundo virtual donde existen objetos «vivos» que se comunican. Estos objetos pueden ser creados, modificados y eliminados sin necesidad de detener o reiniciar el sistema. Una implementación de Smalltalk es cualquier sistema que cumpla las especificaciones del estándar. La implementación que se va a usar en este proyecto es Pharo (versión 3.0). 1 La expresión común para referirse a este tipo de programas es «cliente BitTorrent», pero es incorrecta porque en estas redes no existen los roles exclusivos de cliente y servidor: solo existen peers. En este documento se reserva el uso de «peer » para el concepto abstracto de nodo participante en la red, y el de «aplicación BitTorrent» para el programa que ejecutan los peers 7 1.2. Actores implicados Las siguientes personas están implicadas o se ven afectadas por el desarrollo del proyecto: Autor del proyecto: David Gracia Celemendi. Director del proyecto: Jordi Delgado Pin. Miembro del Consejo de Dirección del European Smalltalk User Group (ESUG) y Coordinador de l’Associació Smalltalk.cat. Se encargará de supervisar los aspectos técnicos del proyecto. Comunidad de usuarios y desarrolladores de Pharo-Smalltalk. Se beneficiarán de la primera librería que implementa BitTorrent en esta comunidad. La librería supondrá una base para desarrollar otros proyectos relacionados con BitTorrent y redes P2P. 1.3. Objetivos generales Los objetivos principales del proyecto son: Diseñar e implementar el conjunto de clases que representen la naturaleza y el funcionamiento de las redes BitTorrent. El diseño y la implementación deben ser fácilmente comprensibles y ampliables por cualquier desarrollador de Pharo-Smalltalk que conozca el funcionamiento de BitTorrent. 1.4. Objetivos técnicos BitTorrent está compuesto por una serie de documentos llamados BitTorrent Enhancement Proposal (BEP) que detallan el funcionamiento del protocolo. Un BEP es una propuesta de ampliación de alguna parte de BitTorrent, que especifica una nueva funcionalidad o comportamiento de la red o de los actores implicados. Todos los BEP pueden encontrarse en http://www.bittorrent.org/beps/bep_0000. html. Estas propuestas están sujetas a un proceso de ampliación que establece que una propuesta debe pasar por unas fases antes de ser considerada definitiva por el Benevolent Dictator for Life (BDFL) y creador de BitTorrent, Bram Cohen. 8 Figura 1: Proceso que siguen las propuestas de ampliación Existen varios BEP utilizados por muchas aplicaciones BitTorrent que todavía se encuentran en fase de borrador (Draft) o solo han sido aceptados (Accepted ) sin llegar a ser definitivos (Final ). Incluso hay otros que, a pesar de no tener siquiera borrador, son utilizados en las aplicaciones BitTorrent más populares. Los objetivos técnicos del proyecto son implementar los siguientes BEP: BEP3 Núcleo de BitTorrent, en fase definitiva. BEP5 Implementa distributed hash table (DHT) para aumentar la descentralización de BitTorrent, en fase de borrador. 1.5. Sobre el proyecto y el formato de la memoria El desarrollo de la aplicación carece de análisis y especificación de requisitos ya que el protocolo ya está especificado y definido. En esta memoria se explica en detalle el diseño y la implementación. No es objetivo de la memoria explicar la especificación y el funcionamiento del protocolo, pero se profundizará en él cuando sea necesario para justificar las decisiones de diseño. El trabajo de este proyecto ha consistido en diseñar e implementar desde cero el protocolo BitTorrent en Pharo-Smalltalk sin usar en absoluto otros trabajos o librerías preexistentes. Se adjuntará código de la aplicación como recurso explicativo del diseño y la implementación. Si la aplicación se hubiera programada en un lenguaje como C/C++ o Java, se habría descartado hacerlo, pero Smalltalk permite crear lenguajes específicos de dominio (en inglés domain-specific language (DSL)) con una sintaxis tan expresiva que puede leerse casi como si fuera lenguaje natural. 9 Pharo-Smalltalk tiene un entorno gráfico que, junto con la reflexión, permite examinar la estructura interna y el estado de los objetos en tiempo real. Aunque desarrollar una interfaz gráfica de usuario (en inglés graphical user interface (GUI)) no era uno de los objetivos técnicos, es posible usar la aplicación con pocas instrucciones y observar en tiempo real el estado de la aplicación con el entorno gráfico de Pharo-Smalltalk. 2. 2.1. Contexto Redes peer-to-peer El criterio de clasificación de redes que probablemente las separa en los dos grupos más significativos es quién sirve los recursos disponibles. Estos dos grupos son las redes cliente-servidor y las redes P2P. Schollmeier[13] define las redes clienteservidor como Arquitectura de red que consiste en un sistema de alto de rendimiento, el servidor, y varios sistemas habitualmente de menor rendimiento, los clientes. El servidor es el único proveedor de servicios. Los clientes solo consumen servicios sin compartir ninguno de sus recursos. Y las redes P2P como Arquitectura de red donde los participantes comparten parte de sus recursos hardware (tiempo de procesador, espacio de almacenamiento, ancho de banda, dispositivos. . . ). Estos recursos son necesarios para proveer el servicio que ofrece la red y son accesibles directamente por cualquier participante sin necesidad de otra entidad mediadora. Los participantes de estas redes son tanto proveedores como consumidores de servicios. 10 (a) Arquitectura cliente-servidor (b) Arquitectura peer-to-peer Figura 2: Comparación entre las arquitecturas cliente-servidor y peer-to-peer Un concepto íntimamente relacionado con las redes P2P es el de supercapa (en inglés overlay). Una supercapa es una capa de aplicación virtual, o red lógica, que ofrece servicios normalmente no disponibles en la capa física subyacente[14]. Las redes P2P en realidad son supercapas de Internet formadas por las conexiones entre los peers. Las siguientes características se encuentran en la mayoría de redes P2P. Aunque a menudo no se cumplen de forma completa o absoluta. Conectividad Todos los nodos están interconectados. Descentralización El comportamiento de la red P2P está determinado principalmente por la acción colectiva de los peers. Simetría Los nodos asumen roles equivalentes. Participación libre Cada peer puede unirse y abandonar la supercapa cuando quiera. Escalabilidad Las supercapa puede llegar a tener millones de nodos debido a que la carga de trabajo se reparte de forma homogénea entre los peers. Estabilidad Las redes P2P toleran situaciones de churn. Churn es el término usado para referirse a la situación donde los peers de la supercapa se unen y abandonan la red con mucha frecuencia. Robustez El funcionamiento de las redes P2P no depende de la existencia de un nodo especial o punto vulnerable. 11 2.1.1. Descubrimiento de recursos Dentro de las redes P2P existen dos grandes categorías: estructuradas y no estructuradas. Lo que las diferencia es cómo los peers descubren los recursos en la supercapa. Las redes P2P no estructuradas se basan en supercapas aleatorias en las que los recursos son descubiertos haciendo backtracking, flooding o usando caminos aleatorios (random walks en inglés)[7]. Backtracking es un algoritmo que busca todas las soluciones posibles (o una parte) de algunos problemas computacionales. Flooding es un algoritmo de encaminamiento en el que cada nodo reenvía cada paquete recibido por todas sus conexiones menos por la que ha llegado. La figura 3 muestra la típica secuencia de mensajes de petición que se da en Freenet. El usuario inicia una petición de información en el peer A, el cual reenvía la petición a B, entonces B la reenvía a C. El peer C no puede reenviar la petición a ningún peer más y Figura 3: En Freenet, backtracking usado devuelve un error a B. El peer B prue- para encontrar un recurso ba su segunda opción, el peer E, el cual reenvía la petición a F, que reenvía a B. Entonces B detecta el bucle y devuelve un error de petición a F, el cual no es capaz de reenviar la petición a ningún peer más y la petición vuelve atrás, hacia E. Entonces el peer E prueba su segunda opción, el peer D, el cual tiene la información deseada. Finalmente, la información encontrada es devuelta a través de E, B y A. El uso de esta técnica, igual que con flooding y caminos aleatorios, es muy costoso porque las peticiones de contenido poco común tienen que ser reenviadas a un gran número de peers antes de encontrar el recurso deseado. Además, los peers tienen que procesar muchas peticiones de información aunque no tengan el recurso deseado. El uso de este tipo de técnicas hace que la topología de la supercapa sea totalmente aleatoria, sin ninguna forma en particular, de ahí el término «no estructurada». En estas redes, la información popular es fácil de encontrar, pero es probable que un peer no encuentre el recurso que busca si es muy raro. Debido a la 12 falta de estructuración, estas redes son fáciles de construir y resistentes a situaciones de churn[14]. Ejemplos de redes no estructuradas son Freenet[2], Gnutella, y FastTrack. (a) Topología de una red P2P no estructu- (b) Topología de una red P2P estructurada rada (distributed hash table) Figura 4: Comparación toplógica entre una red P2P no estructurada y una red P2P estructurada Una red P2P estructurada es una supercapa en la que los peers mantienen la información de encaminamiento y localización de los recursos de forma cooperativa. Esta estrategia normalmente asegura que los recursos son encontrados en un número de pasos acotado superiormente aunque sean extremadamente raros. La mayoría de este tipo de redes usan DHT. Una DHT consiste en una tabla con registros <clave,valor>que contiene la información de contacto de todos los nodos de la red. El espacio de claves, típicamente de 160 bits, es repartido entre los nodos de la red, y cada uno de ellos es responsable de guardar una parte de la tabla. Gracias a las DHT, los nodos participantes pueden encontrar cualquier recurso identificado con una clave. Ejemplos de DHT son Content Addressable Network (CAN)[11], Tapestry[17], Chord[15], Pastry[12], Kademlia[9] y Viceroy[8]. Un estudio de 2013 sobre el uso de Mainline DHT (variante de Kademlia que usa BitTorrent) durante los dos años y medio anteriores estima que el número de usuarios diarios se encuentra entre 15 y 27 millones, revelando una situación de churn diario de al menos 10 millones[16]. En la tabla 1 se puede ver una comparación entre las características de las redes P2P estructuradas y las redes P2P no estructuradas[1]. 13 P2P no estructurado Construcción de la supercapa Recursos Mensajes de petición Localización Coste de búsqueda Coste de mantenimiento de la supercapa Tolerancia a churn y fallos Aplicabilidad P2P estructurado alta flexibilidad baja flexibilidad indexados localmente backtracking, flooding caminos aleatorios probable impredecible bajo indexados en una DHT número de reenvíos acotado garantizada acotado moderado y alta pequeña escala y redes altamente dinámicas moderada gran escala y redes relativamente estables Tabla 1: Comparación entre las características de las redes no estructuradas y las redes estructuradas 2.1.2. Aplicaciones Las aplicaciones más importantes que se le ha dado a las redes P2P son las siguientes: Intercambio de ficheros. Es el uso de P2P más extendido. El protocolo más popular es BitTorrent (http://www.bittorrent.org). P2PTV. Son aplicaciones diseñadas para retransmitir vídeo en tiempo real sobre redes P2P. El contenido retransmitido normalmente son canales de televisión alrededor de todo el mundo. StreamRoot (https://www.streamroot. io) es un ejemplo. Osiris Serverless Portal System. Es un sistema que permite la publicación y mantenimiento de sitios webs sobre redes P2P. Este tipo de webs son inmunes a los ataques de denegación de servicio (http://www.osiris-sps.org). Invisible Internet Project (I2P) es una supercapa P2P de Internet que proporciona cierto grado de anonimato a sus usuarios (https://geti2p.net). Sistemas de monedas criptográficas digitales como Bitcoin[10] y Peercoin[6] funcionan sobre redes P2P. 14 2.2. Historia del intercambio de ficheros La historia del intercambio de ficheros ha pasado por diversas épocas marcadas principalmente por los sistemas que se han utilizado. Aunque el intercambio de ficheros y las redes P2P actualmente son dos conceptos estrechamente relacionados, no siempre ha sido así. A continuación se hace un breve resumen de estas épocas. Bulletin Board System (década de los 70) El Bulletin Board System (BBS) es considerado en gran parte el inicio del intercambio de ficheros digitales tal y como lo conocemos hoy en día. BBS es un servidor que permite a usuarios conectarse al sistema usando un terminal. Una vez dentro del sistema, el usuario puede realizar una serie de operaciones como subir y descargar software y ficheros, leer noticias y boletines, e intercambiar mensajes con otros usuarios vía correo electrónico y chat. Hoy en día todavía existen algunos BBS en funcionamiento. Figura 5: AmiExpress (1992). Aplicación BBS para Commodore Amiga Warez scene (década de los 70) Warez scene es una comunidad clandestina y oculta al gran público, especializada en la distribución de material con derechos de autor, incluyendo programas y series de televisión, películas, música, videoclips, videojuegos, aplicaciones, ebooks y pornografía. Empezó a emerger en la década de los 70. Inicialmente usaban BBS privados para publicar su material, pero posteriormente pasaron a usar los topsites, servidores File Transfer Protocol (FTP) clandestinos, altamente secretos y con gran ancho de banda capaces de transferir un Blu-ray en cuestión de segundos y almacenar terabytes de información. En esta comunidad primero los release groups publican cierto contenido multimedia en un topsite y lo anuncian a través de Internet Relay Chat (IRC). Una vez el material ha sido publicado, los courier groups, que tienen acceso a los topsites transportan las publicaciones de un topsite a otro topsite, normalmente usando FlashXP. Con esta operación, los courier groups obtienen el derecho a descargar 15 tres veces lo que han subido, siempre y cuando el contenido sea original. De esta forma, los courier groups compiten por ser los primeros en transportar las publicaciones entre topsites. Gracias a la naturaleza descentralizada de este sistema, el sistema de recompensas y la posibilidad de partir los ficheros en trozos mediante RAR, las publicaciones rápidamente están disponibles para toda la comunidad de warez scene. Sin embargo, debido al alto hermetismo de este tipo de distribución, es difícil para muchos usuarios conseguir acceso a los topsites. Hoy en día siguen existiendo algunos. Usenet: los orígenes de la descentralización (finales de los 70) Usenet es un sistema mundial distribuido de debate y discusión. Sus usuarios pueden leer y enviar mensajes (llamados articles o posts, y colectivamente llamados news) según categorías, conocidas como newsgroups. Los servidores redistribuyen los mensajes a otros servidores creando múltiples copias. Usenet es el precursor de los foros web que son ampliamente usados en la actualidad y puede ser visto como un híbrido entre gestor de correo electrónico y foro web. La mayor diferencia entre BBS y Usenet es la ausencia de un servidor o administrador central. Aunque Usenet ha existido desde finales de los 70, la práctica de intercambio de ficheros no se extendió hasta mucho más tarde. En 1993, Eugene Roshal creó la tecnología RAR, la cual permitía a los usuarios partir Figura 6: Jerarquía de los ficheros en trozos y posteriormente reconstruirlos. warez scene Gracias a la naturaleza descentralizada de Usenet y a RAR, la velocidad y la eficiencia de la distribución de ficheros aumentó muchísimo permitiendo no tener que redistribuirlos íntegramente en caso de errores. Usenet aún se usa en la actualidad. Sin embargo, se usa mayoritariamente para el intercambio de ficheros antes que para su propósito original, el cual ha sido desplazado por los foros web y IRC. IRC (1988) IRC fué creado por Jarkko Oikarinen en agosto de 1988 para reemplazar un programa de un BBS cuyo propósito original era la mensajería instantánea, pero durante la década de los 90 se popularizó su uso como medio de intercambio de ficheros. Hoy en día, IRC todavía es ampliamente usado para su propósito original de chat. También se usa como mecanismo de impulso o punto 16 de arranque para algunos sistemas de intercambio de ficheros. Hotline (1997) Durante un periodo breve de tiempo Hotline fué un medio de intercambio de ficheros muy popular que usaba una arquitectura clienteservidor. Al principio, Hotline tuvo mucho éxito, sin embargo su uso rápidamente perdió fuerza debido a varias complicaciones, entre ellas el cifrado del código fuente por parte del fundador, Adam Hinkley, antes de abandonar la compañía. Hacia finales de los 90, Hotline empezó a desvanecerse, coincidiendo con la aparición de otros sistemas emergentes. Hoy en día, su uso es casi inexistente. Figura 7: Una red Usenet con servidores y clientes. Los puntos azules, verdes y rojos de los servidores representan los grupos que gestionan. Las flechas entre servidores indican intercambios de información entre grupos de discusión (feeds). Las flechas entre clientes y servidores indican que un usuario está suscrito a un grupo. Napster (1999) Naspter llevó el intercambio de ficheros, en concreto MP3, a las grandes masas. Existía un servidor central que los usuarios consultaban para encontrar los peers que tenían el fichero MP3 que querían. Entonces los peers se ponían en contacto e intercambiaban el fichero íntegramente. El sistema era P2P, aunque también tenía una parte centralizada ya que dependía del servidor donde se encontraba la base datos que controlaba qué canciones tenía cada peer . En el año 2000 la compañía discográfica estadounidense A&M Records y otras compañías demandaron a Napster a través de la Recording Industry Association of America (RIAA) por vulnerar derechos de autor protegidos por la Digital Millennium Copyright Act (DMCA). El tribunal encargado del caso sentenció que Napster era culpable y fue obligado a restringir el acceso a todo el material presente en su red que vulnerase esos derechos, pero no fue capaz y tuvo que detener el servicio en julio de 2001. Al año siguiente, en 2002, Napster entró en bancarrota y fue vendida a otra compañía. 17 Diversificación de los sistemas P2P (a principios del 2000) Durante el litigio entre Napster y la RIAA en el año 2000, aparecieron los sistemas eDonkey2000, Gnutella y Freenet. eDonkey2000 podía intercambiar ficheros de gran tamaño, pero también dependía de un servidor central como Napster. Este sistema fue el primero en aplicar el concepto de swarming, que permitía descargar trozos del fichero de distintos peers simultáneamente para acelerar la descarga. Gnutella era un sistema que no necesitaba ningún servidor central, por lo que no tenía un punto vulnerable y era más difícil detener sus servicios. Freenet también carecía de servidor central, pero además ofrecía un mayor grado de anonimato a sus usuarios que los sistemas anteriores. En 2001, se publicó el protocolo FastTrack cuya implementación más popular fue Kazaa. Este protocolo no era totalmente descentralizado como Gnutella y Freenet, ya que existían unos supernodos cuya función era aumentar la eficiencia de la red. El cierre de Napster en 2001 propició que muchos de sus usuarios se pasaran a otros sistemas P2P y el uso de este tipo de redes siguió creciendo. Audiogalaxy (1998) creció en popularidad, y el protocolo BitTorrent y la aplicación LimeWire (Gnutella) fueron publicados. En 2002, la RIAA demandó a Audiogalaxy y consiguió detener sus servicios. Hasta su decadencia en 2004, Kazaa fue la aplicación de intercambio de ficheros más popular a pesar de sus litigios en los Países Bajos, Australia y los Estados Unidos. Debido a la dificultad de acabar con los sistemas P2P descentralizados, en 2002, la RIAA comenzó a demandar a los usuarios de Kazaa gracias a que los Internet service provider (ISP) o proveedores de servicio a Internet facilitaban su identidad y se produjeron las primeras multas a usuarios. Durante 2002 y 2003 se crearon múltiples servicios web que permitían a los usuarios de BitTorrent introducirse en sus redes, incluyendo Suprnova.org, isoHunt, TorrentSpy y The Pirate Bay. Este último sufrió redadas en 2006 y 2014. En 2005, la compañia desarrolladora de eDonkey2000, MetaMachine, fue obligada a dejar de ofrecer su software como resultado de una demanda impuesta por la RIAA. Ese mismo año, la aplicación BitTorrent Azureus (ahora conocida como Vuze) fue la primera en implementar DHT dentro de la comunidad BitTorrent, hecho que le dió mucha popularidad a este tipo de redes ayudando a que se acabaran convirtiendo en las redes P2P más usadas a nivel mundial. Sistemas de alojamiento de archivos (principios y mediados de los 2000) Paralelamente a la difusión de los sistemas P2P, algunos sistemas de alojamiento de archivos se hicieron muy populares, como Megaupload, Rapidshare y Hotfile (entre otros). La gran ventaja de estos sistemas era su gran facilidad de uso. 18 Los usuarios solo tenían que subir archivos a un servidor y compartir la Uniform Resource Locator (URL) con otros usuarios para que los descargaran mediante Hypertext Transfer Protocol (HTTP). Normalmente, las URL eran compartidas en foros de Internet. Era práctica común premiar a aquellos usuarios cuyos enlaces eran más veces descargados, así que era habitual partir los ficheros en trozos como en la época de los topsites y Usenet, solo que en este caso por motivos estrictamente de provecho. Estos sistemas también fueron objetivo de gobiernos y otras organizaciones como RIAA y Motion Picture Association of America (MPAA). El caso más famoso fue el cierre de Megaupload y arresto de su fundador Kim Dotcom el 19 de enero de 2012. Justo un año después, el 19 de enero de 2013, Kim Dotcom lanzó un nuevo servicio de alojamiento de archivos llamado Mega. 2.3. 2.3.1. BitTorrent Funcionamiento BitTorrent es un protocolo P2P diseñado para el intercambio de ficheros (normalmente de gran tamaño). BitTorrent crea una supercapa no estructurada sobre Internet. Usa la técnica de swarming como ya hacía la red eDonkey, es decir, parte la información que se quiere compartir en piezas pequeñas (256 KiB - 4 MiB) de igual tamaño y los peers de la red descargan y suben estas partes simultáneamente de y hacia múltiples peers acelerando enormemente la descarga. A diferencia de los sistemas de intercambio de ficheros anteriores, BitTorrent no crea una red única donde se comparten todos los ficheros en el mundo, sino que crea una red separada por cada contenido o recurso distinto (estos recursos únicamente identificables reciben el nombre de torrents). En una red BitTorrent participan dos tipos de nodos: los peers y el tracker . Los peers son los encargados de distribuir las piezas por la red, y el trac- Figura 8: Funcionamiento de una red BitTorrent que comparte un torrent de 5 piezas. En la red participan 4 peers (tres leechers y un seeder ) y un tracker . En verde, las piezas que tiene cada peer . 19 ker simplemente registra y coordina a los peers participantes. El conjunto de todos los peers de una red se llama enjambre o swarm. Los peers normalmente son clasificados como seeders o leechers. Los primeros son peers que tienen todas las piezas del torrent, mientras que los segundos son peers que aún les faltan piezas por descargar. Esta distinción es solo teórica ya que a efectos prácticos todos los peers de la red tienen el mismo rol y capacidades. Los peers solo se comunican con el tracker cuando quieren unirse a la red o quieren pedir más peers con los que intercambiar piezas. Cuando un peer quiere unirse a una red BitTorrent, lo primero que hace es descargar un fichero .torrent. Este fichero contiene los metadatos esenciales para el funcionamiento de la red. Además, estos metadatos identifican de forma única al torrent. Entre otras cosas, contienen la dirección URL del tracker , la colección de ficheros que forman el torrent, el tamaño de las piezas y el resumen de cada una de ellas, necesario para verificar su integridad. Una vez el peer tiene el fichero .torrent, conecta con el tracker mediante HTTP para «anunciarse», es decir, para que el tracker sepa que hay un nuevo peer que quiere participar en la red. Acto seguido, el tracker devuelve una lista con la información de contacto de otros Figura 9: Perspectiva del peer local en el peers (dirección Internet Protocol (IP) enjambre. y puerto) para que pueda conectarse con ellos e intercambiar piezas. Estos ficheros .torrent normalmente se encuentran en páginas web que actúan como directorios de torrents. En la figura 8 se puede ver un ejemplo de cómo funciona el swarming en BitTorrent. El peer B, que solo tiene las piezas 1 y 4, descarga la pieza 3 del peer C. Éste último, al mismo tiempo está descargando las piezas 2 y 4 de los peers D y A, respectivamente. Por último, el peer D descarga las piezas 1 y 3 de B y A, respectivamente. Es importante recalcar que un peer cualquiera normalmente no conoce a todos los peers del enjambre, ya que suelen ser más que el número de conexiones simultáneas que el sistema del peer local puede soportar. Pero técnicamente hablando, es posible que un peer pida al tracker todos los peers del enjambre. Otra cuestión 20 es que sea capaz de abrir una conexión con cada uno de ellos simultáneamente. Y si es capaz, que el ancho de banda de su conexión a Internet permita recibir paquetes de información de todas las conexiones a la vez. Los enjambres pueden llegar a ser realmente grandes. Por ejemplo, el enjambre más grande en la historia de BitTorrent se dio en 2014 con 193.000 peers. La figura 8 es útil para tener una visión global del funcionamiento de BitTorrent, pero no se corresponde con la perspectiva de un peer cualquiera. Lo que ve el peer local en realidad es una red como la de la figura 9 donde solo conoce a una parte del enjambre. Dicho de otra forma, cada peer conoce un grupo particular de vecinos. Estos vecinos pueden coincidir con los de otros peer , pero en principio es por pura casualidad. 2.3.2. Implementaciones existentes y últimos avances A lo largo de todos los años que diferentes aplicaciones BitTorrent han estado en funcionamiento, la comunidad BitTorrent ha propuesto nuevos BEP que amplían las funcionalidades del protocolo. Algunas propuestas han sido tan bien valoradas por la comunidad que se han convertido en práctica de facto, incluso teniendo solamente estatus de borrador o aceptadas, sin llegar a ser definitivas (ver figura 1). Estos son los avances tecnológicos más significativos hasta la fecha. uTorrent Transport Protocol (uTP) (BEP29) Es un protocolo implementado sobre User Datagram Protocol (UDP) y diseñado para sustituir a Transmission Control Protocol (TCP) y así evitar sus problemas inherentes de congestión y al mismo tiempo mantener la fiabilidad y el orden de la información. Magnet Uniform Resource Identifier (URI) (BEP9) El esquema Magnet URI permite a los peers unirse a la red sin descargar el fichero .torrent. DHT (BEP5) Con las DHT los peers son capaces de localizar los recursos de la red sin consultar al tracker . Peer Exchange (PEX) A pesar de que su uso es extendido, no existe propuesta oficial. Permite que un peer conozca otros peers a través de sus vecinos. Sigue siendo necesario consultar inicialmente al tracker para unirse a la red. Web seeding (BEP17, BEP19) Permite a los peers descargar el fichero de un servidor HTTP/FTP (además de la opción P2P). Tracker UDP Permite realizar la comunicación entre los peers y el tracker sobre UDP. Disminuye hasta un 50 % el tráfico entre los peers y el tracker . 21 Descubrimiento de peers locales No existe propuesta oficial. Es una forma de descubrir otros peers que se encuentran en la local area network (LAN) o red de área local del peer aprovechando su gran ancho de banda y al mismo tiempo minimizando el tráfico que sale hacia Internet. Super-seeding (BEP16) Es una técnica que ayuda a los leechers a que se conviertan en seeders más rápidamente. Es útil cuando solo hay un seeder en la red. Multitracker (BEP12) Permite el uso de múltiples trackers coordinados (y de repuesto, en caso de que uno falle). Actualmente existen dos librerías que son usadas por aplicaciones BitTorrent: libtorrent es una librería multiplataforma de código abierto escrita en C++. Su primera publicación fue en 2005. Las características implementadas más notables son DHT, uTP, Web seeding y PEX. Algunas aplicaciones implementadas con esta librería son Deluge, Tribler, qBittorrent, Free Download Manager, BitLord, rTorrent y el videojuego World of Tanks. MonoTorrent es otra librería multiplataforma de código abierto escrita en C# basada en Mono (una implementación de .NET Framework). Su primera publicación fue en 2010. Moonson es un aplicación basada en esta librería. Además, también existen otras aplicaciones con sus propias implementaciones como BitTorrent(Mainline) (C++), uTorrent (C++), Vuze (Java), Transmission (C, Objective-C), WebTorrent (JavaScript, WebRTC), Shareaza (C++), Tixati (C++), BitComet (C++) y MLDonkey (OCaml, C y ensamblador). Actualmente no existe ninguna implementación BitTorrent en Pharo de la que se pueda aprovechar su código para el proyecto. Pero sí que pueden ser útiles otras implementaciones como las mencionadas arriba a la hora de tener referencias sólidas y fiables sobre cómo diseñar la aplicación. 3. 3.1. Metodología Seguimiento En el desarrollo del software se usará una metodología incremental, de abajo arriba, partiendo de los sistemas más pequeños e independientes, creando otros sistemas más grandes y complejos en forma de prototipos. Se iterará este ciclo hasta producir el prototipo final. Como mínimo, se celebrará una reunión con el director del proyecto tras la finalización de cada prototipo para evaluar el estado de la 22 aplicación. El director del proyecto también podrá supervisar en todo momento el estado del software a través de http://smalltalkhub.com, un repositorio online para proyectos Smalltalk, donde se irán actualizando las diferentes versiones de la aplicación. 3.2. Estilo de programación En el desarrollo del software se seguirán los patrones de diseño del libro Smalltalk Best Practice Patterns[5]. Además se hará énfasis en producir código que cumpla los siguientes principios generales de programación, que son especialmente útiles para este proyecto. Minimalismo Dividir las funcionalidades lo máximo posible para tener métodos pequeños y altamente reusables. Código autocomentado Elegir el nombre de las clases, métodos y variables de tal forma que el código sea fácilmente comprensible sin necesidad de comentarios explícitos. 3.3. Validación del software El software será validado mediante pruebas unitarias. Estas pruebas unitarias serán automatizadas para que las pueda reproducir cualquier usuario fácilmente. Además se incluirán otras pruebas sobre el funcionamiento general de la aplicación. Las instrucciones para ejecutar estas pruebas se encuentran en el apartado A.4 de la adenda. 4. Visión global de la aplicación En este apartado se hace un resumen en alto nivel del diseño y el funcionamiento de la aplicación con el objetivo de dar una visión global introductoria. El método a utilizar en la explicación va a ser empezar por la clase que representa el sistema más grande, es decir, la que representa la aplicación en sí, y a continuación explicar superficialmente los subsistemas que contiene. La clase principal es BtLocalPeer, que representa la aplicación BitTorrent que ejecuta el peer local. Puede parecer de entrada que el nombre escogido no es el apropiado para representar la aplicación, pero como se explica en los objetivos generales del proyecto, lo que se persigue es representar virtualmente, dentro de Smalltalk, la naturaleza de las redes BitTorrent. Y esta naturaleza se basa en objetos (peers) que interaccionan entre sí a través de mensajes, por lo que se ha 23 decidido llevar también este enfoque a una elección de nombres que ayude a ver estas redes como objetos con entidad propia que se comunican. Como se pretende que solo pueda existir una sola aplicación al mismo tiempo, esta clase es de instancia única. Esta clase contiene una colección de objetos BtTorrent. Un objeto BtTorrent representa la tarea de descarga y compartición de un torrent cualquiera. La razón para que BtLocalPeer contenga una colección de torrents es poder realizar operaciones globales sobre todos los torrents, como iniciarlos y detenerlos todos a la vez, o imponer un límite global de conexiones simultáneas en la aplicación. Figura 10: Diagrama de clases simplificado de la aplicación El primero de los elementos que contiene BtTorrent es un objeto BtMetainfo, que representa los metadatos contenidos en el fichero .torrent, sin el cual no es posible unirse a la red e intercambiar piezas con otros peers. El siguiente objeto que contiene es una colección de objetos BtFile. Cada torrent está compuesto por una serie de ficheros cuyas rutas y tamaños aparecen en los metadatos. De hecho, el torrent es considerado como un único fichero, resultado de la concatenación de los ficheros mencionados en el orden en que aparecen en los metadatos. Esta colección de objetos BtFile lo que permite es acceder a las piezas de este gran fichero de forma totalmente independiente a los ficheros que lo componen, sin importar cuantos ficheros haya implicados. Durante la compartición del torrent también es necesario llevar un control de las piezas que tiene cada peer del vecindario (tanto del local como de los remotos). Esta funcionalidad es necesaria porque, de no llevar este control, el peer local no 24 sabría qué piezas pedir a otros peers. De esto se encarga la clase BtBitfield, que es un campo de bits tan grande como piezas haya en el torrent. Cada objeto BtTorrent contiene una instancia de esta clase ya que cada torrent es un conjunto de ficheros distinto. Este tipo de objetos también los contienen las instancias de la clase BtRemotePeer, que representa los peers remotos del vecindario, para los que también es necesario llevar un control de las piezas que tienen. Los objetos BtRemotePeer representan las conexiones del peer local con los peers remotos. Se podría haber elegido como nombre para estos objetos «BtConnection» o algo similar, pero, como en el caso de BtLocalPeer, se ha elegido el nombre que denota la naturaleza de objetos con los que se puede interaccionar. Cada BtTorrent tiene su propia colección de peers remotos (o conexiones). Otro tipo de objetos con los que interacciona el peer local son los trackers, representados por la clase BtTracker. Por cada torrent, siempre existe como mínimo un tracker que se encarga de introducir y dar a conocer los peers dentro del enjambre dedicado a un torrent concreto. Así que cada BtTorrent contiene un BtTracker como mínimo (también existe el concepto de multitracker, ver apartado 5.5.3). La unidad mínima de información útil del torrent no es la pieza, sino el bloque. Cuando los peers intercambian información útil (octetos del torrent), no intercambian piezas íntegras, sino que intercambian pequeñas porciones de estas piezas: bloques. Para no tener que estar accediendo constantemente al disco físico, estos bloques se guardan temporalmente en la memoria volátil del sistema. Una vez se dispone de todos los bloques de una pieza, entonces se valida calculando su resumen y se escribe en disco, y se libera la memoria volátil. Cada BtTorrent tiene una colección de objetos BtTemporaryPiece, donde se guardan los bloques temporales. Por último, los objetos BtTorrent también contienen una instancia de BtPiecesOnNetwork. La responsabilidad de esta clase es llevar un control de la «distribución» de las piezas en el vecindario. Dicho de otra forma, por cada pieza, cuántas copias hay en el vecindario. Además, también ofrece la posibilidad de ordenar las piezas según varios criterios. La necesidad y utilidad de este sistema se explica en el apartado 5.12. En el siguiente apartado se profundiza en el funcionamiento de la aplicación siguiendo el sentido inverso al que se ha usado en este apartado. Empezando por los subsistemas más pequeños e independientes, y terminando por los sistemas más complejos y dependientes hasta llegar a la aplicación en sí. Esta metodología es, precisamente, la que se ha utilizado en el desarrollo de la aplicación (de abajo arriba), y también es la más idónea para explicarla con un gran grado de detalle. 25 5. Diseño e implementación En este apartado se explica el diseño e implementación de la aplicación BitTorrent. Aunque el objetivo no es explicar en detalle el protocolo, sí que será necesario profundizar un poco en su funcionamiento en algunas partes para que se comprendan determinadas decisiones de diseño. 5.1. Bencoding Los metadatos del fichero .torrent vienen codificados en un sistema de codificación especial diseñado para BitTorrent que se llama bencoding. Los tipos de datos que soporta son cadenas de caracteres, enteros, listas y diccionarios. Las cadenas de caracteres se codifican en secuencias de octetos de la forma <longitud-de-la-cadena-en-base-decimal-ASCII>:<cadena>. Ejemplo: ‘spam’ → 4:spam Los enteros se codifican en secuencias de octetos de la forma i<longitud-del-entero-en-base-decimal-ASCII>e. Ejemplo: 345 → i345e Las listas se codifican en secuencias l<dato-codificado><dato-codificado>. . . e. Ejemplo: [‘spam’,345] → l4:spami345ee de octetos de la forma Los diccionarios se codifican en secuencias de octetos de la forma d<cadena-de-caracteres-codificada><dato-codificado><. . . ><. . . >e. Ejemplo: {‘a’ ⇒ 5,‘b’ ⇒ [‘as’,25]} → d1:ai5e1:bl2:asi25eee Como es necesario que la aplicación pueda leer y escribir estos metadatos, tiene que implementar dos operaciones: codificar los tipos de datos en una secuencia de octetos y decodificar una secuencia de octetos en los tipos de datos que corresponda. Los métodos encargados de llevar a cabo estas operaciones se han añadido a las clases estándar String, Integer, OrderedCollection, Dictionary, ByteArray y PositionableStream. Para codificar, las clases String, Integer, OrderedCollection y Dictionary tienen el método bencoded que codifica las instancias de esas clases en secuencias de octetos. En el siguiente ejemplo las secuencias de octetos resultantes se convierten a cadenas de caracteres (asString) para que los resultados sean legibles. 26 ’hello world’ bencoded asString. −> ’11:hello world’ 3567 bencoded asString. −> ’i3567e’ (OrderedCollection with: 3567 with: ’hello world’) bencoded asString. −> ’li3567e11:hello worlde’ d:=Dictionary new. d at: ’key1’ put: ’value1’. d bencoded asString. −> ’d4:key16:value1e’ Del mismo modo, la clase ByteArray tiene un método con el nombre bedecoded que implementa la función inversa. ’hello world’ bencoded bedecoded. −> ’hello world’ 3567 bencoded bedecoded. −> 3567 (OrderedCollection with: 3567 with: ’hello world’) bencoded bedecoded. −> an OrderedCollection(3567 ’hello world’) d:=Dictionary new. d at: ’key1’ put: ’value1’. d bencoded bedecoded. −> a Dictionary(’key1’−>’value1’ ) La clase ByteArray (como todas las subclases de Collection) tiene un incoveniente: si queremos decodificar varios datos seguidos necesitamos devolver, a parte del dato decodificado, la posición del array donde termina éste para decodificar el dato siguiente a partir de la siguiente posición. Inicialmente, la decodificación la hacía ByteArray pero posteriormente se trasladó esta funcionalidad a la clase PositionableStream (subclase de Stream) para evitar este inconveniente. Las instancias de Stream son wrappers de colecciones que permiten leer los elementos de la colección subyacente al mismo tiempo que avanza la posición de lectura hasta el elemento posterior al último leído. Por tanto, el método ByteArray>>bedecoded2 simplemente invoca al método PositionableStream>>bedecoded. ByteArray>>bedecoded ^self readStream bedecoded Entonces PositionableStream se encarga de decodificar el tipo de dato que corresponda: 2 Clase>>metodo es la forma de Pharo para referirse al método metodo de la clase Clase. 27 PositionableStream>>bedecoded elem := self peek asCharacter. ^ elem = $i ifTrue: [ self bedecodeInteger ] ifFalse: [ elem isDigit ifTrue: [ self bedecodeString ] ifFalse: [ elem = $l ifTrue: [ self bedecodeList ] ifFalse: [ elem = $d ifTrue: [ self bedecodeDictionary ] ifFalse: [ self error: ’Unknown bencoded form’ ] ] ] ] 5.2. Los metadatos Los metadatos, como ya se ha explicado en el apartado 2.3.1, contienen la información esencial para el funcionamiento de la red BitTorrent. En realidad, los metadatos son un diccionario «bencodificado» que contiene las siguientes claves: announce Cadena de caracteres codificada que representa la URL del tracker info Diccionario codificado que contiene las claves descritas abajo El diccionario info contiene las siguientes claves: name Cadena de caracteres que representa el nombre sugerido para el fichero o directorio compartido. piece length Entero que especifica el tamaño en octetos de las piezas en que es partido el torrent. La última pieza puede tener un tamaño diferente. pieces Concatenación de los resúmenes (o funciones hash) de las piezas. También hay otra clave dentro de info que puede ser length o files. Es length cuando el torrent es un solo fichero y su valor es un entero que representa el tamaño del fichero en octetos. Es files cuando el torrent es una colección de ficheros y su valor es una lista de diccionarios que contienen las siguientes claves: length Entero que representa el tamaño del fichero path Una lista de cadenas de caracteres que representa la ruta del fichero, empezando por el directorio más alto y acabando con el nombre del fichero 28 Cuando el torrent es una colección de ficheros, se trata como un solo fichero resultado de la concatenación de todos los ficheros en el orden en que aparecen en files. Cada torrent es identificado de forma única en Internet con el resumen del diccionario info. La clase BtMetainfo, subclase de Dictionary, representa los metadatos. Los metadatos de un torrent son nombrados en la documentación del protocolo como «metainfo», por eso se ha elegido este nombre y de ahora en adelante nos referiremos a los metadatos así. Respecto a la metainfo, solo hay dos situaciones posibles en una red BitTorrent: o bien que el creador de ésta sea el usuario de la aplicación porque quiere compartir unos ficheros de su sistema; o bien que el usuario quiera descargar unos ficheros cuya metainfo ha sido creada por otro usuario de la red. En el primer caso, la forma de generar la metainfo asociada a un fichero o directorio de ficheros del sistema es la siguiente. metainfo:= BtMetainfo new. metainfo buildOn: ’/myFiles/directoryToShare’ asFileReference . metainfo writeToFile: ’/myFiles/myMetainfo.torrent’ asFileReference. En cambio, en el segundo caso solo querremos leer el fichero .torrent y decodificar el diccionario que recibe BtMetainfo>>from: como argumento. metainfoFile:= ’/myFiles/someMetainfo.torrent’ asFileReference . metainfo:= BtMetainfo from: (metainfoFile readStreamDo:[:x | x binary ; bedecoded]). 5.3. Acceso físico a los torrents Los ficheros de un torrent vienen definidos en la metainfo, en ella aparecen sus rutas y el tamaño que cada uno debe tener al finalizar la descarga. Las rutas de los ficheros obviamente son relativas, ya que la ruta absoluta dependerá del sistema de ficheros donde se almacenen. Una aplicación BitTorrent debe tener un sistema de acceso físico a estos ficheros tanto para escribirlos en disco como para leerlos, ya que el peer local envía y recibe piezas a otros peers. El protocolo especifica que los ficheros que componen un torrent deben tratarse como un sólo fichero resultado de la concatenación de todos ellos en el orden en que aparecen en la metainfo. Así que es posible que un fichero empiece en una pieza determinada y termine en otra que se encuentra 4 piezas más allá, como pasa en el segundo fichero de la figura 11. Aunque también es posible que empiece y termine en la misma pieza si el fichero es lo suficiente pequeño. Por tanto, el sistema de acceso físico debe ser capaz de acceder a cualquier porción contigua de la información sin importar cuantos ficheros haya implicados. Este sistema se ha implementado en el paquete BitTalk-DataAccess y está compuesto por las clases 29 BtFile y BtFileCollection. Figura 11: Ejemplo de colección de ficheros de un torrent de 10 piezas y 4 ficheros. Las piezas tienen un tamaño de 512 KiB menos la última, de 264 KiB. La clase FileReference es una clase estándar de Pharo que sirve para representar rutas de ficheros. Esta clase dispone del método size que devuelve el tamaño del fichero en cada momento, pero en una descarga BitTorrent además es necesario saber su tamaño final esperado para saber en qué posición de la colección de ficheros hay que leer y escribir cada pieza. Por eso, la clase BtFile representa las instancias de ficheros que se espera que acaben teniendo un tamaño máximo. Esta clase contiene dos variables de instancia: reference (apunta a un objeto FileReference) y finalSize (apunta a un entero). La primera guarda la ruta y la segunda el tamaño máximo permitido. Este tipo de objetos son precisamente los que se especifican en la metainfo, puesto que inicialmente estarán vacíos y al finalizar la descarga habrán alcanzado el tamaño definido en la metainfo. La clase BtFileCollection (subclase de OrderedCollection) representa las instancias de colecciones ordenadas de ficheros, que es precisamente lo que son los ficheros especificados en la metainfo. Las instancias de esta clase tratan sus elementos como si fueran un solo fichero. Con los métodos readOffset:amount: y writeOffset:data: es posible escribir y leer cualquier porción de la concatenación de ficheros dado un offset y, o bien una cantidad de octetos solicitada en caso de lectura, o bien un ByteArray en caso de escritura. Estos métodos de acceso se encargan de abrir y cerrar solamente aquellos ficheros que hay que acceder para leer o escribir la secuencia de octetos solicitada. Esta clase también dispone del método allSha1SplitEvery: cuyo parámetro es un entero que define cada cuántos octetos de información se calculará el resumen de la colección de ficheros. Por ejemplo, este método lo utiliza BtMetainfo>>buildOn:pieceLength: para calcular los resúmenes de las piezas resultantes de un directorio de ficheros a partir del cual quiere construirse su metainfo. Hay que destacar que las clases BtFile y BtFileCollection no solo sirven para el protocolo BitTorrent, sino que pueden usarse para cualquier concatenación de ficheros que deba tratarse como una sola secuencia de octetos, por lo que la clase BtFileCollection podría ampliarse para que pudiera realizar otras operaciones que satisficieran otras necesidades a parte de las de BitTorrent. 30 5.4. Los nodos de la red Todos los nodos implicados en una red BitTorrent tienen en común que tienen un dominio (ya sea nombre o dirección IP) y un puerto. La clase encargada de representar a cualquier nodo de la red es BtNode y tiene como variables de instancia un BtDomain y un puerto. Pero no todos los nodos son iguales, existen peers y trackers. Ambos tipos comparten las características de BtNode pero cada uno tiene sus particularidades. Estas particularidades las representan las clases BtPeer y BtTracker, ambas subclases de BtNode. La clase BtPeer tiene dos subclases: BtLocalPeer y BtRemotePeer. El peer local es una instancia de BtLocalPeer. Los peers remotos que el local puede llegar a conocer y conectar con ellos son instancias de BtRemotePeer. Esta clasificación es necesaria porque la instancia (única) de BtLocalPeer representa a la aplicación BitTorrent del sistema local, y las instancias de BtRemotePeer representan las conexiones que tiene el peer local con los peers remotos. Se podría haber llamado a la primera clase «BtApplication» y a la segunda «BtPeerConnection» (o algo similar), pero la intención es respetar el «espíritu» de la orientación a objetos y elegir los nombres que ayuden a ver de forma clara que dentro de Smalltalk hay una serie de objetos interpretando el papel de los nodos de la red y que interaccionan entre sí. La clase BtTracker también tiene dos subclases: BtHTTPTracker y BtUDPTracker. Esta clasificación es necesaria ya que no todos los trackers funcionan de la misma forma, unos trabajan sobre HTTP y otros sobre UDP. Las instancias de BtHTTPTracker reciben consultas HTTP mientras que con las instancias de BtUDPTracker la interacción es radicalmente distinta. Todas estas diferencias son transparentes para el peer local ya que la forma de comunicarse con los trackers es invocando los métodos abstractos announce y scrape de BtTracker. Estos métodos realizan unas operaciones que son comunes de todos los trackers, independientemente del protocolo que usen. Este diseño nos permite crear una red BitTorrent virtual dentro Smalltalk que representa en tiempo real la red física conocida por el peer local. En esta red virtual los nodos de la red física se representan como objetos de Smalltalk capaces de interaccionar entre sí. Y todos los mensajes que viajan entre ellos se traducen en el envío de mensajes entre objetos dentro de Smalltalk. 5.5. 5.5.1. Descubrimiento de peers Tracker HTTP La forma principal de interacción entre los peers y el tracker viene definida en el BEP3. Esa parte del protocolo especifica que esta comunicación debe efectuarse 31 Figura 12: Jerarquía de clases de los nodos de la red. mediante consultas HTTP GET. Y detalla qué deben contener estas consultas y que información debe responder el tracker . Todas las clases implicadas directamente en este protocolo se encuentran en el paquete BitTalk-HTTPTrackerProtocol. Existen dos tipos de operaciones: announce y scrape. El significado de la operación announce es que el peer local comunica al tracker que quiere conocer cierta cantidad de peers remotos. Además, el peer local también puede querer informar de que se ha producido un evento concreto como la finalización de la descarga o el abandono de la red. Por tanto, existen dos tipos de información en esta operación: la petición del peer local y la respuesta del tracker . La petición es una consulta GET en la que se especifica una query string con una serie de campos y valores que le dan al tracker cierta información sobre el peer que realiza la consulta. En la query string aparecen, entre otros valores, el info_hash (el resumen del diccionario info que identifica al torrent), el puerto que usa el peer local para el tráfico BitTorrent, el número de peers remotos que desea y los octetos que le quedan por descargar hasta completar el torrent. En la siguiente URL aparece en negrita una posible query string: http://tracker.com/announce?port=60513&numwant=50&info_hash=. . . La clase que representa estos campos y valores es BtHTTPAnnounceQuery y sus variables de instancia son cada uno de los campos que deben o pueden rellenarse en una query string. El método asString convierte la instancia en una cadena de caracteres con la misma forma que la del ejemplo de arriba en negrita. La información relevante de la respuesta HTTP se encuentra en el cuerpo del mensaje (tras la línea en blanco) y es un diccionario «bencodificado». En la clave peers de este diccionario se encuentra una lista de diccionarios cuyas claves son 32 peer id, ip y port. Pero esta forma de expresar peers se ha quedado obsoleta de facto, puesto que se suele usar por defecto otra forma más compacta para expresarlos (BEP23). Esta mejora consiste en sustituir el diccionario de peers por una concatenación de grupos de 6 octetos, donde los 4 primeros octetos expresan la dirección IP y los 2 últimos el puerto. Así, el peer local puede intentar conectar con los peers con esas direcciones. Como esta información es un diccionario, puede representarse con un Dictionary, en concreto con la subclase BtHTTPAnnounceResponseBody. Todas las instancias de BtTracker tienen una variable para guardar una petición de announce (announceRequest) y otra para guardar una respuesta de announce (announceResponse). La clase BtTracker tiene el método abstracto announce implementado por su subclase BtHTTPTracker: 1 2 3 4 BtHTTPTracker>>announce httpGET := self newGET: path query: announceRequest asString . response:= self sendGET: httpGET . self updateAnnounceResponse: response. En el código de arriba se pueden ver todos los pasos de la operación announce. En la línea 2 se forma el mensaje GET a partir de la query string. En la línea 3 el método sendGET: envía la petición y devuelve la respuesta del tracker . Y en la línea 4 el método updateAnnounceResponse: recupera el diccionario «bencodificado» del cuerpo de la respuesta, lo decodifica para obtener un BtAnnounceResponseBody y lo guarda en la variable de instancia announceResponse. La operación scrape, sin embargo, tiene un significado distinto. En este caso el peer local no quiere darse a conocer ni conocer otros peers, sino que desea conocer las estadísticas de ciertos torrents que el tracker conoce. El funcionamiento es el mismo que el de la operación announce, así que el diseño también es el mismo. Pero en este caso, la query string solo está formada por los valores info_hash de los torrents sobre los que el peer local quiere conocer sus estadísticas. Un posible ejemplo sería este: http://tracker.com/scrape?info_hash=. . . &info_hash=. . . &info_hash=. . . La clase que representa este tipo de query string es BtHTTPScrapeQuery, la cual tiene una variable de instancia que es una colección de info_hash. El tracker responde un diccionario «bencodificado» con las estadísticas de cada torrent (el nombre, número de seeders, número de leechers y números de veces que ha registrado descargas completadas). Esta información se representa con la clase BtHTTPScrapeResponseBody, subclase de Dictionary. Las instancias de BtTracker también tienen dos variables para guardar la petición (scrapeRequest) y respuesta (scrapeResponse) de scrape. Del mismo modo que con la operación announce, la clase BtHTTPTracker implementa el método abstracto scrape: 33 BtHTTPTracker>>scrape httpGET := self newGET: self asString asZnUrl asZnUrlScrape pathPrintString query: scrapeRequest asString. response := self sendGET: httpGET. self updateScrapeResponse: response Como se puede ver, esta implementación es análoga a la del método announce. 5.5.2. Tracker UDP Aunque el núcleo de BitTorrent (BEP3) especifica solamente el funcionamiento de los trackers sobre HTTP, la realidad es que una aplicación BitTorrent que solo implemente este tipo de comunicación no podrá unirse a la mayoría de las redes presentes en Internet. La gran mayoría de los ficheros .torrent que existen tienen trackers UDP en su metainfo. Y la tendencia es usar cada vez más UDP en detrimento de HTTP. Esta propuesta de ampliación del protocolo (BEP15) se hizo para evitar el overhead que tiene TCP debido a la necesidad de abrir y cerrar una conexión, y asegurar el orden y la fiabilidad de la información transferida. Usando el protocolo HTTP (que usa TCP), en una petición announce con una respuesta de 50 peers, el número de octetos enviados y recibidos son alrededor de 1206. El protocolo UDP, en la misma operación, usa 4 paquetes y alrededor de 618 octetos, reduciendo el tráfico casi un 50 %. Para un peer este ahorro puede ser insignificante, pero para un tracker que sirve a millones de peers supone una gran diferencia. En este protocolo existen cuatro tipos de operaciones: connect, announce, scrape y error. La primera operación que se realiza es la operación connect. En esta operación el tracker envía un identificador de conexión (connectionID) al peer , el cual deberá utilizar en todas las operaciones posteriores. Cada vez que el tracker recibe una petición, comprueba el connectionID y, si no coincide con el que le suministró, ignora la petición. El peer tiene permitido usar este identificador de conexión hasta un minuto después de recibirlo y los trackers solo pueden responder a peticiones con identificadores de conexión de hasta dos minutos de vida. En caso de que una conexión expire, el peer debe realizar una operación connect de nuevo. Las operaciones announce y scrape tienen el mismo significado que en el protocolo HTTP y la operación error solo la utiliza el tracker para comunicar al peer que se ha producido un error y porqué. UDP no es un protocolo fiable. Esto significa que no retransmite los paquetes perdidos por sí solo, sino que es la aplicación la responsable de ello. Siguiendo esta idea, si el peer no ha recibido una respuesta después de 15 · 2n segundos, debe retransmitir la petición, donde n empieza siendo 0 y se incrementa después de cada intento hasta llegar a 8 (3840 segundos). 34 Las clases implicadas en este protocolo se encuentran en el paquete BitTalk-UDPTrackerProtocol. Todas las operaciones se llevan a cabo con el intercambio de paquetes UDP entre el peer y el tracker , y estos paquetes se han representado en la clase BtUDPPacket. Todos estos paquetes tienen en común que contienen un identificador único de operación (id) y un número que establece el código o tipo de operación (actionCode), ambos representados con enteros de 32 bits. Los códigos de operación son: 0 - connect, 1 - announce, 2 - scrape y 3 - error. La combinación de estos dos números se ha representado en la clase BtUDPTransaction cuyas variables de instancia son id y actionCode. Y todas las instancias de BtUDPPacket tienen una variable de instancia transaction que apunta a un objeto BtUDPTransaction. Cada operación consta de un paquete de petición (request) enviado por el peer y de un paquete de respuesta (response) enviado por el tracker (excepto la operación error, que solo tiene paquete de respuesta). Todos los paquetes request tienen en común que contienen un número de conexión (connectionID) además de la información de BtUDPPacket por lo que los paquetes request son representados en la clase BtUDPConnectRequest (subclase de BtUDPPacket) con una variable de instancia connectionID. Si el paquete request en cuestión es de la operación connect entonces no contiene ninguna información más a parte del número de conexión. En cambio, si el paquete es de la operación announce o scrape, tiene cierta información particular. Esta información se representa en las clases BtUDPAnnounceRequest y BtUDPScrapeRequest, ambas subclases de BtUDPConnectRequest. La información particular de estos dos últimos tipos de paquetes es la misma que contienen BtHTTPAnnounceQuery y BtHTTPScrapeQuery, respectivamente. Por otro lado, se encuentran los paquetes response de cada operación. En este caso, estos paquetes no comparten información a parte de la de BtUDPPacket así que simplemente cada tipo de repuesta se representa con una de las clases BtUDPConnectResponse, BtUDPAnnounceResponse, BtUDPScrapeResponse y BtUDPErrorResponse, todas ellas subclases de BtUDPPacket. En el caso particular de BtUDPScrapeResponse, la respuesta contiene 3 enteros de 32 bits por cada torrent consultado que informan del número de seeders, el número de veces que el tracker ha registrado el evento de descarga completada y el número de leechers (en este orden). En total, el tracker responde una secuencia de 3n enteros donde n es el número de torrents consultados. La clase BtUDPScrapeTorrentStat representa la estadística de un torrent que devuelven los trackers UDP y tiene las variables de instancia seeders, completed y leechers. La clase BtUDPScrapeResponse, por tanto, contiene una colección de BtUDPScrapeTorrentStat en la variable de instancia scrapeTorrentStatCollection. 35 Figura 13: Jerarquía de clases de los paquetes UDP. Todas las clases explicadas hasta ahora sirven para representar la información de las operaciones como objetos dentro de Smalltalk, pero esta información viaja por la red como secuencias de octetos, así que es necesario un sistema de codificación y decodificación que permita transformar estos objetos en secuencias de octetos y viceversa. En primer lugar, la clase BtUDPPacket, todas sus subclases y las clases BtUDPTransaction y BtUDPScrapeTorrentStat tienen el método asByteArray que convierte la información que representan en una secuencia de octetos tal y como especifica el protocolo. En el siguiente trozo de código se puede ver la implementación del método asByteArray en la clase BtUDPTransaction. BtUDPTransaction>>asByteArray ^(actionCode asByteArrayOfSize: 4) , (id asByteArrayOfSize: 4) Este método lo invoca a su vez el método BtUDPPacket>>asByteArray ya que todos los paquetes UDP contienen como mínimo la información de una operación (transaction). BtUDPPacket>>asByteArray ^transaction asByteArray En el siguiente trozo de código se puede ver la implementación de BtUDPConnectRequest>>asByteArray. Como todos los paquetes request comparten la información de BtUDPPacket, este método invoca al método asByteArray de la superclase y concatena el resultado con la información específica de este tipo de paquetes en el orden que establece el protocolo. BtUDPConnectRequest>>asByteArray ^ (connectionID asByteArrayOfSize: 8) , (super asByteArray) Análogamente, el método BtUDPAnnounceRequest>>asByteArray invoca al método de la superclase BtUDPConnectRequest porque todas las peticiones de announce comparten la información de BtUDPConnectRequest. Una vez tiene la 36 información de la superclase en forma de octetos, los concatena con la información específica de este tipo de paquetes en el orden que determina el protocolo. BtUDPAnnounceRequest>>asByteArray ^ super asByteArray , ( infoHash asByteArrayOfSize: 20) , ( peerID asByteArrayOfSize: 20) , [...] ( numWant asByteArrayOfSize: 4) , ( node port asPortNumber ) . Las implementaciones de asByteArray de los demás tipos de paquetes que no aparecen aquí siguen el mismo diseño. Hemos visto que las implementaciones de asByteArray convierten la información de los paquetes UDP en secuencias de octetos, pero también es necesaria la función inversa: convertir las secuencias de octetos procedentes del tracker en instancias de las clases BtUDPConnectResponse, BtUDPAnnounceResponse, BtUDPScrapeResponse o BtUDPErrorResponse. El diseño de esta funcionalidad es idéntico al del bencoding (ver apartado 5.1); el método ByteArray>>decodeUDPResponse invoca a PositionableStream>>decodeUDPResponse, ByteArray>>decodeUDPResponse ^self readStream decodeUDPResponse que se encarga de decodificar el tipo de respuesta en función del código de operación: PositionableStream>>decodeUDPResponse actionCode:= (self next: 4) asInteger. 4 timesRepeat: [ self back ]. actionCode = (BtUDPTransaction announceActionCode) ifTrue:[^self decodeUDPAnnounceResponse]. actionCode = (BtUDPTransaction connectActionCode) ifTrue:[^self decodeUDPConnectResponse]. actionCode = (BtUDPTransaction scrapeActionCode) ifTrue:[^self decodeUDPScrapeResponse]. actionCode = (BtUDPTransaction errorActionCode) ifTrue:[^self decodeUDPErrorResponse]. La clase BtUDPTracker representa la «conexión UDP» del peer con el tracker . Aunque también puede interpretarse como la representación del tracker UDP físico dentro de Smalltalk, con el que el peer local puede interaccionar a través de los métodos announce y scrape. Los objetos principales que contiene una instancia de BtUDPTracker son una instancia de la clase Socket en la variable de instancia socket y una instancia de BtUDPTransactionQueue en la variable de instancia transactionQueue. 37 La variable socket se inicializa como un socket UDP que el peer local puede utilizar para enviar y recibir paquetes con los métodos sendPacket: y receivePacket. BtUDPTracker>>sendPacket: aPacket socket sendData: aPacket asByteArray toHost: domain address asIPv4 port: port. BtUDPTracker>>receivePacket packetSize := 0. bufferIn := ByteArray new: 2000. packetSize := socket receiveDataInto: bufferIn fromHost: domain address asIPv4 port: port ]. packet := (bufferIn copyFrom: 1 to: packetSize) decodeUDPResponse. ^ packet La clase BtUDPTransactionQueue es una cola (subclase de SharedQueue) en cuyas instancias el peer puede introducir las operaciones pendientes de realizar. Como se ha dicho más arriba la instancia del tracker UDP contiene el objeto transactionQueue, este objeto es necesario puesto que en un momento determinado el peer puede querer realizar más de una operación. Por ejemplo, puede querer realizar una operación connect, una operación announce y una operación scrape. El objeto BtUDPTracker es el encargado de ir accediendo a la cola para realizar las operaciones pendientes en el orden que se introdujeron. Este tipo de cola tiene la particularidad que cada vez que se añade una operación connect, ésta pasa a la primera posición ya que mantener una conexión viva es más prioritario que realizar cualquier otra operación puesto que sin conexión el tracker no servirá ninguna petición excepto la de connect. Así que siempre que hay una operación connect en la cola, se encuentra en la primera posición. Para conseguir este funcionamiento específico, BtUDPTransactionQueue, en su inicialización, sustituye la colección subyacente (instancia de OrderedCollection) por una instancia de SortedCollection y usa la función de comparación BtUDPTransaction>>priorOrEqualTo: como criterio de ordenación que asegura el orden deseado, como se puede ver en los siguientes trozos de código. BtUDPTransactionQueue>>initialize super initialize. items := SortedCollection sortBlock: [ :a :b | a priorOrEqualTo: b ] BtUDPTransaction>>priorOrEqualTo: anotherTransaction ^actionCode = ConnectActionCode or:[ anotherTransaction actionCode ~= ConnectActionCode] Además, el objeto BtUDPTracker se encarga de eliminar de la cola la operación pendiente que se satisface cada vez que llega una respuesta. Para ello, decodifica el mensaje entrante para recuperar el objeto BtUDPTransaction que contiene y así 38 poder encontrarlo en la cola. Para que la comunicación entre el peer y el tracker funcione, el objeto BtUDPTracker necesita enviar los paquetes pendientes y recibir las respuestas constantemente, además de controlar si hay que restablecer la conexión o si ha pasado el tiempo suficiente antes de volver a enviar una petición. Para ello, inicia dos procesos independientes que solo terminan cuando se invoca el método BtUDPTracker>>terminate. El contenido del bloque de ejecución del proceso de envío de paquetes (definido en la variable sending3 ) es el siguiente. [ self secondsToNextAttempt <= 0 ifTrue: [ self dequeueAndSendNextPacket ] ifFalse: [ self waitForNextAttempt ] ] repeat Se puede observar que solo envía peticiones si ha pasado el tiempo suficiente (secondsToNextAttempt <= 0) y espera hasta que se pueda enviar la siguiente petición pendiente. En caso de poder enviar otra petición, el proceso invoca a dequeueAndSendNextPacket. BtUDPTracker>>dequeueAndSendNextPacket self isConnectionAlive ifFalse: [ self reestablishConnection ]. packet := self dequeueNextRequest. packet ifNotNil: [ self sendPacket: packet ] Este método, antes de enviar cualquier petición, comprueba que la conexión no haya expirado. En caso de que haya expirado, añade una nueva petición de connect a la cola invocando el método reestablishConnection y el criterio de ordenación asegura que será la primera petición en enviarse aunque se hayan añadido otras previamente. El proceso de recibir paquetes (definido en la variable receiving) invoca el método receiveAndHandleNextPacket constantemente, como se puede ver en su bloque de ejecución. [ self receiveAndHandleNextPacket ] repeat BtUDPTracker>>receiveAndHandleNextPacket self handleReceivedPacket: self receivePacket Este último método invoca a receivePacket, que no devuelve un paquete hasta que no haya recibido uno. A continuación se invoca el método handleReceivedPacket: con el paquete entrante como argumento y éste actualiza el estado de la conexión en función del tipo del paquete y la información que contiene. 3 En Smalltalk todo son objetos, incluso los procesos 39 5.5.3. La clase BtMultitracker La mayor debilidad de las redes BitTorrent es el tracker . Si el tracker deja de funcionar ya sea por error o por acciones malintencionadas, la red entera desaparece. El BEP12 es una propuesta de ampliación que intenta paliar esta vulnerabilidad. Esta medida consiste en que varios trackers puedan servir a la misma red coordinadamente de forma que si un tracker no puede responder, otro tracker pueda seguir realizando la misma función. Es bastante común la situación en que los trackers solitarios de los ficheros .torrent que incluyen múltiples trackers adicionales no sean accesibles. Así que una aplicación BitTorrent que no implemente esta ampliación tendrá bastantes dificultades para unirse a muchos enjambres, puesto que la mayoría de ficheros .torrent incluyen la opción multitracker. Para que esta mejora funcione, primero hay que modificar el diccionario metainfo para que los peers sepan las URL de los trackers adicionales que pueden consultar. Esta información aparece en una nueva clave del diccionario (al mismo nivel de la clave announce) con el nombre announce-list y apunta a una lista de listas de URL. A estas listas se les da el nombre de tiers (capas o niveles en castellano). Más adelante se explica el porqué de estos tiers. Si la aplicación es compatible con esta funcionalidad y esta nueva clave se encuentra en la metainfo, entonces ignora la clave announce y solo usa las URL apuntadas por announce-list. Los tiers de trackers son usados secuencialmente; todas las URL de un tier deben ser comprobadas antes de usar el siguiente tier. Dentro de cada tier, las URL se comprueban en orden aleatorio. Por lo que es responsabilidad de la aplicación mezclarlas aleatoriamente antes de usar el tier por primera vez. Si la aplicación conecta satisfactoriamente con un tracker , su URL se mueve a la primera posición del tier. A continuación se muestran unos ejemplos de valores de announce-list y cómo la aplicación debe usarlos. En estos ejemplos, se supone que la aplicación ya ha mezclado todas las URL dentro cada tier. d[‘announce-list’]=[ [tracker1], [backup1], [backup2] ] En este caso, primero se prueba tracker1. Si no responde, se intenta con backup1. Si tampoco responde, se intenta con backup2. Y se sigue probando todos los tiers en el orden que aparecen mientras no responda ningún tracker . Este caso se da cuando los trackers son estándar y no pueden coordinarse para compartir información. d[‘announce-list’]=[[tracker1, tracker2, tracker3]] En este caso, primero se intenta conectar con tracker1. Si no se puede, se intenta con tracker2. Si éste sí responde, entonces la lista pasa a ser [tracker2, tracker1, tracker3]. Y este es el nuevo orden en que la aplicación procesa la lista en el 40 próximo intento. Si más adelante ni tracker2 ni tracker1 responden pero tracker3 sí, entonces la lista pasa a ser [tracker3, tracker2, tracker1] y será procesada en este orden en el próximo intento. Cuando hay varios trackers dentro de un tier, como en este ejemplo, significa que son capaces de coordinarse para compartir información. Además, el hecho de mezclarlos ayuda a balancear la carga de trabajo. d[‘announce-list’]=[[tracker1, tracker2], [backup1,backup2]] En este caso, la aplicación intenta acceder a los trackers del primer tier como en el ejemplo anterior. Si todos los trackers fallan, entonces pasa a probar el siguiente tier y lo reordena de la misma forma que en el ejemplo anterior si es necesario. Esta estructura de trackers es representada con la clase BtMultitracker. Esta clase encapsula una OrderedCollection que contiene los tiers (también OrderedCollection’s). El método shuffle mezcla los elementos de todos los tiers; el método selectNext selecciona el siguiente tracker del tier actual o selecciona el primero del siguiente si el tracker es el último del tier actual; el método moveCurrentToFirst mueve el tracker actual a la primera posición del tier donde se encuentra; los métodos selectFirst y selectLast devuelven el primer tracker del primer tier y el último tracker del último tier, respectivamente; y el método current devuelve el tracker seleccionado en el instante de invocarlo. Con estos métodos, la aplicación ya dispone de una interfaz para manipular la estructura de los multitracker. 5.6. Control de las piezas En el enjambre, el peer local debe mantener el control de las piezas que tiene y las piezas que tienen los peers remotos conocidos. Tener esta información es esencial para saber qué piezas pedir a otros peers y al mismo tiempo poder comunicar a los demás peers qué piezas tiene para que se las puedan pedir. La clase encargada de llevar este control es BtBitfield. Esta clase encapsula un ByteArray en el que el bit de mayor peso del primer octeto controla si el peer en cuestión tiene la primera pieza (con índice 0), mientras que el bit de menor peso del último octeto hace lo mismo con la última pieza. Además dispone de todos los métodos necesarios para consultar o modificar cualquier bit. El método BtBitfield>>interestingBitfield: recibe como argumento otro BtBitfield y responde otra instancia de la misma clase pero solo con los bits a 1 si el receptor del mensaje los tenía a 0 y el argumento a 1. En otras palabras, el receptor responde un BtBitfield indicando las piezas que le faltan y el argumento sí tiene. Esto se consigue aplicando la función booleana (a ⊕ b) ∧ b bit a bit a cada octeto, lo que en Pharo se expresa así: 41 BtBitfield>>interestingBitfield: [...] newByte := (byte bitXor: anotherByte) bitAnd: anotherByte. [...] ^ newBitfield Con este método es posible saber qué piezas pedir a cada peer . Por cada torrent que gestiona, el peer local tiene un objeto BtBitfield para controlar qué piezas tiene, y una colección de peers remotos que contienen un objeto BtBitfield cada uno de ellos, que pueden ser accedidos para actualizarlos. De esta forma el peer local puede mantener un control de sus piezas y las de los vecinos. 5.7. Comunicación peer-to-peer La comunicación P2P es la parte del protocolo que se produce a partir del momento en que un peer ha conseguido la dirección IP y puerto de otro peer e intenta establecer una conexión con la intención de intercambiar piezas. El núcleo de BitTorrent (BEP3) especifica la forma estándar en que se producen estas conexiones, que es sobre TCP. Estas conexiones son simétricas, es decir, el formato de los mensajes es independiente del sentido en el que viajan. Esto es así porque a ambos extremos de la conexión se encuentran dos nodos con roles y capacidades equivalentes. Existen dos conceptos que determinan el estado de una conexión entre dos peers. Estos conceptos son nombrados en el protocolo como choked y interested. La traducción literal de choked es «obstruido», pero de ahora en adelante nos referiremos a este concepto como bloqueado o no aceptado. Cuando un peer A bloquea a otro peer B significa que A no enviará ninguna pieza a B mientras no lo desbloquee. La traducción de interested es «interesado». Cuando un peer A está interesado en otro peer B significa que B tiene piezas que A no tiene. Por cada conexión, el peer local debe guardar ambos estados para los dos extremos de la conexión. Es decir, si el peer local bloquea al peer remoto y si es interesante; y viceversa. Las conexiones empiezan por defecto con el peer remoto bloqueado y siendo no interesante. En el apartado 5.9 se explica qué papel juega el bloqueo en una red BitTorrent. 5.7.1. Mensajes El mecanismo para establecer una conexión P2P en BitTorrent es mediante un handshake entre los dos extremos. Primero, el peer que quiere iniciar la conexión 42 envía un mensaje que consta de la siguiente información. Un octeto con el valor 19 en decimal, indicando la longitud de la cadena de caracteres que da nombre al protocolo. La cadena «BitTorrent protocol», de la longitud indicada en el primer octeto. Ocho octetos reservados para posibles ampliaciones del protocolo. El valor info_hash (20 octetos) que identifica al torrent de forma única. El peer_id (20 octetos) que el peer envía al tracker en la operación announce. Entonces, el peer receptor del mensaje envía el mismo mensaje pero cambiando el valor peer_id con el suyo propio. Los peers deben comprobar que la información de los mensajes que reciben en el handshake es correcta y que el info_hash corresponde con alguno de los torrents que está compartiendo. En caso contrario, deben cerrar la conexión. Suponiendo que el handshake ha tenido éxito, a partir de ese momento la conexión P2P ha sido establecida y los peers pueden empezar a compartir sus piezas. Estas piezas son nombradas por su índice (la primera pieza tiene el índice 0). Hasta ahora solo se ha mencionado que los torrents se parten en piezas de igual tamaño y que éstas se transfieren entre los peers de la red. Pero eso es una simplificación del funcionamiento de BitTorrent. En realidad, la unidad mínima de información útil es el bloque. Si entendemos una pieza como una secuencia de octetos, un bloque es una parte contigua de tamaño variable de una pieza. Durante la vida de la conexión P2P, los peers están autorizados a enviarse una serie de mensajes, todos ellos con la siguiente forma. <longitud del mensaje>[<tipo del mensaje>[<información útil>]] La primera parte son 4 octetos que indican la longitud total del mensaje (sin incluir esta parte) en decimal; el tipo del mensaje (decimal) es 1 octeto; y por último, la información útil es una secuencia de octetos de tamaño variable (en función del tipo de mensaje). A continuación se detalla el contenido y significado de cada tipo de mensaje. keep alive: <0> Se envía para mantener la conexión viva si ningún otro mensaje ha sido enviado durante 2 minutos. choke: <1><0> El emisor comunica al receptor que lo ha bloqueado, es decir, que no le servirá ninguna petición de bloque pendiente o futura. 43 unchoke: <1><1> El emisor comunica al receptor que lo ha desbloqueado y que responderá las peticiones de bloques. interested: <1><2> El emisor comunica al receptor que está interesado en alguna de sus piezas. not interested: <1><3> El emisor comunica al receptor que no está interesado en ninguna de sus piezas. have: <5><4><índice de pieza> El índice de pieza son 4 octetos en decimal. El emisor comunica al receptor que tiene una pieza determinada. Este mensaje lo envía el peer local a todos los peers conocidos cada vez que consigue descargar una pieza nueva. bitfield: <1+X><5><bitfield> El emisor envía al receptor una secuencia de octetos de tamaño X que representa un campo de bits indicando qué piezas tiene (bit a 1) y cuales no (bit a 0). Leyendo los bits de izquierda a derecha, el primer octeto corresponde a las piezas 0-7, el segundo corresponde a la piezas 8-15. . . Los bits del final que no se usan se ponen a 0. Este es el primer mensaje que se envía en la conexión P2P. Los peers sin ninguna pieza pueden obviar el envío de este mensaje. request: <13><6><índice de pieza><comienzo><longitud> Cada uno de los tres campos que aparecen después del tipo del mensaje son 4 octetos en decimal. Estos campos describen un bloque que se encuentra en una pieza determinada, comienza en un octeto dentro de ésta y tiene una determinada longitud. Este mensaje sirve para pedir un bloque al peer receptor. piece: <9+X><7><índice de pieza><comienzo><bloque> Cada uno de los dos primeros campos que aparecen después del tipo del mensaje son 4 octetos en decimal e indican la posición de un bloque dentro de una pieza. El tercer campo contiene el bloque, que es una secuencia de octetos de longitud X. Este mensaje sirve para enviar un bloque previamente solicitado por un peer remoto. cancel: <13><8><índice de pieza><comienzo><longitud> Cada uno de los tres campos que aparecen después del tipo del mensaje son 4 octetos en decimal y describen un bloque. Este mensaje normalmente se envía durante la fase End Game para cancelar peticiones duplicadas. En el apartado 5.11 se explica en qué consiste esa fase. Como en otras partes de la implementación, es necesario un conjunto de clases que representen estos mensajes dentro de Smalltalk como objetos y un sistema 44 para, por un lado, codificar estos objetos en secuencias de octetos para ser enviadas a la red y, por otro lado, decodificar las secuencias de octetos que contienen estos mensajes obteniendo los objetos que los representan. Todas las clases implicadas directamente en la comunicación P2P se encuentran en el paquete BitTalk-P2P. Los mensajes intercambiados durante el handshake son distintos a los mensajes enviados después ya que no comparten forma. Así que la clase BtHandshakeMessage representa los mensajes del handshake y la clase BtMessage representa a todos los mensajes enviados después del handshake, que sí comparten forma. La clase BtHandshakeMessage tiene como variables cada una de las partes que se envían en el mensaje del handshake como se puede ver en su definición. Object subclass: #BtHandshakeMessage instanceVariableNames: ’protocolIdentifier infoHash peerID protocolBehaviour’ classVariableNames: ’ProtocolIdentifier’ category: ’BitTalk−P2P’ Y el método BtHandshakeMessage>>asByteArray codifica sus instancias en secuencias de octetos listas para ser enviadas a la red. Con el método ByteArray>>decodeHandshake se realiza la operación inversa. Como pasa con el bencoding y los paquetes de la comunicación con los trackers UDP, este método invoca a PositionableStream>>decodeHandshake. La clase BtMessage tiene las variables de instancia type, que representa el tipo del mensaje, y payload, que representa la información útil. No tiene ninguna variable de instancia para guardar la longitud del mensaje ya que ésta depende de las otras dos, y es calculada y codificada en el método BtMessage>>asByteArray. Este método codifica cada una de las partes que componen un mensaje en secuencias de octetos como se ve en su implementación. BtMessage>>asByteArray | bytesPayload length message | length := type ifNil: [ 0 ] ifNotNil: [ 1 ]. bytesPayload := payload ifNil: [ ByteArray new ] ifNotNil: [ payload asByteArray ]. length := length + bytesPayload size. message := OrderedCollection new. message addAll: (length asByteArrayOfSize: 4). type ifNotNil: [ message add: type ]. message addAll: bytesPayload. ^ message asByteArray La última parte, el payload o información útil, es representada por la clase BtMessagePayload y tiene una subclase por cada tipo de payload que puede enviarse. Esta clase tiene el método abstracto asByteArray ya que la forma de codificar la información del payload depende de su tipo. Estos tipos son BtBitfieldPayload (para el mensaje bitfield ), BtBlockDescriptionPayload (para los mensajes request y cancel ), BtBlockPayload (para el mensaje piece) y BtHavePayload (para 45 el mensaje have). Cada tipo de payload tiene sus propias variables de instancia porque la información que contienen es distinta. Por tanto, cada tipo tiene su propia implementación de asByteArray. En los siguientes trozos de código podemos ver las implementaciones de BtBlockDescriptionPayload y BtBlockPayload. BtBlockDescriptionPayload>>asByteArray | bytes | bytes := OrderedCollection new. bytes addAll: (index asByteArrayOfSize: 4); addAll: (begin asByteArrayOfSize: 4); addAll: (length asByteArrayOfSize: 4). ^ bytes asByteArray BtBlockPayload>>asByteArray | bytes | bytes := OrderedCollection new. bytes addAll: (index asByteArrayOfSize: 4); addAll: (begin asByteArrayOfSize: 4); addAll: block. ^ bytes asByteArray El proceso inverso (decodificar secuencias de octetos en instancias de BtMessage) lo hace el método ByteArray>>decodeMessage. Como en los anteriores sistemas de codificación mencionados, este método invoca a PositionableStream>>decodeMessage. 5.7.2. Sistema de colas En cada conexión P2P es necesaria una estructura de cola para almacenar los objetos BtMessage pendientes de enviar y otra para almacenar los objetos BtMessage entrantes procedentes del peer remoto. La clase BtMessageQueue, subclase de SharedQueue, cumple esta función. Se ha decidido heredar las propiedades de SharedQueue porque estas colas son accedidas por más de un proceso a la vez y este tipo de cola garantiza exclusión mutua. Además, el uso de estas colas permite no tener que enviar directamente los mensajes request por el socket que gestiona la conexión P2P. Esto es importante, por ejemplo, cuando el peer local recibe un mensaje choke. En tal caso, todos los mensajes request de la cola de salida pueden ser descartados ya que no serán respondidos. Sucede lo mismo a la inversa, cuando el peer local envía el mensaje choke a un peer remoto. El primero debe descartar todos los mensajes piece salientes pendientes de enviar, así como todos los mensajes request entrantes porque no los va a responder. Sin el uso de estas colas, no se podría evitar el envío de estos mensajes innecesarios. Aún así, no se 46 puede impedir completamente el envío de mensajes innecesarios a la red, ya que siempre es posible que algunos mensajes ya hayan sido extraídos y tratados antes de poder impedirlo. Los métodos encargados de descartar estos mensajes son discardAllPieces y discardAllRequests. Estas colas también disponen de una serie de optimizaciones implementadas en el método BtMessageQueue>>nextPut: que se explican a continuación. Cada vez que se añade un mensaje choke/unchoke o interested /not interested, todos los mensajes de estos tipos son eliminados de la cola, respectivamente. Esto es posible hacerlo porque en una sucesión de estos tipos de mensajes, el último en llegar anula a los anteriores puesto que representa el último estado deseado por el peer emisor. Cada vez que se añade un mensaje cancel, elimina todos los requests de la cola que concuerdan con la descripción de bloque del cancel, puesto que éste los anula y ya no es necesario tratarlos. Cada vez que se añade un mensaje not interested, elimina todos los requests porque el emisor de estos mensajes ya no quiere los bloques descritos por esos requests. 5.7.3. La clase BtRemotePeer La clase BtRemotePeer representa las conexiones P2P. Aunque una conexión P2P dentro de Smalltalk también puede verse como el objeto que representa al peer remoto en el otro extremo de la conexión y con el cual se puede interaccionar enviando y recibiendo mensajes. Las principales variables de esta clase son las siguientes. socketStream. Un objeto SocketStream sobre TCP para enviar y recibir los mensajes P2P. bitfield. Un objeto BtBitfield para llevar el control de las piezas del peer remoto. unfulfilledOutgoingRequests. Una OrderedCollection que contiene las peticiones pendientes de respuesta del peer local hacia el peer remoto. Esta colección es necesaria para no duplicar peticiones en la red. Los elementos de unfulfilledOutgoingRequests son instancias de la clase BtBlockRequest, que representa las peticiones de bloque. Sus variables de instancia son pieceIndex, offset y length (la información que describe un bloque). inMessageQueue. Un objeto BtMessageQueue para guardar los mensajes entrantes pendientes de tratar. 47 outMessageQueue. Un objeto BtMessageQueue para guardar los mensajes salientes pendientes de enviar. Como ya se ha dicho anteriormente, el peer local puede bloquear/desbloquear y poner como interesante/no interesante al peer remoto de cada conexión; y el peer remoto hace lo mismo con el peer local. Estos estados se controlan con las variables de instancia isAccepted y isInteresting para los estados que determina el peer local, y acceptsMe y isInterested para los estados que determina el peer remoto. Las instancias de BtRemotePeer tienen dos procesos que se ejecutan indefinidamente mientras la conexión está viva. Estos procesos son el de enviar los mensajes (variable sending) y el de recibirlos (variable receiving). El proceso de enviar mensajes invoca el método dequeueAndSendNextMessage. BtRemotePeer>>dequeueAndSendNextMessage self ensureKeepAlive. nextMessageToSend := outMessageQueue nextOrNil. nextMessageToSend ifNotNil: [ socketStream nextPutAllFlush: nextMessageToSend asByteArray. peerLock critical: [ lastMessageSentTime := Time millisecondClockValue ] ] Este método primero añade el mensaje keep alive a la cola de salida si no se ha enviado ningún mensaje en los últimos 2 minutos. Para saber si esto es así, siempre se actualiza el tiempo del último mensaje enviado en la variable lastMessageSentTime. A continuación, extrae el siguiente mensaje pendiente de la cola de salida outMessageQueue y lo envía por el socketStream codificado en una secuencia de octetos (con el método asByteArray). El proceso de recibir mensajes invoca el método receiveAndEnqueueNextMessage. BtRemotePeer>>receiveAndEnqueueNextMessage messageReceived := [ self receiveNextMessage ] on: ConnectionTimedOut , BtMessageError do: [ :ex | ex return: nil ]. messageReceived ifNotNil: [ inMessageQueue nextPut: messageReceived ] Primero recibe el siguiente mensaje entrante del socketStream con el método receiveNextMessage y seguidamente lo introduce en la cola de entrada inMessageQueue. El método receiveNextMessage, recibe los octetos del siguiente mensaje por el socketStream e invoca al método ByteArray>>decodeMessage, que es el encargado de decodificar los octetos en una instancia de BtMessage. Y ese es el objeto que devuelve, como puede verse en su implementación. 48 BtRemotePeer>>receiveNextMessage | length stream prefix | prefix := self receiveNext: 4. stream := WriteStream with: prefix. length := prefix asInteger. length > 0 ifTrue: [ stream nextPutAll: (self receiveNext: length) ]. ^ stream contents decodeMessage Adicionalmente, los objetos BtRemotePeer también tienen las variables de instancia downloaded y uploaded que sirven para actualizar la cantidad de octetos descargados y subidos por el peer remoto, respectivamente. Estas variables son importantes, como se verá más adelante, en el algoritmo de bloqueo. 5.8. Piezas temporales Como se ha dicho anteriormente, la unidad mínima de información útil que se envía es el bloque. El tamaño de bloque que la mayoría de implementaciones usan es 16 KiB y también es el que se usa por defecto en esta implementación. Cuando el peer local recibe un bloque tiene que decidir qué hacer con él: o bien lo escribe directamente en la parte del disco que le corresponde, o bien lo conserva en la memoria volátil del sistema hasta que tenga todos los bloques que forman una pieza, y entonces escribe la pieza en disco y borra los bloques de la memoria volátil. En esta implementación se ha decidido conservar los bloques en la memoria volátil por una cuestión de eficiencia. En Pharo-Smalltalk, como en otros sistemas y lenguajes, es mucho más costoso realizar muchas escrituras de poca información que realizar una única escritura de toda la información a la vez. La clase encargada de guardar los bloques antes de escribirlos en disco es BtTemporaryPiece. Las instancias de esta clase contienen una colección ordenada cuyos elementos son bloques (instancias de ByteArray). Y su tamaño es el cociente entre el tamaño de la pieza que representa y el tamaño de los bloques que esté usando el peer local para el torrent que está descargando. La forma de acceder a los bloques es mediante los métodos at: y at:put:. El primer parámetro en ambos casos es el offset que ocupa dentro de la pieza; en el caso de escritura, el segundo parámetro son los octetos a escribir. Además, también es necesario saber si los bloques han sido pedidos a otro peer para no pedirlo por duplicado. El estado de los bloques se controla con los métodos requestedAt:, setRequest: y clearRequest:, los cuales reciben un offset. 49 5.9. Algoritmo de bloqueo Durante la compartición de torrent, el peer local está recibiendo constantemente múltiples peticiones de bloque procedentes de los peers de su vecindario. El peer local tiene que poner un límite al número peers a los que envía bloques. Es decir, tiene que decidir qué peers va a bloquear y cuales no. Hay varios motivos para hacerlo. El primero es que el control de congestión de TCP se comporta bastante mal cuando se envían (o se suben) mensajes por muchas conexiones a la vez. El segundo es que es necesario establecer una política de reciprocidad u «ojo por ojo, diente por diente» para evitar que peers egoístas (o free riders), aquellos que descargan de otros peers pero no corresponden compartiendo sus recursos, puedan aprovecharse de los demás. Por defecto, el protocolo establece que el peer local sirva a los 4 peers remotos más generosos, es decir, aquellos peers de los que ha conseguido descargar más bloques. Esta decisión es revaluada cada 10 segundos para evitar lo que se conoce como fibrillation, lo que pasa cuando un peer entra en un estado en el que es bloqueado y desbloqueado sucesivamente con tanta frecuencia que el enjambre pierde eficiencia. Cuando el peer local ha descargado el torrent completamente ya no tiene nada que descargar, así que ya no puede decidir en función de la generosidad de los demás peers. En este caso el peer local debe desbloquear los 4 peers a los que más bloques ha enviado. Este algoritmo de bloqueo supone un problema para los peers que acaban de unirse a la red y no tienen piezas, puesto que nunca serán desbloqueados por otros peers debido a que son considerados los más egoístas (no han compartido nada todavía). Para solucionarlo, el protocolo establece una algoritmo adicional para desbloquear de forma optimista (optimistic unchoking). En todo momento, el peer local tiene desbloqueado a un peer remoto aleatorio independientemente de lo generoso que sea, y esta decisión se toma cada 30 segundos. Tiempo suficiente para que el peer nuevo consiga descargar una pieza que pueda compartir con otros peers, y así poder ser considerado menos egoísta, y por tanto, desbloqueado. Con este algoritmo los peers nuevos tienen tres veces más de posibilidades de ser desbloqueados de forma optimista que los demás. Además, este desbloqueo optimista ofrece al peer local la posibilidad de encontrar nuevas conexiones con mejores tasa de descarga que las desbloqueadas por el algoritmo de bloqueo que se basa en la reciprocidad. Teóricamente, se puede dar el caso que un peer esté bloqueado por todos sus vecinos, incluso por aquellos a los que está subiendo información si el ancho de banda de subida no es suficientemente grande como para ser elegido entre los 4 primeros por alguno de sus vecinos. En este caso, el peer local lo que debe hacer es, o bien buscar nuevos peers, o bien reducir el número de peers desbloqueados. BitTorrent usa una medida «antibloqueo» (o anti-snubbing) que consiste en bloquear a todos los peers remotos de los que no se ha descargado ningún bloque en 50 el último minuto. El algoritmo de bloqueo de BitTorrent intenta alcanzar la eficiencia u óptimo de Pareto[3]. La implementación de este algoritmo de bloqueo se encuentra en la clase BtTorrent, que se explica en el apartado 5.14. 5.10. Algoritmo de selección de piezas Una vez que el peer local ha establecido conexiones con sus vecinos, necesita determinar en qué orden va a descargar las piezas disponibles (a cuales les da más prioridad) en la parte del enjambre que conoce. A continuación se explican las diferentes políticas de selección de piezas que se pueden usar en algún momento durante el tiempo que el peer local está en la red. En todas las políticas se da por sentado que las piezas que ya tiene el peer local se descartan de la selección. La implementación del sistema de selección de piezas se encuentra en la clase BtTorrent, que se explica en el apartado 5.14. 5.10.1. Rarest first Esta política selecciona primero las piezas con menor número de copias en el vecindario. Esta política tiene tres ventajas. Primero, aumenta la probabilidad de que el peer local sea interesante para otros peers porque posee algo que los demás no tienen. Segundo, iguala la distribución de las piezas en la red, cosa que minimiza el riesgo de que el tiempo de descarga de un grupo cualquiera de peers aumente considerablemente cuando los peers que poseen una pieza muy rara abandonan la red. Tercero, esta política tiende a igualar la velocidad de propagación de todas las piezas por la red. Esta política puede ralentizar la descarga en el período inicial, cuando el peer todavía no ha descargado un cierto número pequeño de piezas. Esto se debe a dos motivos. Uno, el algoritmo de bloqueo perjudica a los peers que no tienen piezas que compartir. Y dos, cuanto más rara es una pieza más tiempo se necesita para descargarla. Por lo que la política rarest first puede ser mala justo al inicio. 5.10.2. Random Esta política simplemente selecciona una pieza aleatoria. Puede ser útil justo en el inicio de la descarga, como alternativa a la política rarest first. Es la política estándar en el núcleo de BitTorrent (BEP3), aunque la política rarest first es la que se usa de facto durante la mayor parte de la descarga por las ventajas mencionadas arriba. 51 5.10.3. Most common first Esta política selecciona primero las piezas más comunes. Puede ser mejor que la política random justo al inicio de la descarga si el peer local tiene muchas dificultades para conseguir su primera pieza. 5.10.4. Lower first Es posible que una aplicación BitTorrent necesite descargar las piezas en orden estricto de índice. Esto sucede sobretodo en las aplicaciones que hacen streaming sobre BitTorrent. 5.11. End Game Cuando el peer local se ha unido a una red BitTorrent y ha conseguido una buena tasa de descarga se debe a que está descargando un número considerable de piezas procedentes de múltiples peers remotos que lo tienen desbloqueado. A medida que el torrent se acerca al final de la descarga, la tasa de descarga disminuye. La primera explicación que cualquiera podría dar es que las últimas piezas son muy raras. Y no es así, las últimas piezas son tan comunes o más que todas las descargadas previamente porque lo asegura la política rarest first. La disminución de la tasa de descarga se debe a que el número de piezas que se buscan es tan pequeño que es mucho más difícil conocer el mismo número de peers que las tengan que cuando se buscaban muchas más piezas. Y al encontrar menos peers, la tasa de descarga disminuye. Para contrarrestar esta situación, BitTorrent establece la fase o modo End Game, que comienza cuando todos los bloques de las piezas restantes han sido pedidos. A partir de ese momento, el peer local pide todos los bloques restantes a todos los peers remotos que lo tengan desbloqueado y tengan esos bloques. Para evitar que esta situación, aunque relativamente breve, sature la red con bloques redundantes, cuando el peer local recibe un bloque, envía un cancel a todos los peers remotos a los que les pidió ese bloque. 5.12. Conteo de las piezas en la red Cuando el peer local está conectado a otros peers, en la red pueden haber muchas piezas disponibles y, según la fase en que se encuentre la descarga del torrent, convendrá usar una política de selección de piezas u otra a la hora de elegir el orden en que se descargarán (como se explicó en el apartado 5.10). Una información esencial para llevar esto a cabo es el número de ocurrencias de las piezas en el vecindario. Así que es necesario tener un objeto al que podamos acceder para 52 que nos dé esa información y que nos permita establecer qué criterio queremos para ordenarlas en cada momento. Estas funcionalidades las ofrecen las clases BtPieceCounter y BtPiecesOnNetwork. La clase BtPieceCounter simplemente representa el contador de una pieza cuyos miembros son dos enteros: uno para definir el índice de la pieza y otro para actualizar la cuenta. Las instancias de esta clase disponen de un método de comparación rarestOrEqualThan: que recibe otro BtPieceCounter como argumento y determina si el receptor aparece menos o igual veces en la red. También es posible compararlas por índice con el método lowerOrEqualThan:. La clase BtPiecesOnNetwork encapsula una SortedCollection que contiene tantas instancias de BtPieceCounter como piezas tenga el torrent que se esté compartiendo. Esta colección por defecto está ordenada utilizando la función de comparación de conteo nombrada arriba. También es posible modificar explícitamente el tipo de ordenación que usará la colección con los métodos setIndexMode y setRarityMode. El conteo de las piezas de BtPiecesOnNetwork se actualiza con el método modifyAt:by: indicando el conteo de qué pieza va a modificarse y cuánto va a aumentar o disminuir. Esta actualización solo se produce en tres casos: Un peer nuevo se une al vecindario, es decir, el peer local consigue establecer una conexión nueva. En tal caso, el peer remoto envía los índices de las piezas que tiene y el peer local debe aumentar en 1 el conteo de las piezas que posee este nuevo peer . Un peer remoto envía un mensaje indicando que tiene una pieza nueva. En tal caso, hay que aumentar en 1 el conteo de la pieza cuyo índice ha llegado. Cuando un peer remoto es descartado por el peer local, las copias de las piezas que tiene ese peer ya no están disponibles en el vecindario, así que hay que decrementar en 1 el conteo de esas piezas. 5.13. La clase BtRemotePeerCollection El peer local, por cada torrent, tiene asociada una colección de conexiones u objetos BtRemotePeer con los que intercambia mensajes y bloques. El peer local tiene que ser capaz de realizar una serie de operaciones básicas sobre estos peers como seleccionar u ordenarlos según algunos criterios. Por ejemplo, cuando tiene que decidir qué peers bloquear y cuales no, es necesario seleccionarlos según si los peers remotos están interesados o no, y ordenarlos por tasa de transferencia. Se ha decidido implementar una clase que tenga las características de SortedCollection que permita ordenar los peers remotos por tasa de transfe53 rencia y que además ofrezca una serie de operaciones específicas de los objetos BtRemotePeer. Por ello, la clase BtRemotePeerCollection es subclase de SortedCollection. Estas colecciones de peers remotos tienen dos modos: NotCompleteMode y CompleteMode. El primero mantiene los peer remotos ordenados por tasa de subida desde el punto de vista del peer remoto, es decir, los peers que más bloques comparten con el peer local están en las primeras posiciones de la colección. Este criterio de ordenación es necesario usarlo cuando el peer local todavía no tiene todas las piezas del torrent. Sin embargo, cuando ya tiene todas las piezas es necesario ordenarlos por tasa de bajada. Como el peer local ya no descarga ninguna pieza, solo puede guiarse por cuanto descargan los peers remotos de él. El segundo modo se corresponde con esta última situación. La forma de establecer un modo u otro es con los métodos setCompleteMode y setNotCompleteMode. Adicionalmente también dispone de otros métodos que permiten seleccionar o descartar los peers según varios criterios que los propios nombres de los métodos indican claramente como selectAccepted, selectAcceptsMe, selectInterested, selectPieceOwnersOf:, selectSeeders. . . Dos de estos métodos son útiles, por ejemplo, cuando el peer local necesita seleccionar a qué peers puede pedir bloques de una determinada pieza. Tal cosa se puede conseguir con la siguiente instrucción. remotePeers selectAcceptsMe selectPieceOwnersOf: pieceIndex. Primero selecciona los peers remotos que lo tienen desbloqueado mandando el mensaje selectAcceptsMe a remotePeers. Y después, de entre esos, selecciona aquellos que poseen la pieza con índice pieceIndex. Otro método útil es ensureAcceptFirst:, que recibe un entero n y se asegura de que los primeros n peers remotos de la lista estén desbloqueados por el peer local y que el resto estén bloqueados. Este método se usa en el algoritmo de bloqueo. Los métodos doHave: y doCancel:, por ejemplo, recorren toda la lista y envían un mensaje have y cancel a cada peer , respectivamente. El primero se utiliza cuando hay que comunicar a cada peer remoto que el peer local ha descargado y validado una pieza nueva. Y el segundo se utiliza en el End Game cada vez que hay que enviar un cancel a todos los peers a los cuales se ha enviado un request repetido. Precisamente en esta última tarea, hay otro método de BtRemotePeerCollection implicado, que es selectWhoIHaveRequested:, el cual recibe un BtBlockRequest y devuelve aquellos peers de la colección a los cuales el peer local ha pedido ese bloque. En el siguiente trozo de código se puede ver cómo se realiza esta tarea combinando este último método con doCancel:. blockRequest := BtBlockRequest pieceIndex: index offset: begin length: length. (remotePeers selectWhoIHaveRequested: blockRequest) doCancel: blockRequest Primero selecciona los peers a los que ha enviado el mensaje request representado por blockRequest, y después envía un mensaje cancel a cada uno de 54 ellos. 5.14. La clase BtTorrent La clase BtTorrent representa una tarea de descarga y compartición de un torrent concreto. Sus variables más significativas son las siguientes. metainfo. Un objeto de la clase BtMetainfo. Indispensable para unirse a una red BitTorrent. location. Un objeto FileReference que indica la ruta del disco donde se almacena el contenido del torrent. fileCollection. Un objeto de la clase BtFileCollection para acceder físicamente a las piezas del torrent. tracker. Un objeto BtHTTPTracker o BtUDPTracker dependiendo de la URL que aparezca en la clave announce de metainfo. multitracker. Un objeto BtMultitracker cuyos elementos son los trackers que aparecen en la clave announce-list de metainfo. bitfield. Un objeto BtBitfield para llevar el control de las piezas del torrent que tiene el peer local. piecesOnNetwork. Un objeto BtPiecesOnNetwork para llevar el conteo de las piezas en el vecindario. remotePeers. Un objeto BtRemotePeerCollection donde guardar y manejar las conexiones P2P. temporaryPieces. Un objeto OrderedCollection cuyos elementos son instancias de BtTemporaryPiece, donde guardar los bloques de las piezas a medio descargar. Las instancias de BtTorrent también disponen de una serie de procesos que se encargan de realizar todas las operaciones necesarias en la descarga y compartición del torrent. Estas instancias disponen de tres operaciones básicas implementadas en los métodos start, stop y delete. El método start inicia la compartición del torrent y ejecuta todos los procesos necesarios durante su compartición. Para ello ejecuta el proceso starting, que inicia esos procesos y termina inmediatamente. El método stop detiene la compartición del torrent y elimina todos los procesos en ejecución iniciados por la instancia. Para ello ejecuta el proceso stopping, que ejecuta el método terminateProcesses y se asegura de comunicar al tracker que 55 abandona la red. El método delete simplemente elimina los ficheros del torrent que se encuentran en el disco. En los subsiguientes apartados se explican todos los procesos implicados durante la compartición del torrent. 5.14.1. Proceso: Validación incial Este proceso está definido en la variable initialChecking. Se ejecuta al iniciar el torrent y comprueba qué piezas ya están descargadas. El método principal de este proceso es checkPieces. BtTorrent>>checkPieces bitfield clearAll. 0 to: metainfo numPieces − 1 do: [ :index | | piece | piece := fileCollection readOffset: index ∗ metainfo pieceLength amount: (metainfo pieceLengthOf: index). piece ifNotNil: [ (SHA1 hashStream: piece readStream) = (metainfo sha1Of: index) ifTrue: [ bitfield set: index ] ] ] Este método primero pone todos los bits de bitfield a 0 y después comprueba pieza por pieza si su resumen concuerda con el que aparece en metainfo. En caso afirmativo, pone su bit a 1. Para acceder a las piezas utiliza el objeto fileCollection. 5.14.2. Proceso: Tracker requesting Este proceso está definido en la variable trackerRequesting y se encarga de determinar si es necesario realizar operaciones announce ya sea por que el peer local necesita más peers remotos a los que conectarse o porque se ha producido un evento determinado. Hace uso de los objetos tracker y multitracker. Su bloque de ejecución es el siguiente. [ self shouldAnnounce ifTrue: [ self doAnnounce ]. (Delay forSeconds: 1) wait ] repeat Este bucle primero comprueba si es necesario realizar una operación announce. En caso afirmativo, invoca el método doAnnounce. 56 BtTorrent>>doAnnounce self selectLastTracker. [ self selectNextTracker. self updateAnnounceRequest. self currentTracker announce ] doWhileTrue: [ self announceHasFailed ]. self moveCurrentTrackerToFirstInTier. self updateRemotePeers Este método recorre la lista del multitracker mientras ninguna petición obtenga una respuesta satisfactoria. En cuanto la operación tiene éxito, mueve el tracker que ha respondido a la primera posición de su tier e invoca updateRemotePeers. Este último método se encarga de recuperar las direcciones IP y puertos de los peers remotos que se encuentran en la respuesta del tracker e intenta abrir conexiones con ellos (respetando el número máximo de conexiones simultáneas que se encuentra en la variable maxConnections). Los métodos selectLastTracker, selectNextTracker y moveCurrentTrackerToFirstInTier solo tienen efecto si el objeto metainfo tiene clave announce-list y si el torrent está usando el «modo multitracker ». El método currentTracker devuelve el objeto tracker o el tracker seleccionado de multitracker dependiendo del modo en que esté el torrent. La forma de gestionar el modo de interacción con el(los) tracker (s) es con los métodos supportMultitracker, setMultitrackerMode, setSingletrackerMode y isInMultitrackerMode. 5.14.3. Proceso: Choking Este proceso está definido en la variable choking y se encarga de bloquear y desbloquear a los peers remotos según el algoritmo de bloqueo «ojo por ojo, diente por diente» que marca el protocolo. [ self updateChokingStatus. remotePeers doRestartMeasuringRates. (Delay forSeconds: 10) wait ] repeat 57 1 2 3 4 5 6 7 8 9 10 BtTorrent>>updateChokingStatus candidates := remotePeers reject: [ :peer | peer isLucky ]. candidatesThatDontCompete := candidates select: [ :peer | peer isInterested not or: [ peer isSnubber ] ]. candidatesThatDontCompete ensureNotAccept. competitors := candidates difference: candidatesThatDontCompete. self isComplete ifTrue: [ competitors setCompleteMode ] ifFalse: [ competitors setNotCompleteMode ]. max := competitors size min: maxPeersAccepted. competitors ensureAcceptFirst: max El proceso invoca, cada 10 segundos, el método que actualiza el estado bloqueado/desbloqueado de los peers vecinos (updateChokingStatus). Este método primero selecciona los candidatos a ser bloqueados/desbloqueados descartando los peers que están desbloqueados de forma optimista en la línea 2. De estos, selecciona los que seguro no van a competir porque no están interesados en el peer local o le hacen snubbing, y son bloqueados si no lo estaban ya (líneas 3 y 4). En la línea 5 selecciona los peers que van a competir por ser desbloqueados calculando la diferencia entre los candidatos iniciales y los peers que no compiten. A continuación, ordena la colección resultante dependiendo de la fase de compartición en que se encuentre el torrent (líneas 6-8). Después desbloquea los primeros max peers de la colección y el resto son bloqueados (líneas 9 y 10). Por último, el proceso pone a 0 las cantidades de octetos descargados y subidos de cada peer conocido para poder medir las tasas de transferencia de cada periodo de bloqueo/desbloqueo (remotePeers doRestartMeasuringRates). 5.14.4. Proceso: Optimistic Choking Este proceso está definido en la variable optimisticChoking y se encarga de bloquear y desbloquear a los peers remotos según el algoritmo de bloqueo optimista que marca el protocolo. [ self updateLuckyPeer. (Delay forSeconds: 30) wait ] repeat 58 1 2 3 4 5 6 7 8 9 10 BtTorrent>>updateLuckyPeer oldLuckyPeer := remotePeers detectLucky. candidates := oldLuckyPeer ifNil: [ remotePeers rejectAccepted ] ifNotNil: [ remotePeers rejectAccepted add: oldLuckyPeer; yourself ]. newLuckyPeer := [ candidates atRandom ] on: Error do: [ :ex | ex return: nil ]. oldLuckyPeer ~= newLuckyPeer ifTrue: [ oldLuckyPeer notAccept; isLucky: false . newLuckyPeer accept; isLucky: true ] El proceso invoca el método updateLuckyPeer cada 30 segundos. Este método primero selecciona los candidatos a ser desbloqueados de forma optimista (líneas 25). Después selecciona aleatoriamente de entre los candidatos el nuevo peer que va a ser desbloqueado (línea 6). Si es diferente al antiguo, entonces éste es bloqueado y el nuevo desbloqueado (líneas 7-10). 5.14.5. Proceso: Tratamiento de mensajes entrantes Este proceso está definido en la variable receiving y se encarga de tratar todos los mensajes entrantes de cada conexión. [ peersToDiscard := OrderedCollection new. remotePeers do: [ :peer | peer secondsSinceLastReceivedMessage > BtMessage connectionTimeOut ifTrue: [ peersToDiscard add: peer ] ifFalse: [ | message | message := peer nextReceivedMessage. message ifNotNil: [ self handleReceivedMessage: message remotePeer: peer ] ] ]. [ peersToDiscard isEmpty ] whileFalse: [ self discardRemotePeer: (peersToDiscard remove: peersToDiscard first) ] ]. Processor yield. true ] whileTrue En cada iteración del bucle, recorre la lista de peers remotos y trata el primer mensaje entrante de cada peer . En caso de que el peer local no haya recibido ni un solo mensaje durante los últimos 2 minutos (BtMessage connectionTimeOut) lo añade a una colección temporal de peers que serán descartados al final de la iteración. El método encargado de tratar los mensajes es handleReceivedMessage:remotePeer: que recibe un mensaje y un peer remoto como parámetros. En función del tipo de mensaje, este método invoca a uno de los métodos restantes del protocolo handling received messages 59 (es un grupo de métodos dentro de la clase BtTorrent). Por ejemplo, si el mensaje recibido es un request, invoca handleReceivedRequest:remotePeer:, que accede a fileCollection para leer el bloque solicitado y lo añade a la cola de salida del peer (o conexión P2P) que lo ha solicitado con el método BtRemotePeer>>sendPiece:begin:block: 5.14.6. Proceso: Petición de bloques Este proceso está definido en la variable requesting y se encarga de solicitar los bloques de las piezas a los peers remotos y de decidir la prioridad de las piezas a la hora de ser descargadas. En total, es capaz de usar 4 políticas de selección de piezas diferentes. Rarest first Cuando se usa esta política, las piezas son seleccionadas con el método rarestPiecesOnNetwork, que devuelve una colección de índices de piezas ordenada ascendientemente por número de ocurrencias en el vecindario. Most common first Cuando se usa esta política, las piezas son seleccionadas con el método mostCommonPiecesOnNetwork, que devuelve una colección de índices de piezas ordenada descendientemente por número de ocurrencias en la red. Lowest first Cuando se usa esta política, las piezas son seleccionadas con el método lowestPiecesOnNetworkGreaterThan:, que devuelve una colección de índices de piezas ordenada ascendientemente y mayores que el entero que recibe. Random Cuando se usa esta política, las piezas son seleccionadas con el método randomPiecesOnNetwork, que devuelve una colección de índices de piezas ordenada de forma aleatoria. Todos los métodos nombrados arriba acceden al objeto piecesOnNetwork, que es el que mantiene el control de las ocurrencias de las piezas en la red. Y todos tienen en común que descartan las piezas sin ocurrencias o que el peer local ya tiene, ya que esas piezas no le interesan. Independientemente de las políticas de selección de piezas, los objetos BtTorrent pueden usar dos modos de descarga, cada uno con propósitos distintos. Smart (BtTorrent>>setSmartMode). Este modo tiene como objetivo descargar el torrent de la forma más rápida posible. Este modo usa la política most common first mientras no se haya descargado 4 piezas. El objetivo es maximizar las probabilidades de obtener las primeras piezas. Una vez tenga 4 piezas, entonces usa la política rarest first. 60 Streaming (BtTorrent>>setStreamingMode). Este modo usa durante toda la vida del torrent la política lowest first. Este modo está pensado para los torrents que, por algún motivo, tiene sentido acceder a algunas partes del mismo sin necesidad de que la descarga haya acabado previamente. Cuando un peer tiene una o más piezas a medio descargar, la prioridad es acabar de completar esas piezas lo antes posible para así tener más piezas que compartir y, por tanto, mejores tasas de subida que permitan al peer local ser correspondido (desbloqueado) por más peers. Un peer que mantiene muchas piezas a medio descargar provoca ineficiencia en la red puesto que ralentiza la velocidad de propagación de éstas. Por tanto, el proceso de petición de bloques siempre hace una selección previa de piezas a medio descargar cuyos bloques no han sido solicitados por completo por el peer local; sus índices pueden obtenerse invocando el método BtTorrent>>halfwayNotFullRequestedPieces. Después hace la selección «normal» que se use en cada momento, que depende del modo de descarga y de la fase en que se encuentre ésta. De forma totalmente independiente a las políticas de selección y los modos de descarga, este proceso también implementa la fase End Game. En cada iteración del bucle, si el torrent se encuentra en este modo, las políticas de selección y los modos de descarga dejan de tener efecto y simplemente se solicitan todos los bloques restantes de todas las piezas a todos los peers remotos que las tengan. Hemos visto cómo este proceso selecciona las piezas que va a intentar descargar, pero falta ver cómo solicita los bloques de las piezas que ha seleccionado a los peers remotos que las tienen. El método broadcastRequestMissingBlocks: recibe una colección de índices de piezas, y solicita todos los bloques de esas piezas (que el peer local no tiene) a todos los peers remotos que los tienen. Este método es el que se invoca cuando el torrent se encuentra en la fase End Game. El método requestMissingAndNotRequestedBlocksOf: recibe una colección de índices de piezas y solicita todos los bloques de esas piezas (que el peer local no tiene) y para los que no hay una solicitud pendiente en la red. En este caso, el método asegura que no habrán dos solicitudes pendientes en la red para el mismo bloque. Este método es el que se invoca cuando el torrent no se encuentra en la fase End Game. Todos los métodos de las políticas de selección, independientemente del orden en que seleccione las piezas, tienen en común que devuelven los índices de todas las piezas restantes que no tiene el peer local. Esto significa que si no se pone ningún límite al número de peticiones de bloque salientes sin responder, supone un problema porque nada más empezar la descarga del torrent, todos los bloques serán solicitados. Teniendo en cuenta que un torrent de 1 GiB con piezas de 512 KiB y bloques de 16 KiB tiene 65536 bloques, el peer local envía 65536 peticiones en cuanto todas las piezas estén disponibles en el vecindario (hecho de lo más común puesto que con la existencia de un solo seeder se cumple esta situación). Esto 61 supone que por cada conexión de la que el peer local descargue, habrá muchas peticiones salientes sin responder. Esto supone un problema porque en caso de recibir un choke, la inmensa mayoría de estás peticiones no serán respondidas (como marca el protocolo) desperdiciando mucho ancho de banda. Para evitar este problema, este proceso hace dos cosas: Limitar el número de peticiones sin responder por conexión a 20. Equilibrar el número de peticiones sin responder entre conexiones. Cuando hay que pedir un determinado bloque y hay más de un peer remoto para elegir, se elige el que tenga menos peticiones pendientes. Esta estrategia tiende a la situación en que todas las conexiones de las que el peer local está descargando alcancen el número máximo de peticiones pendientes permitidas. 5.15. La clase BtLocalPeer La clase BtLocalPeer es subclase de BtPeer. Representa la aplicación BitTorrent y su función es almacenar objetos BtTorrent para gestionarlos y recibir todas las conexiones P2P entrantes. Las principales variables de esta clase son las siguientes. torrents. Un objeto OrderedCollection para almacenar instancias de BtTorrent. receiverSocket. Un objeto Socket sobre TCP para recibir conexiones entrantes. Como esta clase representa la aplicación BitTorrent, es conveniente que solo pueda existir una instancia de esta clase dentro de Pharo-Smalltalk (lo que se conoce en inglés como singleton pattern). Para ello se ha sobrescrito el método constructor new de la superclase, que asegura que no se instanciará un nuevo objeto si ya existe otro previamente. En tal caso, devuelve ese objeto, como se puede ver en su implementación: BtLocalPeer class>>new instances := self allInstances. ^ instances ifEmpty: [ super new ] ifNotEmpty: [ instances first ] Cuando hay que añadir un nuevo torrent a la colección torrents, hay que establecer en el nuevo torrent el identificador y el puerto que está usando la instancia de BtLocalPeer antes de añadirlo a la colección. De esto se encarga el método BtLocalPeer>>addTorrent:, como se ve en el siguiente trozo de código. 62 BtLocalPeer>>addTorrent: aTorrent (torrents includes: aTorrent) ifFalse: [ aTorrent id: id; port: port. torrents add: aTorrent ] La forma de iniciar y detener los torrents es accediendo a ellos con la interfaz estándar de torrents (que es una OrderedCollection) y usar los métodos BtTorrent>>start y BtTorrent>>stop, respectivamente. Además, también existe el método BtTorrent>>isActive que permite saber si el torrent en cuestión está trabajando o no. La instancia única de BtLocalPeer ejecuta dos procesos que se inician con el método BtLocalPeer>>start. Uno se encarga de recibir las conexiones TCP entrantes, y el otro se encarga de establecer el número máximo de conexiones permitidas a cada torrent de la colección torrents. En cambio, el método BtLocalPeer>>stop se encarga de detener los procesos y detener todos los torrents que gestiona. Ambos procesos se explican en los siguientes apartados. 5.15.1. Proceso: Escucha de puerto Este proceso se encuentra en la variable de instancia listening, y se encarga de invocar constantemente el método receiveConnection, que se encarga de escuchar el puerto usado por la aplicación y recibir las conexiones TCP entrantes que el peer local recibe durante la compartición de los torrents que gestiona. 1 2 3 4 5 6 7 BtLocalPeer>>receiveConnection remotePeer := self waitForConnectionFor: 1. remotePeer ifNotNil: [ [ torrent := self giveHandshake: remotePeer. torrent addRemotePeer: remotePeer . ] forkAt: Processor userBackgroundPriority ]. El método receiveConnection primero escucha el puerto que usa la aplicación BitTorrent durante un segundo. Si se ha producido un intento remoto de conexión, entonces el método waitForConnectionFor: devuelve un nuevo objeto BtRemotePeer que encapsula el SocketStream creado a partir del Socket de la nueva conexión remota, como puede verse en su implementación: BtLocalPeer>>waitForConnectionFor: seconds newSocket := receiverSocket waitForAcceptFor: seconds ifTimedOut: [ nil ]. ^ newSocket ifNil:[ nil ] ifNotNil: [ (BtRemotePeer on: (SocketStream on: newSocket)) debug: debug ] 63 Otra vez de vuelta en receiveConnection, el método giveHandshake: recibe el peer remoto como parámetro y se encarga de realizar el handshake, y devuelve el objeto BtTorrent de la colección cuyo info_hash concuerda con el recibido durante el handshake. La conexión es rechazada si alguna parte del handshake no es correcta, el objeto BtLocalPeer no tiene ningún torrent con el info_hash del handshake o el torrent al que va dirigido la conexión ha alcanzado el número máximo de conexiones. Por último, el nuevo objeto BtRemotePeer es añadido al objeto torrent con el método BtTorrent>>addRemotePeer:. Para poder recibir y tratar otras conexiones TCP cuanto antes, una vez se ha recibido una nueva conexión, el resto de las instrucciones se ejecutan en un proceso nuevo (líneas 4-6) para volver a invocar waitForConnectionFor: inmediatamente. Y, así, receiverSocket puede seguir recibiendo conexiones entrantes. Como se puede ver, la instancia de BtLocalPeer solo recibe conexiones entrantes y delega su gestión en los torrents que corresponda de forma que cada torrent es responsable de su propio conjunto de conexiones. Sin embargo, esta dependencia de los torrents en BtLocalPeer solo se da con las conexiones entrantes. Cuando es el peer local el que inicia una conexión con un peer remoto, es la instancia de BtTorrent la responsable de hacerlo sin necesidad de que intervenga BtLocalPeer, ya que dispone del método BtTorrent>>connectTo:, que recibe un objeto BtRemotePeer y establece una conexión en una dirección IP y un puerto determinado. 5.15.2. Proceso: Gestión de torrents Este proceso se encuentra en la variable de instancia managing, y se encarga de repartir las conexiones que puede mantener cada torrent que gestiona. La aplicación BitTorrent, representada por la instancia única BtLocalPeer, tiene un límite de conexiones impuesto en la variable maxConnections que no puede ser sobrepasado por la suma de conexiones de los torrents. Esta limitación es necesaria para evitar sobrecargar el sistema. Para respetar este límite, invoca el método updateConnectionsPerTorrent cada segundo, cuya implementación se puede ver a continuación. 64 1 2 3 4 5 6 7 8 9 10 11 12 13 BtLocalPeer>>updateConnectionsPerTorrent remaining := maxConnections. activeTorrents := torrents select: [ :torrent | torrent isActive ]. numTorrents := activeTorrents size. connectionsPerTorrent := maxConnections quo: numTorrents. activeTorrents do: [ :torrent | torrent maxConnections: connectionsPerTorrent ]. remaining := maxConnections rem: numTorrents. index := 1. [ remaining > 0 and: [ index <= numTorrents ] ] whileTrue: [ (activeTorrents at: index) modifyMaxConnectionsBy: 1. remaining := remaining − 1. index := index + 1 ] Primero selecciona los torrents de la colección que están activos (línea 3), y después asigna el número de máximo de conexiones permitidas a cada torrent de forma equitativa (líneas 6-13). Entonces, cada instancia de BtTorrent se encarga internamente de respetar esta restricción con el método BtTorrent>>reduceExcessOfRemotePeers, que es invocado cada vez que se invoca BtTorrent>>maxConnections:. BtTorrent>>maxConnections: anInteger maxConnections := anInteger. self reduceExcessOfRemotePeers 5.15.3. Justificación de la necesidad de BtLocalPeer Podría ser deseable que los torrents ni siquiera necesitaran a BtLocalPeer para recibir conexiones entrantes siguiendo el principio de que cada objeto sea responsable de las funciones que le corresponden. De esta forma, BtTorrent se encargaría completamente de gestionar todas sus conexiones y BtLocalPeer no intervendría en absoluto. En tal caso, cada torrent tendría su particular instancia de Socket (como el objeto receiverSocket de BtLocalPeer) para escuchar sus conexiones TCP entrantes. Lamentablemente, existe una limitación técnica que lo hace inviable. No es posible tener varios sockets escuchando en el mismo puerto y conseguir que cada conexión entrante la trate el socket del torrent al que va dirigida. Suponiendo que técnicamente fuera posible, también existiría un inconveniente que no haría esta opción tan deseable. Teniendo en cuenta que las aplicaciones BitTorrent conviven con muchas más en el sistema, no sería «justo» acaparar tantos puertos como torrents se estuvieran compartiendo. Además, existe un argumento de peso a favor de la existencia de BtLocalPeer. Sin la existencia de un objeto capaz de contener varios objetos BtTorrent no es posible imponer un límite de conexiones global para evitar sobrecargar el sistema. 65 Por estos motivos, la decisión tomada ha sido implementar la clase BtLocalPeer, que representa a toda la aplicación BitTorrent. 6. 6.1. Planificación Planificación inicial El proyecto se planificó para ser presentado en el turno de junio, comenzando el 28 de marzo (después de finalizar el GEP) y finalizando el 15 de junio, una semana antes de la fecha límite para entregar la memoria. El proyecto se dividió en cuatro tareas principales, las cuales se corresponden con las implementaciones de los diferentes prototipos previstos inicialmente: Prototipo 1 La aplicación BitTorrent implementa bencoding, lee y genera ficheros .torrent, y puede comunicarse con trackers y peers remotos (que implementen cualquier otra aplicación BitTorrent válida). Prototipo 2 La aplicación BitTorrent puede acceder físicamente a los torrents. La aplicación puede detenerse y reanudarse sin necesidad de volver a descargar las mismas piezas. La aplicación implementa un algoritmo de bloqueo y de selección de piezas. La aplicación puede mantener múltiples conexiones con peers remotos a la vez. Prototipo 3 La aplicación puede gestionar varios torrents a la vez. Puede gestionar los directorios de descarga y limitar el ancho de banda utilizado. Prototipo 4 (y final) La aplicación BitTorrent implementa DHT. En concreto, Mainline DHT, especificado en BEP5. Se programó una reunión de seguimiento al finalizar cada prototipo. Además, en las tareas de los prototipos 2 y 4, que son las más largas, se programaron dos reuniones adicionales alrededor del 50 % de su realización. Solo se dispuso de un programador, por lo que no hubo posibilidad de paralelizar la programación de las tareas, resultando en una programación estrictamente secuencial. La reunión del Hito de seguimiento se programó para el 6 de mayo. En la tabla 2 se pueden ver las fechas de inicio y fin de las tareas previstas inicialmente, y en la figura 14 el correspondiente diagrama de Gantt. 66 Nombre Hito inicial Prototipo 1 Reunión seguimiento Prototipo 2 Reunión seguimiento Informe seguimiento Hito seguimiento (Reunión Director) Prototipo 3 Reunión seguimiento Prototipo 4 Reunión seguimiento Límite seguimiento turno junio Límite turno junio Reunión seguimiento Límite entrega memoria Periodo lectura Duración 0d 10d 0d 30d 0d 0d 0d 10d 0d 30d 0d 0d 0d 0d 0d 5d Inicio 28/03/2015 28/03/2015 06/04/2015 07/04/2015 21/04/2015 29/04/2015 06/05/2015 07/05/2015 18/05/2015 17/05/2015 29/05/2015 29/05/2015 29/05/2015 15/06/2015 21/06/2015 29/06/2015 Tabla 2: Planificación de las tareas 67 Fin 28/03/2015 06/04/2015 06/04/2015 06/05/2015 21/04/2015 29/04/2015 06/05/2015 16/05/2015 18/05/2015 15/06/2015 29/05/2015 29/05/2015 29/05/2015 15/06/2015 21/06/2015 03/07/2015 Figura 14: Diagrama de Gantt del proyecto 68 6.2. Modificaciones a la planificación inicial La planificación inicial tuvo que modificarse dejando de implementar el BEP5 (DHT) y retrasando la presentación del proyecto al turno de octubre debido a los siguientes motivos: Se tuvieron que implementar varios BEP inicialmente no previstos, que a priori no se consideraron necesarios por no estar especificadas en el BEP3 (el núcleo de BitTorrent), pero, de facto, totalmente necesarios para el funcionamiento de la aplicación. Estas ampliaciones fueron los BEP23 (información de contacto compacta de los peers), BEP12 (multitracker ) y BEP15 (trackers UDP). El software, al ser una aplicación compleja y distribuida, fue necesario más tiempo del previsto para adquirir un conocimiento en profundidad del funcionamiento de Smalltalk, muy distinto a los lenguajes de programación y herramientas usados durante el transcurso del Grado. 7. 7.1. Presupuesto y sostenibilidad Identificación de recursos y estimación de costes Los recursos humanos han constado de una sola persona, que ha desempeñado las funciones de Jefe de Proyecto, diseñador/programador y probador. Se ha estimado un sueldo de 40A C la hora. La impresión de la memoria se ha estimado en 10A C por copia. El consumo eléctrico del equipo de trabajo se ha calculado haciendo uso de los sitios web especializados http://www.pcsilencioso.com y http://www.extreme.outervision.com, que calculan el coste de forma precisa según el hardware del equipo. El consumo de la conexión a Internet se ha estimado convirtiendo A C/mes a A C/hora. La fuente usada han sido las facturas de los últimos meses previos a la realización del proyecto. 69 Prototipo 1 Prototipo 2 Prototipo 3 Prototipo 4 Impresión de la memoria Unidades Precio Información de la unidad Tiempo (horas) Total 1 1 1 1 3 40A C 40A C 40A C 40A C 10A C Programador. Programador. Programador. Programador. hora hora hora hora 80A C 240A C 80A C 240A C 0.032A C 0.042A C Por hora. Ordenador encendido 8h/dia durante 3 meses Por hora. 30A C/mes = 0.042A C/h 720 2160 3200A C 9600A C 3200A C 9600A C 30A C 25600A C 23.04A C 90.72A C Sueldo Sueldo Sueldo Sueldo a a a a la la la la Costes directos Consumo eléctrico Consumo ADSL 113.76A C 3857.06A C Costes indirectos Contingencia (CD + CI × 0.15) Reimpresión de la memoria 3 10A C Probabilidad = 0.2 6A C 6A C 29576.82A C Imprevistos Presupuesto Tabla 3: Presupuesto 7.2. Viabilidad económica Desde el punto de vista económico, el proyecto es totalmente viable. Solo se ha requerido una persona y un equipo de trabajo para desarrollar la aplicación. Suponiendo que el objetivo del proyecto fuera comercial, el mercado sería global puesto que cualquier usuario con un equipo portátil o de sobremesa modesto puede participar y beneficiarse de las redes BitTorrent. Si comparamos las posibilidades potenciales de venta con la poca cantidad de recursos utilizados para desarrollar la aplicación, el proyecto es altamente rentable. 7.3. Impacto social y ambiental La aplicación desarrollada tiene claramente ventajas e inconvenientes muy marcados. La principal ventaja de BitTorrent es la evolución tecnológica respecto a las redes centralizadas o cliente-servidor. Los dos principales puntos fuertes de este tipo de redes son la escalabilidad y la robustez. Mientras las redes cliente-servidor pueden colapsar llegado a cierto número de participantes, las redes BitTorrent pueden escalar teóricamente de forma infinita ya que la carga de trabajo se reparte de forma homogénea entre todos los peers. Su naturaleza descentralizada también las hace robustas ya que no existe un punto vulnerable como con las redes cliente-servidor. Gracias a estos avances tecnológicos los usuarios pueden intercambiar ficheros con una seguridad muy alta de que ningún agente externo podrá impedirlo. Otra posible utilidad de la aplicación que puede ser beneficiosa para la sociedad es la de herramienta ilustrativa para la enseñanza del funcionamiento de BitTorrent o el P2P en general. Gracias a herramientas gráficas de Pharo desarrolladas por la comunidad, sería posible adaptar la aplicación sin demasiado esfuerzo para que mostrara gráficamente el funcionamiento de BitTorrent con un propósito didáctico. El principal inconveniente de BitTorrent se produce cuando se explotan sus 70 características tecnológicas para intercambiar ficheros protegidos con derechos de autor como pueden ser películas, series, videojuegos. . . Desde el punto vista ambiental, la aplicación no va a aumentar las emisiones de CO2 de una forma que pueda tenerse en cuenta, puesto que no van a encenderse muchos más ordenadores en el mundo solo para ejecutar esta aplicación, sino que los usuarios normalmente la pondrán en marcha cuando tengan su equipo personal encendido por otros motivos ajenos al intercambio de ficheros. Así que se puede afirmar que la aplicación no perjudica el medio ambiente a una escala que merezca la pena considerar. 8. Conclusiones Al finalizar el proyecto, la aplicación BitTorrent desarrollada es totalmente funcional. Cualquier usuario puede unirse a cualquier red BitTorrent con facilidad. La aplicación ha sido diseñada e implementada de forma que cualquier programador de la comunidad Pharo-Smalltalk pueda entender fácilmente su funcionamiento gracias a un lenguaje claro y altamente expresivo. También se ha conseguido representar virtualmente dentro de Smalltalk la naturaleza de BitTorrent. Por tanto, Los objetivos generales del proyecto se han cumplido. Se ha implementado el BEP3 (núcleo de BitTorrent) y otras ampliaciones que han acabado siendo necesarias como los trackers UDP (BEP15) y el uso de multitracker (BEP12). Debido a esto y al hecho de tener que desarrollar una aplicación compleja y distribuida en un sistema muy diferente a todos los lenguajes y herramientas utilizadas hasta ahora como ha sido Smalltalk, no ha sido posible implementar la DHT (BEP5). Aún así, la implementación de DHT se encuentra bastante avanzada en el momento de la entrega del proyecto, como se puede comprobar en el paquete BitTalk-DHT. 9. Posibles ampliaciones Por supuesto, la primera ampliación propuesta sería acabar de implementar DHT (BEP5). Al ser la primera librería sobre BitTorrent para Pharo-Smalltalk, el rendimiento de la aplicación no ha sido una prioridad, si no que la prioridad ha sido obtener una aplicación funcional. Una posible mejora sería mejorar la eficiencia y la velocidad de descarga para llegar a ofrecer un rendimiento parecido al de otras aplicaciones BitTorrent más populares. Actualmente no existen más BEP con estatus final o aceptado que no estén implementados en esta aplicación. Pero existen algunos BEP con estatus de borrador 71 que se usan como si fueran definitivos. Si hubiera que elegir dos, probablemente uno sería el que sustituye la comunicación peer-to-peer sobre TCP por el protocolo uTP (BEP29), que funciona sobre UDP. De esta forma, todas las comunicaciones serían sobre UDP, que es a lo que tiende BitTorrent desde hace años. El otro sería implementar superseeding (BEP16), que ayuda al primer peer del la red a distribuir más rápidamente las piezas. La aplicación, actualmente tiene un límite fijo impuesto al número de peticiones sin responder por cada conexión. Una posible mejora sería implementar una estrategia más inteligente que adaptara en tiempo real este límite a la velocidad de subida del peer remoto. Si esto se hiciera correctamente, se maximizaría la velocidad de descarga y el cuello de botella dependería exclusivamente o bien de la aplicación, o bien de la velocidad de bajada de la conexión a Internet del peer local, pero no de un límite impuesto que desperdicia la potencia del peer remoto. 72 Adenda A. A.1. Manual de usuario Instalación de Pharo Pharo es compatible con OS X, Windows y GNU/Linux. La versión 3.0 puede descargase de http://files.pharo.org/platform/Pharo3.0-mac.zip, http://files.pharo.org/platform/Pharo3.0-win.zip, o http://files.pharo.org/platform/Pharo3.0-linux.zip, según corresponda. Una vez descargado el fichero .zip, se extrae en un directorio cualquiera y ya estará listo para ejecutarlo. A.2. Ejecución de Pharo Una vez dentro del directorio «pharo3.0», solo hay que ejecutar el fichero «pharo» (la extensión depende del sistema operativo). Cuando lo hagamos, si solo existe la imagen por defecto (el fichero «Pharo3.0.image»), ejecutará esa imagen en la máquina virtual directamente. Si existen otras imágenes a parte de la mencionada, entonces aparecerá una ventana ofreciendo la posibilidad de elegir la imagen deseada, como se puede ver en la figura 15. 73 Figura 15: Selección de imagen en Pharo (Linux Mint) A.3. Importación de la librería Una vez dentro de Pharo, hacemos clic izquierdo en el fondo de escritorio de Pharo para abrir el menú global (ver figura 16) y seleccionamos Monticello Browser. En la ventana del Monticello hacemos clic izquierdo en +Repository, después seleccionamos HTTP como tipo de repositorio y copiamos y pegamos entre las comillas simples de location la siguiente URL (ver figura 17). http://smalltalkhub.com/mc/BitTalkDev/BitTalk/main 74 Figura 16: Menú global de Pharo Figura 17: Añadir un repositorio HTTP público a Pharo Aparecerá otra ventana con las versiones del repositorio; seleccionamos la más reciente con el ratón y hacemos clic izquierdo en Load. Después de pocos segundos 75 el repositorio ya estará en el sistema. Ahora es recomendable guardar la imagen. Como es típico en las aplicaciones de escritorio, se puede sobrescribir la imagen con la opción Save del menú global, o guardar la imagen con un nombre distinto con la opción Save as. . . . Llegados a este punto, se pueden explorar las clases de la aplicación BitTorrent abriendo el System Browser a través del menú global. Las clases de la aplicación se encuentran en el paquete BitTalk. Figura 18: Exploración del paquete BitTalk en Pharo A.4. A.4.1. Pruebas Pruebas unitarias Todas las pruebas unitarias se encuentran en el paquete BitTalk-Tests. La forma más rápida de ejecutar todas las prueba de una vez es, con el System Browser abierto, hacer clic con el botón secundario del ratón sobre el paquete de pruebas y hacer clic en Run tests. . . (ver figura 19). También es posible ejecutar cada una de las clases Bt...Test por separado haciendo clic en el botón que aparece a la izquierda del nombre de la clase en cuestión. Y si se quiere ser aún más preciso, también se pueden ejecutar los métodos de prueba por separado haciendo clic en el botón a la izquierda del nombre, como con las clases. 76 Figura 19: Captura de imágen de Pharo mostrando cómo ejecutar todas las pruebas unitarias de una vez. A.4.2. Prueba global Para probar la aplicación lo que hay que hacer es abrir un Workspace con el menú global y pegar el siguiente código dentro de él. Una vez el código esté seleccionado con el ratón, hacemos clic derecho y presionamos Do it (ver figura 20). A la variable metainfoString hay que asignarle la cadena de caracteres de la ruta del fichero .torrent (pueden descargarse de http://www.legittorrents.info), y a la variable locationString hay que asignarle la cadena de caracteres de la ruta donde se quiere almacenar el torrent. 77 metainfoString:=’/path/name.torrent’. locationString:=’/path/sharing’. torrent:= BtTorrent metainfoFileString: metainfoString locationString: locationString. localPeer:= BtLocalPeer start. localPeer addTorrent: torrent. localPeer torrents last start; inspect. La última instrucción abre una ventana con la que se puede inspeccionar en tiempo real el estado interno del torrent que acabamos de añadir. Entre otras cosas, podemos ver el conjunto de peers remotos que conoce el peer local (e inspeccionarlos a ellos también), el BtBitfield, los trackers, etc. En definitiva, se pueden examinar todos los objetos descritos en el apartado de diseño e implementación. Figura 20: Ejecución del código de la prueba global Si se quiere detener el torrent sin detener la aplicación, simplemente hay que acceder a él y detenerlo con localPeer torrents last stop. Sin embargo, si lo que se quiere es detener la aplicación en su totalidad, solo hay que detener al peer local con 78 localPeer stop. , y la aplicación se encargará de detener todos los torrents de su colección. 79 Acrónimos BBS Bulletin Board System. 15, 16 BDFL Benevolent Dictator for Life. 8 BEP BitTorrent Enhancement Proposal. 8, 9, 21, 69, 71 CAN Content Addressable Network. 13 DHT distributed hash table. 9, 13, 14, 18, 21, 22, 66, 69, 71 DMCA Digital Millennium Copyright Act. 17 DSL domain-specific language. 9 ESUG European Smalltalk User Group. 8 FTP File Transfer Protocol. 15, 21, 83, 86 GUI graphical user interface. 10 HTTP Hypertext Transfer Protocol. 19–21, 31, 32, 34, 75 I2P Invisible Internet Project. 14 IP Internet Protocol. 20, 31, 33, 42, 57, 64, 86 IRC Internet Relay Chat. 15, 16, 84 ISP Internet service provider. 18 LAN local area network . 22 MPAA Motion Picture Association of America. 19 P2P peer-to-peer . 7, 8, 10–15, 17–19, 21, 42–47, 55, 60, 62, 70, 81, 82, 84, 85 PEX Peer Exchange. 21, 22 RIAA Recording Industry Association of America. 17–19 TCP Transmission Control Protocol. 21, 34, 42, 50, 62–65, 72, 83, 86 80 TFG Trabajo de Fin de Grado. 7 UDP User Datagram Protocol. 21, 31, 34–38, 45, 69, 71, 72 URI Uniform Resource Identifier. 21 URL Uniform Resource Locator. 19, 20, 28, 32, 40, 55, 74, 84 uTP uTorrent Transport Protocol. 21, 22 Glosario A|C|D|E|F|M|P|R|S|T|W A aplicación BitTorrent programa que ejecutan los peers en una red BitTorrent. Normalmente se usa de forma incorrecta la expresión «cliente BitTorrent» para referirse a este tipo de aplicaciones. Las redes que usan BitTorrent son P2P, donde no existen los roles exclusivos de cliente y servidor. 1, 2, 7, 9, 21, 23, 26, 29, 31, 34, 40, 62–64, 66, 71, 76 ataque de denegación de servicio ataque a un sistema que provoca la inaccesibilidad de un servicio o recurso por parte de los usuarios legítimos por el consumo del ancho de banda de la red de la víctima o sobrecarga de los recursos computacionales del sistema de la víctima. 14 B backtracking algoritmo que busca todas las soluciones posibles (o una parte) de algunos problemas computacionales. 6, 12, 14 bencoding sistema de codificación diseñado para BitTorrent. Los tipos de datos soportados son cadenas de caracteres, enteros, listas y diccionarios. 26, 37, 45, 66 81 Benevolent Dictator for Life título que se otorga a ciertos individuos de la comunidad de desarrolladores de software de código abierto, normalmente fundadores de proyectos que tienen la última palabra en disputas o discusiones dentro de la comunidad. 8, 80 BitTorrent protocolo diseñado para el intercambio de ficheros sobre una red P2P que se usa para distribuir gran cantidad de información por Internet. 1, 2, 6–9, 13, 14, 18–23, 26, 28–32, 34, 40, 42, 43, 50–52, 55, 62–66, 69–72, 76, 80–84, 86 BitTorrent Enhancement Proposal propuesta de ampliación de alguna parte de BitTorrent, que especifica una nueva funcionalidad o comportamiento de la red o de los actores implicados. 8, 80 bloque parte contigua de tamaño variable de una pieza. 25, 39, 43, 44, 47, 49, 50, 52–55, 60–62 Blu-ray formato de almacenamiento digital para discos ópticos. 15 Bulletin Board System servidor que permite a usuarios conectarse al sistema usando un terminal. Una vez dentro del sistema, el usuario puede realizar una serie de operaciones como subir y descargar software y ficheros, leer noticias y boletines, e intercambiar mensajes con otros usuarios vía correo electrónico y chat. 15, 80 C churn término usado para referirse a la situación donde los peers de la supercapa se unen y abandonan la red con mucha frecuencia. 11, 13, 14 cliente-servidor arquitectura de red que consiste en un sistema de alto de rendimiento, el servidor, y varios sistemas habitualmente de menor rendimiento, los clientes. El servidor es el único proveedor de servicios. Los clientes solo consumen servicios sin compartir ninguno de sus recursos. 2, 10, 11, 17, 70, 83, 84 82 courier group grupo de personas que distribuyen entre topsites el material publicado por los release groups. 15, 16, 86 D distributed hash table sistema distribuido que ofrece un servicio de búsqueda similar al de las tablas de hash, en el que cada nodo de la red es responsable del mantenimiento de una parte de todas las asociaciones <clave,valor>. 9, 13, 80 E enjambre conjunto de peers participantes de una red BitTorrent. 6, 20, 21, 25, 41, 50, 51, 83 F fibrillation situación que se da cuando un peer es bloqueado y desbloqueado sucesivamente con tanta frecuencia que provoca ineficiencia en el enjambre. 50 fichero .torrent fichero que contiene los metadatos del torrent. 20, 21, 24, 26, 29, 34, 40, 66, 77, 86 File Transfer Protocol protocolo para tranferir ficheros de un ordenador a otro sobre una red TCP con arquitectura cliente-servidor. 15, 80 FlashXP cliente FTP que admite transferencias entre cliente y servidor, y entre servidor y servidor. 15 flooding algoritmo de encaminamiento en el que cada nodo reenvía cada paquete recibido por todas sus conexiones menos por la que ha llegado. 12, 14 free rider actor que consume recursos pero no sirve ninguno a cambio. 50 83 I Internet Protocol protocolo de comunicación principal de Internet Protocol Suite. 20, 80 Internet Protocol Suite conjunto de protocolos de comunicación usado en Internet. 84, 86 Internet Relay Chat protocolo de la capa de aplicación que permite la comunicación en forma de texto. Funciona sobre una arquitectura cliente-servidor. Los clientes IRC son programas que los usuarios pueden instalar en su sistema. Estos clientes se comunican con el servidor para transferir mensajes a otros clientes. IRC está principalmente diseñado para la comunicación grupal en foros de discusión, llamados canales, pero también permite mensajería privada e intercambio de ficheros. 15, 80 Invisible Internet Project supercapa P2P de Internet que proporciona cierto grado de anonimato a sus usuarios. 14, 80 L leecher peer que no tiene todas las piezas del torrent. 19, 20, 22, 33, 35 M metadatos información esencial para el funcionamiento de una red BitTorrent. Identifican de forma única a los torrents. Entre otras cosas, contienen la dirección URL del tracker , la colección de ficheros que forman el torrent, el tamaño de las piezas y el resumen de cada una de ellas, necesario para verificar su integridad. 20, 24, 26, 28, 29, 83, 84 metainfo metadatos. 29, 30, 34, 40 P 84 peer nombre con que se denominan los nodos de una red con arquitectura P2P. La traducción al castellano sería «igual» o «par». 6, 7, 11–13, 17–25, 29, 31–35, 37–44, 46–64, 66, 69, 70, 72, 78, 81–86 peer-to-peer arquitectura de red donde los participantes comparten parte de sus recursos hardware (tiempo de procesador, espacio de almacenamiento, ancho de banda, dispositivos. . . ). Estos recursos son necesarios para proveer el servicio que ofrece la red y son accesibles directamente por cualquier participante sin necesidad de otra entidad mediadora. Los participantes de estas redes son tanto proveedores como consumidores de servicios. 2, 6, 7, 10, 11, 72, 80 pieza parte en que se divide un torrent. 19, 20, 24, 25, 28–30, 41–44, 47, 49–56, 60, 61, 66, 72, 82, 84, 85 R RAR formato de archivo que admite compresión de la información, recuperación ante errores y partición de la información. 16 release group grupo de personas que publica material con derechos de autor en los topsites. 15, 83, 86 S seeder peer que tiene todas las piezas del torrent. 19, 20, 22, 33, 35, 61 singleton pattern patrón de diseño que limita el número de instancias de una clase a uno. 62 supercapa capa de aplicación virtual, o red lógica, que ofrece servicios normalmente no disponibles en la capa física subyacente. 11–14, 19, 82, 84 T 85 topsite término usado por la warez scene para referirse a los servidores FTP clandestinos, altamente secretos y con gran ancho de banda utilizados por los release groups y courier groups para almacenar y distribuir ficheros. 15, 16, 19, 83, 85 torrent Puede referirse a: 1. colección de ficheros que se comparten en una red BitTorrent. 2. fichero .torrent. . 6, 19, 20, 24, 25, 28–30, 32, 33, 35, 42, 43, 49, 50, 52–58, 60–66, 77–79, 83–85 tracker servidor que coordina la distribución de los ficheros en una red BitTorrent. 19–22, 25, 28, 31–35, 37–41, 43, 45, 55, 57, 66, 69, 71, 78, 84 Transmission Control Protocol protocolo perteneciente a la capa de transporte del Internet Protocol Suite. TCP ofrece un sistema fiable y ordenado de entrega de secuencias de bytes entre aplicaciones que se comunican sobre la red IP. 21, 80 U Usenet sistema mundial distribuido de debate y discusión. Sus usuarios pueden leer y enviar mensajes (llamados articles o posts, y colectivamente llamados news) según categorías, conocidas como newsgroups. Los servidores redistribuyen los mensajes a otros servidores creando múltiples copias. 16, 17, 19 V vecindario conjunto de peers remotos que conoce el peer local. 24, 25, 50–53, 55, 60, 61 W 86 warez scene comunidad clandestina y oculta al gran público, especializada en la distribución de material con derechos de autor, incluyendo programas y series de televisión, películas, música, videoclips, videojuegos, aplicaciones, ebooks y pornografía. 6, 15, 16, 86 87 Referencias [1] Bandara, H. M N Dilum y Anura P. Jayasumana: Collaborative applications over peer-to-peer systems-challenges and solutions. Peer-to-Peer Networking and Applications, 6(3):257–276, 2013, ISSN 19366442. [2] Clarke, Ian, Oskar Sandberg, Brandon Wiley y Tw Hong: Freenet: A distributed anonymous information storage and retrieval system. Designing Privacy Enhancing . . . , páginas 46–66, 2001. http://link.springer.com/chapter/ 10.1007/3-540-44702-4_4. [3] Cohen, Bram: Incentives Build Robustness in BitTorrent. Workshop on Economics of PeertoPeer systems, 6:68–72, 2003. http: //citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.1911& amp;rep=rep1&amp;type=pdf. [4] Goldberg, A y D Robson: Smalltalk-80: the language and its implementation. Addison-Wesley, 1983, ISBN 0-201-11371-6. [5] Kent, B: Smalltalk Best Practice Patterns. Prentice Hall PTR, Upper Saddle River, 1997, ISBN 013476904X. [6] King, Sunny y Scott Nadal: PPCoin: Peer-to-Peer Crypto-Currency with Proof-of-Stake. Ppcoin.Org, 2012. http://ppcoin.org/static/ ppcoin-paper.pdf. [7] Lua, Eng Keong Lua Eng Keong, J. Crowcroft, M. Pias, R. Sharma y S. Lim: A survey and comparison of peer-to-peer overlay network schemes. IEEE Communications Surveys & Tutorials, 7(2), 2005, ISSN 1553-877X. [8] Malkhi, Dahlia, Moni Naor y David Ratajczak: Viceroy: A Scalable and Dynamic Emulation of the Butterfly. Proceedings of the 21st annual ACM symposium on Principles of distributed computing, páginas 183–192, 2002. [9] Maymounkov, Petar y D Mazieres: Kademlia: A peer-to-peer information system based on the xor metric. First International Workshop on Peer-to-Peer Systems, páginas 53–65, 2002, ISSN 3540441794. http://link.springer. com/chapter/10.1007/3-540-45748-8_5. [10] Nakamoto, Satoshi: Bitcoin: A Peer-to-Peer Electronic Cash System. Consulted, páginas 1–9, 2008, ISSN 09254560. http://s.kwma.kr/pdf/Bitcoin/ bitcoin.pdf. 88 [11] Ratnasamy, S, P Francis, M Handley, R Karp y S Shenker: A Scalable Content Addressable Network. Informe técnico TR-00-010, University of Berkeley, CA, 2000. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.60. 3260. [12] Rowstron, A y P Druschel: Pastry: Scalable, distributed object location and routing for large-scale peer-to-peer systems. IFIPACM International Conference on Distributed Systems Platforms Middleware, 11(November 2001):329– 350, 2001. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1. 1.28.5987. [13] Schollmeier, Rüdiger.: A definition of peer-to-peer networking for the classification of peer-to-peer architectures and applications. Proceedings First International Conference on Peer-to-Peer Computing, 2001. [14] Shen, Xuemin, John Buford, Heather Yu y Mursalin Akon: Handbook of peerto-peer networking. Springer US, New York, 2010, ISBN 978-0-387-09750-3. [15] Stoica, Ion, Robert Morris, David Liben-Nowell, David R. Karger, M. Frans Kaashoek, Frank Dabek y Hari Balakrishnan: Chord: A scalable peer-to-peer lookup protocol for Internet applications. IEEE/ACM Transactions on Networking, 11(1):17–32, 2003. [16] Wang, Liang y Jussi Kangasharju: Measuring large-scale distributed systems: Case of BitTorrent Mainline DHT. En 13th IEEE International Conference on Peer-to-Peer Computing, IEEE P2P 2013 - Proceedings, páginas 1–10, 2013, ISBN 978-1-4799-0515-7. [17] Zhao, Ben Y., Ling Huang, Jeremy Stribling, Sean C. Rhea, Anthony D. Joseph y John D. Kubiatowicz: Tapestry: A resilient global-scale overlay for service deployment. IEEE Journal on Selected Areas in Communications, 22(1):41–53, 2004. 89