OmniORB es un Object Request Broker (ORB) que implementa la especificación 2.6 de la Common Object Request Broker Architecture (arquitectura común de intermediarios en peticiones a objetos), más conocida como CORBA.
Ha sido diseñado para ser portable. Corre en muchos tipos de sistemas UNIX, Windows, varios sistemas operativos embebidos, y sistemas relativamente desconocidos como OpenVMS o Fujitsu-Siemens BS2000. Ha sido diseñado para ser fácil de portar a nuevas plataformas. El mapeo de IDL a C++ es el mismo para todas las plataformas.
OmniORB usa excepciones y clases anidadas de C++. Mantiene en lo posible la especificación estándar de CORBA y no usa mapeos alternativos para C++. Se basa en librerías nativas de hilos que proporcionan capacidad multihilo. Además, también está disponible para Python.
Para llevar a cabo la instalación de OmniORB (Free CORBA ORB) sobre Ubuntu 14.10, es necesario realizar los siguientes pasos:
Recientemente he necesitado instalar y configurar un servicio de FTP (File Transfer Protocol) sobre Ubuntu 14.04 Natty Narwhal. Para este propósito, he elegido ProFTPd. ProFTPd es un servidor FTP bajo licencia GPL y multiplataforma. Puede configurarse de una manera rápida y fácil. Además, soporta IPv6.Estos son los pasos a seguir para su instalación y configuración:
sudo apt-get install proftpd
A continuación se puede seleccionar «INETD» o «Independiente». Seleccionar «Independiente». Después, abrir el fichero /etc/proftpd/proftpd.conf y descomentar la línea DefaultRoot ~ , quedando como sigue:
# Use this to jail all users in their homes
DefaultRoot ~
# Use this to jail all users in their homes
DefaultRoot ~
Reiniciar el servicio, con sudo /etc/init.d/proftpd restart. Ahora ya puedes acceder por FTP a tu directorio HOME.
CUDA es una arquitectura hardware y software para la computación en la GPU. Consiste en utilizar el procesador gráfico o GPU como coprocesador junto con la CPU.
La GPU es un procesador especializado cuyas capacidades de cómputo se resumen en lo siguiente:
Múltiples núcleos dirigidos por un gran ancho de banda de memoria.
Gran paralelismo.
Optimización para cálculos en coma flotante.
Bajo coste.
Ingente cantidad de transistores.
Procesamiento de datos.
Almacenamiento en caché.
Control del flujo.
Estos son generalmente los pasos de cualquier aplicación CUDA:
Copiar los datos a procesar desde la memoria de la CPU a la memoria de la GPU.
Invocar el Kernel en la GPU.
El Kernel ejecuta iteraciones del bucle en paralelo en cada núcleo.
Copiar el resultado desde la memoria de la GPU a la memoria de la CPU.
El siguiente programa utiliza esta arquitectura para realizar de forma concurrente la suma de dos vectores a y b, y almacenar el resultado en un tercer vector c. Cabe destacar que este es el ejemplo más sencillo que se puede encontrar del uso de CUDA.
#include #include #include usingnamespace std;#define TAM 8 // Tamanho del vector// Declaración del kernel
__global__ void kernelSuma(int* a, int* b, int* c);int main(int argc, char*argv[]){int*a_h, *b_h, *c_h;int*a_d, *b_d, *c_d;// Reservamos memoria para los vectores host
a_h =newint[TAM];
b_h =newint[TAM];
c_h =newint[TAM];// Reservamos memoria en la GPU
cudaMalloc((void**)&a_d, TAM*sizeof(int));
cudaMalloc((void**)&b_d, TAM*sizeof(int));
cudaMalloc((void**)&c_d, TAM*sizeof(int));// Se configura un tamanio de bloque de 4 hilosint tamanio_bloque =4;int num_bloques = TAM/tamanio_bloque +(TAM%tamanio_bloque ==0?0:1);// Inicialización de los vectores en memoria hostfor(int i=0;i<TAM;i++){
a_h[i]= i;
b_h[i]= i*2;}// Copiamos los datos desde la memoria host a la memoria device
cudaMemcpy(a_d, a_h, TAM*sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(b_d, b_h, TAM*sizeof(int), cudaMemcpyHostToDevice);
kernelSuma<<<tamanio_bloque, num_bloques>>>(a_d, b_d, c_d);// Copia los resultados del vector c desde la memoria de la GPU// a la memoria del host
cudaMemcpy(c_h, c_d, TAM*sizeof(int), cudaMemcpyDeviceToHost);// Imprime los resultadosfor(int i =0; i < TAM; i++)cout<< c_h[i]<<" ";cout<< endl;// Libera la memoria// Tanto en el host...free(a_h);free(b_h);free(c_h);// ...como en la device.
cudaFree(a_d);
cudaFree(b_d);
cudaFree(c_d);}// Definición del kernel
__global__ void kernelSuma(int*a, int*b, int*c){// i = Número de bloque * dimensión del bloque + hilo dentro del bloqueint i = blockIdx.x* blockDim.x+ threadIdx.x;
c[i]= a[i]+b[i];}
#include
#include
#include
using namespace std;
#define TAM 8 // Tamanho del vector
// Declaración del kernel
__global__ void kernelSuma(int* a, int* b, int* c);
int main(int argc, char *argv[]) {
int *a_h, *b_h, *c_h;
int *a_d, *b_d, *c_d;
// Reservamos memoria para los vectores host
a_h = new int[TAM];
b_h = new int[TAM];
c_h = new int[TAM];
// Reservamos memoria en la GPU
cudaMalloc((void **)&a_d, TAM*sizeof(int));
cudaMalloc((void **)&b_d, TAM*sizeof(int));
cudaMalloc((void **)&c_d, TAM*sizeof(int));
// Se configura un tamanio de bloque de 4 hilos
int tamanio_bloque = 4;
int num_bloques = TAM/tamanio_bloque + (TAM%tamanio_bloque == 0 ? 0:1);
// Inicialización de los vectores en memoria host
for(int i=0;i<TAM;i++) {
a_h[i] = i;
b_h[i] = i*2;
}
// Copiamos los datos desde la memoria host a la memoria device
cudaMemcpy(a_d, a_h, TAM*sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(b_d, b_h, TAM*sizeof(int), cudaMemcpyHostToDevice);
kernelSuma<<<tamanio_bloque, num_bloques>>>(a_d, b_d, c_d);
// Copia los resultados del vector c desde la memoria de la GPU
// a la memoria del host
cudaMemcpy(c_h, c_d, TAM*sizeof(int), cudaMemcpyDeviceToHost);
// Imprime los resultados
for(int i = 0; i < TAM; i++)
cout << c_h[i] << " ";
cout << endl;
// Libera la memoria
// Tanto en el host...
free(a_h);
free(b_h);
free(c_h);
// ...como en la device.
cudaFree(a_d);
cudaFree(b_d);
cudaFree(c_d);
}
// Definición del kernel
__global__ void kernelSuma(int *a, int *b, int *c) {
// i = Número de bloque * dimensión del bloque + hilo dentro del bloque
int i = blockIdx.x * blockDim.x + threadIdx.x;
c[i] = a[i]+b[i];
}
La computación concurrente es la capacidad de simultanear en la ejecución de múltiples tareas interactivas. Un proceso es un programa en ejecución, que es gestionado por el Sistema Operativo y compite por los recursos del procesador. Los procesos tienen estado y memoria en ejecución reservada. El mecanismo por el cual un proceso crea otro proceso se denomina bifurcación (fork). Cuando un proceso se bifurca, se crea una copia exacta del proceso en ejecución, independiente del resto y no comparte el espacio de memoria con el proceso que los ha creado ni con otros procesos.
Un hilo, en sistemas operativos, es una característica que permite a una aplicación realizar varias tareas a la vez (concurrentemente). Los distintos hilos de ejecución comparten una serie de recursos tales como el espacio de memoria, o los archivos abiertos. Esta técnica permite simplificar el diseño de una aplicación que debe llevar a cabo distintas funciones simultáneamente. Un hilo es un tarea que se ejecuta en paralelo con otra tarea.
La comunicación entre el cliente y el servidor se hace posible gracias a estructuras abstractas denominadas sockets, mediante las cuales los programas pueden intercambiar flujo de datos e información. Este concepto está asociado al concepto de puerto.
Un puerto es una forma genérica de denominar a una interfaz a través de la cual los diferentes tipos de datos se pueden enviar y recibir. En el protocolo TCP/IP, son de tipo lógico, por ejemplo, los puertos que permiten la transmisión de datos entre diferentes computadores.
Existen dos tipos de sockets, orientados a conexión y no orientados a conexión. La diferencia fundamental es que en los sockets orientados a conexión (TCP) el protocolo garantiza que los datos serán entregados en su destino sin errores y en el mismo orden en que se transmitieron, mientras que en los sockets no orientados a conexión (o UDP) no se garantiza que el mensaje llegue a su destino. Parece claro que si el programa envía un mensaje y no hay nadie escuchando, ese mensaje se pierde. De todas formas, aunque haya alguien escuchando, el protocolo tampoco garantiza que el mensaje llegue. Lo único que garantiza es, que si llega, llega sin errores.
En una comunicación con sockets multihilo, como es el caso de la implementación que nos ocupa, cada conexión es gestionada por un proceso o hilo de ejecución independiente. Así, con cada cliente que conecte con el servidor, se expande un nuevo hilo del lado del servidor, que gestionará las peticiones del cliente asociado a él. Servidores concurrentes pueden ser multiproceso o multihilo. Estos son utilizados para servicios largos con mucha sincronización Cliente-Servidor.
A continuación dejo el código de ambas implementaciones, primero en C++ y después en Python.
Comandos
ADD usuario:Introduce un usuario en el chat.
LIST: Obtiene la lista de clientes conectados.
END: Desconecta y sale de la sesión.
TEXT: Envía un mensaje a todos los usuarios conectados.
TEXT TO usuario: Envía un mensaje privado a un usuario.
#include <stdio.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <pthread.h>#define SERVER_PORT 6543#define SERVER_ADDRESS "127.0.0.1"#define MAXLINE 512#define MAXCLIENTS 10int buscarCliente(char*);void subCadena(char*, char*, int, int);struct vector
{int socket;char usuario[MAXLINE-4];int sign_in;};struct vector vectorClientes[MAXCLIENTS];int clientes =0;int main(int argc, char*argv[]){void* gestionaCliente(void* p);int socketfd, new_sd;
socklen_t client_len;struct sockaddr_in server_addr, client_addr;int i, status, id;
pthread_t hilos[MAXCLIENTS];for(i=0;i<MAXCLIENTS;i++)strcpy(vectorClientes[i].usuario, " ");// Open TCP internet STREAM socketif((socketfd = socket(AF_INET, SOCK_STREAM, 0))<0)perror("server: Can't open stream socket");// Bind local address to allow the client to connect
bzero((char*)&server_addr, sizeof(server_addr));
server_addr.sin_family= AF_INET;
server_addr.sin_addr.s_addr= htonl(INADDR_ANY);
server_addr.sin_port= htons(SERVER_PORT);if(bind
(socketfd, (struct sockaddr *)&server_addr,
sizeof(server_addr))<0)perror("server: can't bind local address");
listen(socketfd, 5);for(;;){
client_len =sizeof(client_addr);if((new_sd = accept(socketfd, (struct sockaddr *)&client_addr, &client_len))<0){printf("Error aceptando peticiones\n");exit(0);}else{
id = clientes;
vectorClientes[id].socket= new_sd;
vectorClientes[id].sign_in=0;fflush(stdout);
clientes++;if((status = pthread_create(&hilos[id],NULL,gestionaCliente,(void*)&id))){printf("Error al crear el hilo\n");exit(0);}}}
close(socketfd);// Close original socketreturn0;}void subCadena(char*subCad, char*cad, int inicio, int cuantos){int i,j=0;for(i=inicio;i<inicio+cuantos && cad[i]!='\0';i++){
subCad[j]=cad[i];
j++;}
subCad[j]='\0';}int buscarCliente(char* usuario){int i;for(i=0;i<clientes;i++){if(strcmp(vectorClientes[i].usuario,usuario )==0&& vectorClientes[i].sign_in==1)return vectorClientes[i].socket;}return-1;}void* gestionaCliente(void* p){int*ide, id;
ide =(int*) p;
id =*ide;char buffer[MAXLINE], nombre[MAXLINE-4], temp[MAXLINE-8];int i, longitud, destino;while(1){
recv(vectorClientes[id].socket,buffer,MAXLINE,0);printf("\nid%d\n", id);if(strstr(buffer, "ADD")&& vectorClientes[id].sign_in==0){
longitud =strlen(buffer);//Le quitamos el ADD
subCadena(nombre, buffer, 4, longitud-4);strcpy(vectorClientes[id].usuario, nombre);//Se informa a todos menos a él mismo y al que se haya idostrcpy(buffer, "El usuario ");strcat(buffer, nombre);strcat(buffer, " ha entrado en el chat.");for(i =0; i < clientes; i++)if(i != id && vectorClientes[i].sign_in==1)
send(vectorClientes[i].socket,buffer,MAXLINE,0);
vectorClientes[id].sign_in=1;}if(strstr(buffer, "LIST")&& vectorClientes[id].sign_in==1){//Se envia al cliente todos los usuarios menos los que hayan abandonado la sesiónfor(i =0; i < clientes; i++){if(vectorClientes[i].sign_in==1)
send(vectorClientes[id].socket, vectorClientes[i].usuario, MAXLINE, 0);}}if(strstr(buffer, "END")&& vectorClientes[id].sign_in==1){//Se informa a todos menos a él mismo y al que se haya idostrcpy(buffer, "El usuario ");strcat(buffer, vectorClientes[id].usuario);strcat(buffer, " ha abandonado en el chat.");
bzero(vectorClientes[id].usuario, MAXLINE);for(i =0; i < clientes; i++)if(i != id && vectorClientes[i].sign_in==1)
send(vectorClientes[i].socket,buffer,MAXLINE,0);
vectorClientes[id].sign_in=0;}if(strstr(buffer, "TEXT")&&!strstr(buffer, "TEXT TO")&& vectorClientes[id].sign_in==1){
longitud =strlen(buffer);
subCadena(temp, buffer, 5, longitud-5);//Se envía a todos menos a él mismo y al que se haya ido
bzero(buffer, MAXLINE);strcat(buffer, vectorClientes[id].usuario);strcat(buffer, " dice: ");strcat(buffer, temp);for(i =0; i < clientes; i++)if(i != id && vectorClientes[i].sign_in==1)
send(vectorClientes[i].socket,buffer,MAXLINE,0);}if(strstr(buffer, "TEXT TO")&& vectorClientes[id].sign_in==1){//Le quitamos el TEXT TO
subCadena(nombre, buffer, 8, MAXLINE-8);//Nos quedamos sólo con el nombre, quitando desde el primer espacio en blanco hasta el finalstrtok(nombre," ");//Se obtiene el socket destino
destino = buscarCliente(nombre);
longitud=strlen(nombre);strcpy(nombre, vectorClientes[id].usuario);strcat(nombre, " dice: ");//Recortamos el TEXT TO, el nombre, y los dos espacios hasta el mensaje(se suma solo uno (un espacio) //porque empieza a recorrer longitud(instruccion)+longitud(nombre) desde la pos. 0 de la cadena.
subCadena(temp, buffer, 8+longitud+1, MAXLINE-(8+longitud+1));strcat(nombre, temp);if(destino !=-1)
send(destino, nombre, MAXLINE, 0);}fflush(stdout);}
close(vectorClientes[id].socket);}
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define SERVER_PORT 6543
#define SERVER_ADDRESS "127.0.0.1"
#define MAXLINE 512
#define MAXCLIENTS 10
int buscarCliente(char*);
void subCadena(char*, char*, int, int);
struct vector
{
int socket;
char usuario[MAXLINE-4];
int sign_in;
};
struct vector vectorClientes[MAXCLIENTS];
int clientes = 0;
int main(int argc, char *argv[])
{
void* gestionaCliente(void* p);
int socketfd, new_sd;
socklen_t client_len;
struct sockaddr_in server_addr, client_addr;
int i, status, id;
pthread_t hilos[MAXCLIENTS];
for(i=0;i<MAXCLIENTS;i++)
strcpy(vectorClientes[i].usuario, " ");
// Open TCP internet STREAM socket
if ((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
perror("server: Can't open stream socket");
// Bind local address to allow the client to connect
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
if (bind
(socketfd, (struct sockaddr *) &server_addr,
sizeof(server_addr)) < 0)
perror("server: can't bind local address");
listen(socketfd, 5);
for (;;) {
client_len = sizeof(client_addr);
if((new_sd = accept(socketfd, (struct sockaddr *) &client_addr, &client_len)) < 0) {
printf("Error aceptando peticiones\n");
exit(0);
}
else {
id = clientes;
vectorClientes[id].socket = new_sd;
vectorClientes[id].sign_in = 0;
fflush(stdout);
clientes++;
if ( (status = pthread_create(&hilos[id],NULL,gestionaCliente,(void *)&id)) )
{
printf("Error al crear el hilo\n");
exit(0);
}
}
}
close(socketfd); // Close original socket
return 0;
}
void subCadena(char *subCad, char *cad, int inicio, int cuantos)
{
int i,j=0;
for(i=inicio;i<inicio+cuantos && cad[i]!='\0';i++)
{
subCad[j]=cad[i];
j++;
}
subCad[j]='\0';
}
int buscarCliente(char* usuario)
{
int i;
for(i=0;i<clientes;i++)
{
if(strcmp(vectorClientes[i].usuario,usuario ) == 0 && vectorClientes[i].sign_in == 1)
return vectorClientes[i].socket;
}
return -1;
}
void* gestionaCliente(void* p)
{
int *ide, id;
ide = (int* ) p;
id = *ide;
char buffer[MAXLINE], nombre[MAXLINE-4], temp[MAXLINE-8];
int i, longitud, destino;
while(1)
{
recv(vectorClientes[id].socket,buffer,MAXLINE,0);
printf("\nid%d\n", id);
if(strstr(buffer, "ADD") && vectorClientes[id].sign_in == 0) {
longitud = strlen(buffer);
//Le quitamos el ADD
subCadena(nombre, buffer, 4, longitud-4);
strcpy(vectorClientes[id].usuario, nombre);
//Se informa a todos menos a él mismo y al que se haya ido
strcpy(buffer, "El usuario ");
strcat(buffer, nombre);
strcat(buffer, " ha entrado en el chat.");
for(i = 0; i < clientes; i++)
if (i != id && vectorClientes[i].sign_in == 1)
send(vectorClientes[i].socket,buffer,MAXLINE,0);
vectorClientes[id].sign_in = 1;
}
if(strstr(buffer, "LIST") && vectorClientes[id].sign_in == 1) {
//Se envia al cliente todos los usuarios menos los que hayan abandonado la sesión
for(i = 0; i < clientes; i++){
if(vectorClientes[i].sign_in == 1)
send(vectorClientes[id].socket, vectorClientes[i].usuario, MAXLINE, 0);
}
}
if(strstr(buffer, "END") && vectorClientes[id].sign_in == 1) {
//Se informa a todos menos a él mismo y al que se haya ido
strcpy(buffer, "El usuario ");
strcat(buffer, vectorClientes[id].usuario);
strcat(buffer, " ha abandonado en el chat.");
bzero(vectorClientes[id].usuario, MAXLINE);
for(i = 0; i < clientes; i++)
if (i != id && vectorClientes[i].sign_in == 1)
send(vectorClientes[i].socket,buffer,MAXLINE,0);
vectorClientes[id].sign_in = 0;
}
if(strstr(buffer, "TEXT") && !strstr(buffer, "TEXT TO") && vectorClientes[id].sign_in == 1) {
longitud = strlen(buffer);
subCadena(temp, buffer, 5, longitud-5);
//Se envía a todos menos a él mismo y al que se haya ido
bzero(buffer, MAXLINE);
strcat(buffer, vectorClientes[id].usuario);
strcat(buffer, " dice: ");
strcat(buffer, temp);
for(i = 0; i < clientes; i++)
if (i != id && vectorClientes[i].sign_in == 1)
send(vectorClientes[i].socket,buffer,MAXLINE,0);
}
if(strstr(buffer, "TEXT TO") && vectorClientes[id].sign_in == 1) {
//Le quitamos el TEXT TO
subCadena(nombre, buffer, 8, MAXLINE-8);
//Nos quedamos sólo con el nombre, quitando desde el primer espacio en blanco hasta el final
strtok(nombre," ");
//Se obtiene el socket destino
destino = buscarCliente(nombre);
longitud=strlen(nombre);
strcpy(nombre, vectorClientes[id].usuario);
strcat(nombre, " dice: ");
//Recortamos el TEXT TO, el nombre, y los dos espacios hasta el mensaje(se suma solo uno (un espacio)
//porque empieza a recorrer longitud(instruccion)+longitud(nombre) desde la pos. 0 de la cadena.
subCadena(temp, buffer, 8+longitud+1, MAXLINE-(8+longitud+1));
strcat(nombre, temp);
if(destino != -1)
send(destino, nombre, MAXLINE, 0);
}
fflush(stdout);
}
close(vectorClientes[id].socket);
}
#!/usr/bin/python# -*- coding: utf-8 -*-#Servidorimportstringimportthreadingimportsocket
clientes ={'nombre':[],'socket':[]}class gestionaClientes(threading.Thread):
def__init__(self,socket):
threading.Thread.__init__(self)self.conn=socketself.conectado=Falseself.data=''def run(self):
whileTrue:
self.data=self.conn.recv(1024)if'ADD'inself.data:
#Busca si ya ha sido insertadoif(self.conectado==False):
self.conectado=True
clientes['nombre'].append(self.data[4:])
clientes['socket'].append(self.conn)for i in clientes['socket']:
if i !=self.conn:
i.send(self.data[4:]+" ha entrado en el chat.")else:
self.conn.send("Ya estás en el chat.")print clientes
if('LIST'inself.data)and(self.conectado==True):
for i in clientes['nombre']:
print"enviado a "+str(self.conn)+" "+str(i)self.conn.send(i+" ")print clientes
if('END'inself.data):
if(self.conectado==True):
for i in clientes['socket']:
if i ==self.conn:
nombre = clientes['nombre'][clientes['socket'].index(i)]
clientes['nombre'].remove(nombre)
clientes['socket'].remove(i)self.conectado=Falsefor i in clientes['socket']:
if i !=self.conn:
i.send(nombre+" ha salido del chat.")self.conn.send(" ")else:
self.conn.send(" ")if('TEXT'inself.dataand'TEXT TO'notinself.data)and(self.conectado==True):
for i in clientes['socket']:
if i !=self.conn:
i.send(clientes['nombre'][clientes['socket'].index(self.conn)]+" dice: "+self.data[5:])print clientes
if('TEXT TO'inself.data)and(self.conectado==True):
palabras =self.data[8:].split()#busca si existe alguien con ese aliasfor i in clientes['nombre']:
if i == palabras[0]:
#Prepara el mensajedel palabras[0]
mensaje =string.join(palabras,' ')
clientes['socket'][clientes['nombre'].index(i)].send(clientes['nombre'][clientes['socket'].index(self.conn)]+" dice: "+mensaje)self.conn.close()#creamos socket pasivo y escuchamos en el puerto 9000
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((socket.gethostname(),9000))
s.listen(5)while(True):
conn, addr = s.accept()
gestionaClientes(conn).start()
#!/usr/bin/python
# -*- coding: utf-8 -*-
#Servidor
import string
import threading
import socket
clientes = {'nombre':[], 'socket':[]}
class gestionaClientes(threading.Thread):
def __init__(self, socket):
threading.Thread.__init__(self)
self.conn = socket
self.conectado = False
self.data = ''
def run(self):
while True:
self.data = self.conn.recv( 1024 )
if 'ADD' in self.data:
#Busca si ya ha sido insertado
if(self.conectado == False):
self.conectado = True
clientes['nombre'].append(self.data[4:])
clientes['socket'].append(self.conn)
for i in clientes['socket']:
if i != self.conn:
i.send(self.data[4:]+" ha entrado en el chat.")
else:
self.conn.send("Ya estás en el chat.")
print clientes
if ('LIST' in self.data) and (self.conectado == True):
for i in clientes['nombre']:
print "enviado a "+str(self.conn)+" "+str(i)
self.conn.send(i+" ")
print clientes
if ('END' in self.data):
if (self.conectado == True):
for i in clientes['socket']:
if i == self.conn:
nombre = clientes['nombre'][clientes['socket'].index(i)]
clientes['nombre'].remove(nombre)
clientes['socket'].remove(i)
self.conectado = False
for i in clientes['socket']:
if i != self.conn:
i.send(nombre+" ha salido del chat.")
self.conn.send(" ")
else:
self.conn.send(" ")
if ('TEXT' in self.data and 'TEXT TO' not in self.data) and (self.conectado == True):
for i in clientes['socket']:
if i != self.conn:
i.send(clientes['nombre'][clientes['socket'].index(self.conn)]+" dice: "+self.data[5:])
print clientes
if ('TEXT TO' in self.data) and (self.conectado == True):
palabras = self.data[8:].split()
#busca si existe alguien con ese alias
for i in clientes['nombre']:
if i == palabras[0]:
#Prepara el mensaje
del palabras[0]
mensaje = string.join(palabras, ' ')
clientes['socket'][clientes['nombre'].index(i)].send(clientes['nombre'][clientes['socket'].index(self.conn)]+" dice: "+mensaje)
self.conn.close()
#creamos socket pasivo y escuchamos en el puerto 9000
s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
s.bind( ( socket.gethostname(), 9000 ) )
s.listen( 5 )
while(True):
conn, addr = s.accept()
gestionaClientes(conn).start()
Hay bastantes mejoras en Grub Customizer 2.0. Entre ellas, la posibilidad de seleccionar la entrada por defecto que está seleccionada para arrancar, el tiempo límite de interacción, o el establecimiento de parámetros del núcleo.
Además en el caso de Grub 2.0 hay algunas características especiales como cambiar los colores del menú o la imagen de fondo. Para instalar la aplicación en Ubuntu podéis hacer lo siguiente:
RPC (Remote Procedure Call) es una tecnología, tradicionalmente empleada en ambiente UNIX, que permite el desarrollo de sistemas de procesamiento distribuido basados en el paradigma procedimental.
Una llamada a un procedimiento (función o subrutina) es un método bien conocido para transferir el control de una parte del programa a otra, con un retorno del control a la primera. Asociado con la llamada a un procedimiento están el pase de argumentos y el retorno de uno o varios resultados. Cuando el código que invoca a un procedimiento y dicho procedimiento están en un mismo proceso en un computador dado, se dice que ha ocurrido una llamada a un procedimiento local.
Por el contrario, en una llamada a un procedimiento remoto (RPC, Remote Proceure Call) el sistema local invoca, a través de la red, a una función alojada en otro sistema. Lo que se pretende es hacerle parecer al programador que está ocurriendo una simple llamada local. Este es el flujo de datos en el modelo RPC:
Para este ejemplo se va a asumir que el entorno de trabajo ahora mismo es linux con gcc y rpcgen instalado.Los pasos para hacer que todo funcione son 4, y se describen a continuación:
La definición del XDR, usando ese lenguaje para especificar qué funciones quiere “publicar” el servidor, y cómo se han de llamar por parte del cliente.
La implementación de los métodos remotos, para tener un código que ejecutar cuando nos hagan una llamada remota.
La implementación de un cliente que invoque los métodos del servidor.
La compilación de todo esto y una ejecución de prueba.
XDR
XDR (External Data Representation) es un estándar para la descripción y codificación de datos que utiliza un lenguaje cuya sintaxis es similar a la del lenguaje de programación C. Es empleado principalmente en la transferencia de información entre diferentes arquitecturas computacionales y se ubica dentro de la capa de presentación del modelo ISO. Involucra un mecanismo de tipificado implícito (Implicit Typing), es decir que sólo viaja el valor de la variable por la red.
Es importante resaltar que XDR no es un lenguaje de programación, sino una especificación que incluye un lenguaje de descripción de datos el cual es extendido por ONC-RPC para la definición de procedimientos remotos. Los tipos de datos de XDR presentan cierta similitud con los tipos de datos de C. Algunos de estos son:
int
unsigned int
enum
bool
hyper
unsigned hyper
float
double
quadruple (punto flotante de cuádruple precisión)
opaque (de longitud fija o variable)
string
array (de longitud fija o variable)
struct
union (uniones discriminadas)
void
constant
El lenguaje especificado por RPC es idéntico al lenguaje de XDR, excepto que agrega la definición de «programa», cuya gramática es mostrada a continuación:
Lo primero es definir qué métodos está ofreciendo el servidor así como los parámetros que admite y el tipo de retorno. Esto lo hacemos en un fichero .x como el siguiente:
1
2
3
4
5
program HORA_PROG {
version HORA_VERS {
string HORA(void) = 1; /*devuelve una cadena*/
} = 1;
} = 0x31230000;
program HORA_PROG {
version HORA_VERS {
string HORA(void) = 1; /*devuelve una cadena*/
} = 1;
} = 0x31230000;
Las partes importantes del fichero son la definición de la estructura Operandos (en el anterior ejemplo no la hay) y el program { version { con las definiciones de las funciones. La definición de Operandos permite definir estructuras prácticamente arbitrarias como tipos para parámetros y retornos. Un ejemplo de fichero XDR con definición de operandos sería el siguiente, aunque no se usa en esta ocasión:
1
2
3
4
5
6
7
8
9
struct hora {
char arg[90];
};
program HORA_PROG {
version HORA_VERS {
hora HORA(void) = 1; /*devuelve una cadena*/
} = 1;
} = 0x31230000;
struct hora {
char arg[90];
};
program HORA_PROG {
version HORA_VERS {
hora HORA(void) = 1; /*devuelve una cadena*/
} = 1;
} = 0x31230000;
Los program son una manera de agrupar primitivas (he usado el convenio de notación de Sun para nombrarlo). El valor 0×31230000 es un identificador único para el servicio, de manera que que pueda ser localizado por el cliente y el servidor.
Dentro de program, tenemos una version que es la que contiene los métodos. Esto es así porque RPC admite varias versiones de los métodos para un mismo programa (por motivos de compatibilidad hacia atrás, entre otros). En este caso tenemos una única (la 1).
Las definiciones de las funciones son prácticamente iguales a las que haríamos en C, con una consideración adicional (para este caso): a la hora de devolver una cadena no devolvemos char*, sino string. El convenio de Sun que impone poner aquí los nombres de las funciones en mayúscula. Si devolvemos un operando definido, simplemente se escribe antes del nombre de la función (hora). Por tanto, una vez terminada esta parte, he definido un servicio con una primitiva que devuelve la hora.
Implementación del servidor
El siguiente paso es la implementación del servidor; necesitamos crear el código que se ejecutará efectivamente en la máquina remota. Lo bueno de este sistema es que podemos hacer esencialmente lo que queramos: consultar bases de datos, reaizar cálculos, invocar otros servicios remotos, etc. De este modo, los procedimientos RPC pueden ser usados como rutinas de computación, de administración/gestion u obtención de datos.
En este caso, por tener un ejemplo concreto, la implementación en sí de esas funciones es trivial. Lo que interesa ahora es la sintaxis (su significado), la manera en que se nos ofrecen los datos y cómo devolver nuestros resultados. El siguiente código utiliza la primera definición de XDR (sin operandos definidos)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <rpc/rpc.h>#include "time.h"char**hora_1_svc(struct svc_req *rqstp){staticlong timeval;// debe ser una variable estáticastaticchar*ptr;// debe ser una variable estática
timeval =time((long*)0);
ptr =ctime(&timeval);return(&ptr);}int hora_prog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result){
xdr_free(xdr_result, result);return(1);}
#include <rpc/rpc.h>
#include "time.h"
char **hora_1_svc(struct svc_req *rqstp) {
static long timeval; // debe ser una variable estática
static char *ptr; // debe ser una variable estática
timeval = time((long *) 0);
ptr = ctime(&timeval);
return(&ptr);
}
int hora_prog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) {
xdr_free(xdr_result, result);
return (1);
}
El tipo de retorno es un puntero al tipo que habíamos definido en el XDR (notar que string=char*). Además, a la hora de devolver, devolvemos referencias a datos estáticos (ya que si devolviésemos referencias a la pila éstas no serían válidas al salir de la función). Este comportamiento es configurable en las opciones de rpcgen.
El tipo de los argumentos pueden ser también punteros a los tipos originales (Operandos) (no hay en el código de arriba), y además se añade un nuevo parámetro que es siempre struct svc_req*. El motivo de lo primero tiene que ver con cómo nos llegan los datos remotos (y con cómo los podemos modificar). El segundo parámetro contiene información sobre la petición que nos han hecho y el cliente que la ha realizado.
Con respecto al nombre de las funciones, estas se corresponden con el nombre descrito en el fichero XDR, añadiendo _i_svc, donde i es el número de versión que estamos implementando. Esto es así porque podemos tener diferentes versiones de una misma función, y con este sistema evitamos colisiones en los nombres.
Implementación del cliente
Para completar la parte de implementación, tenemos que escribir un código de cliente que haga uso de los procedimientos remotos. Éste se escribe como un programa local normal, salvo por que hacemos llamadas a ciertas funciones que son las que se encargan de hacer el trabajo de RPC.
El #include se hace sobre un fichero que todavía no hemos creado, hora.h. Éste se genera automáticamente con el comando rpcgen, y suele ser el nombre del fichero .x intercambiando la extensión.
Esta llamada devuelve un stub, que es una estructura con toda la información que necesita nuestra biblioteca de rpc. Los parámetros que admiten son el nombre de host del servidor (localhost si hacéis las pruebas en modo local, o una ip, y dos constantes que básicamente tienen que ser el nombre que pusimos después de program y después de version en el fichero .x del principio. El último parámetro indica el protocolo de transporte que queremos usar para nuestra petición.
Las llamadas en sí son muy sencillas, y utilizan exactamente el mismo interfaz que implementamos en el servidor. El segundo parámetro es el stub del cliente. Como esta función no tiene parámetros, se introduce NULL.
Compilación
rpcgen es un programa que toma como argumentos un fichero .x y genera el código de los stubs.
$ rpcgen hora.x
$ rpcgen hora.x
Esta línea ha debido generar los siguientes ficheros: hora.h, hora_svc.c, hora_clnt.c y hora_xdr.c (en el caso de haber definido una estructura de operandos en el XDR).
El primero, hora.h, están las definiciones de tipos, constantes y funciones generales comunes tanto al servidor como al cliente. Es por eso por lo que hemos hecho el include en nuestras implementaciones.
Los ficheros hora_clnt.c y hora_svc.c contienen respectivamente el código los stubs de cliente y de servidor. Dichos stubs son los que se encargan efectivamente de que la comunicación sea transparente: transforman los argumentos y retornos que usamos en las funciones publicadas como servicios y también las decodifican en el otro extremo.
hora_xdr.c es el módulo que contiene información sobre los tipos que ha definido el usuario. Como sólo hemos utilizado tipos básicos, no ha hecho falta.
Lo único que falta es compilar el programa cliente y el servidor como si de cualquier otro programa se tratase. Para el cliente, como hemos hecho una implementación en C, usamos gcc como compilador:
$ gcc client.c hora_clnt.c -o client
$ gcc client.c hora_clnt.c -o client
Para el servidor:
$ gcc server.c hora_svc.c -o server
$ gcc server.c hora_svc.c -o server
Ejecución
Si todo lo anterior ha ido bien, basta con ejecutar el servidor:
$ ./server
$ ./server
y ejecutar el cliente:
$ ./client localhost
La hora del servidor localhost es Thu Jul 2117:31:562011
$ ./client localhost
La hora del servidor localhost es Thu Jul 21 17:31:56 2011
Nota
En recientes versiones de Ubuntu, tras ejecutar el servidor se obtiene un mensaje como este:
Cannot register service: RPC: Unable to receive; errno = Connection refused
unable to register (HORA_PROG, HORA_VERS, udp).
Esto es porque es necesario instalar el paquete portmap, lo cual puede realizarse con el siguiente comando en consola:
sudoapt-get install portmap
sudo apt-get install portmap
Espero que el tema os haya gustado. Dejo abajo el enlace al código del ejemplo.