Conectar dos ordenadores en linux

Necesito conectar dos ordenadores por el puerto serie en linux de tal forma que se envíen y reciban los datos que yo le diga para una práctica de una asignatura de transmisión de datos y redes de computadores. En vez de dos ordenadores, estoy usando uno en el que conecto los dos puertos serie que tiene mediante el cable adecuado para ello. Mi problema es que, una vez que establezco la comunicación en un sentido (tengo un programa emisor y un receptor) no puedo luego establecerla en el otro sin cerrar el puerto, cosa que no nos permiten los profesores. Yo envío datos del emisor al receptor, el receptor me los recibe sin problema, y cuando el receptor crea una trama de confirmación y se la envía al emisor, ambos procesos se quedan colgados de tal forma que, aunque no me bloquean el ordenador, sí me bloquean los puertos y tengo que reiniciar el ordenador. Los profesores dicen que debo tener algún error tonto en el código, pero se niegan a mirármelo, y tengo que entregar la práctica (con otras dos partes que no puedo hacer hasta que no tenga acabada esta) el martes de la semana que viene! ¿Puedes ayudarme? ¿Se te ocurre que puede estar pasándome? Te pongo aquí el código, por si puedes mirarlo tú y ves qué puede estar dándome problemas. Por cierto, está escrito en C, y el linux que tengo es RedHat 9 en el que actúo como root para esta práctica:
Emisor.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define BAUDRATE B9600 //B38400
#define MODEMDEVICE "/dev/ttyS0"
#define MODEMDEVICE1 "/dev/ttyS1"
#define _POSIX_SOURCE 1 /*fuentes cumple POSIX*/
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
int EnviaDatos(int fd,char *datos,int longitud){
int iret,total=0;
while(total<longitud){
iret=write(fd,&datos[total],longitud-total);
if(iret<0) return(iret);
else total += iret;
};
return(total);
};
int RecibeDatos(int fd, char *datos, int longitud){
return(read(fd,datos,longitud));
};
void ConfiguraPuerto(int fd, struct termios * newtio){
newtio->c_cflag=BAUDRATE|CRTSCTS|CS8|CLOCAL|CREAD;
newtio->c_iflag=IGNPAR;
newtio->c_oflag=0;
/*pone el modo entrada (no-canónico, sin eco,...)*/
newtio->c_lflag=0;
newtio->c_cc[VTIME]=0; /*temporizador entre carácter, no usado*/
newtio->c_cc[VMIN]=0; /*no bloquea lectura*/
tcflush(fd,TCIFLUSH);
tcsetattr(fd,TCSANOW,newtio);
}
void RestauraPuerto(int fd, struct termios * oldtio){
tcsetattr(fd,TCSANOW,oldtio);
}
main(int argc, char *argv[]){
int fd, fd1, c,res;
struct termios oldtio, newtio;
char buf[255];
int contador;
if(argc!=2){
printf("Error en los argumentos del programa.\n");
printf("Se ejecuta como ./redes <puerto> (siendo puerto 1 para COM1 o 2 para COM2)\n");
exit(-1);
}
if(atoi(argv[1])==1){
fd=open(MODEMDEVICE, O_RDWR| O_NOCTTY);
fd1=open(MODEMDEVICE, O_RDWR| O_NOCTTY);
if(fd<0) { perror(MODEMDEVICE);exit(-1); }
if(fd1<0) { perror(MODEMDEVICE);exit(-1); }
}else{
if(atoi(argv[1])==2){
fd=open(MODEMDEVICE1, O_RDWR| O_NOCTTY);
fd1=open(MODEMDEVICE1, O_RDWR| O_NOCTTY);
if(fd<0) { perror(MODEMDEVICE1);exit(-1); }
if(fd1<0) { perror(MODEMDEVICE1);exit(-1); }
}
else{
printf("Error en los argumentos del programa.\n");
printf("Se ejecuta como ./redes <puerto> (siendo puerto 1 para COM1 o 2 para COM2)\n");
exit(-1);
}
}
tcgetattr(fd,&oldtio);
bzero(&newtio,sizeof(newtio));
ConfiguraPuerto(fd, &newtio);
for(contador=0; contador<50; contador++){
buf[contador]='z';
}
//enviar
EnviaDatos(fd, buf, 50);
RestauraPuerto(fd, &oldtio);
}
RECEPTOR.C
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define BAUDRATE B9600 //B38400
#define MODEMDEVICE "/dev/ttyS0"
#define MODEMDEVICE1 "/dev/ttyS1"
#define _POSIX_SOURCE 1 /*fuentes cumple POSIX*/
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
int EnviaDatos(int fd,char *datos,int longitud){
int iret,total=0;
while(total<longitud){
iret=write(fd,&datos[total],longitud-total);
if(iret<0) return(iret);
else total += iret;
};
return(total);
};
int RecibeDatos(int fd, char *datos, int longitud){
return(read(fd,datos,longitud));
};
void ConfiguraPuerto(int fd, struct termios * newtio){
newtio->c_cflag=BAUDRATE|CRTSCTS|CS8|CLOCAL|CREAD;
newtio->c_iflag=IGNPAR;
newtio->c_oflag=0;
/*pone el modo entrada (no-canónico, sin eco,...)*/
newtio->c_lflag=0;
newtio->c_cc[VTIME]=0; /*temporizador entre carácter, no usado*/
newtio->c_cc[VMIN]=0; /*no bloquea lectura*/
tcflush(fd,TCIFLUSH);
tcsetattr(fd,TCSANOW,newtio);
}
void RestauraPuerto(int fd, struct termios * oldtio){
tcsetattr(fd,TCSANOW,oldtio);
}
main(int argc, char *argv[]){
int fd,fd1,c,res;
struct termios oldtio, newtio;
char buf[255];
if(argc!=2){
printf("Error en los argumentos del programa.\n");
printf("Se ejecuta como ./redes <puerto> (siendo puerto 1 para COM1 o 2 para COM2)\n");
exit(-1);
}
if(atoi(argv[1])==1){
fd=open(MODEMDEVICE, O_RDWR| O_NOCTTY);
fd1=open(MODEMDEVICE, O_RDWR| O_NOCTTY);
if(fd<0) { perror(MODEMDEVICE);exit(-1); }
if(fd1<0) { perror(MODEMDEVICE);exit(-1); }
}else{
if(atoi(argv[1])==2){
fd=open(MODEMDEVICE1, O_RDWR| O_NOCTTY);
fd1=open(MODEMDEVICE1, O_RDWR| O_NOCTTY);
if(fd<0) { perror(MODEMDEVICE1);exit(-1); }
if(fd1<0) { perror(MODEMDEVICE1);exit(-1); }
}
else{
printf("Error en los argumentos del programa.\n");
printf("Se ejecuta como ./redes <puerto> (siendo puerto 1 para COM1 o 2 para COM2)\n");
exit(-1);
}
}
tcgetattr(fd,&oldtio);
bzero(&newtio,sizeof(newtio));
ConfiguraPuerto(fd, &newtio);
while(STOP==FALSE){
res=RecibeDatos(fd, buf, 255);
buf[res]=0;
if(res>0){
printf(":%s:%d\n",buf,res);
STOP=TRUE;
}
}
RestauraPuerto(fd, &oldtio);
}

1 Respuesta

Respuesta
1
Viendo el código fuente no parece que exista ningún problema con los programas. Solo el hecho de que el receptor no vacía el puerto de entrada y envía el ack ni el emisor espera la llegada del ack, pero me imagino que lo habías quitado porque así si que te funcionaba. Por el resto lo único que se me ocurre que puede llegar a pasarte es el hecho de que el receptor intenta recibir una trama de 255 y en cuanto recibe algo termina, pero, ¿y si no ha llegado a leerlo todo?, el puerto se quedaría enganchado porque el receptor ya no escucha y el emisor satura el buffer (aunque dado que el puerto esta a 9600 baudios es poco probable que lo llegue a saturar).
La verdad es que es lo único que se me ocurre que te pueda ocurrir. Aparte de eso no veo nada malo en el código.
¿El puerto no se vacía con tcflush()? El profesor explicó que esa ordena era para limpiar el puerto, así que éste quedaría libre. Efectivamente, no envía nadie confirmación, ya que así es lo único que me funciona para que no se me bloquee el ordenador. Éste main no es el que de verdad necesito, sólo es una prueba para comprobar que funcionan las funciones creadas. El ejemplo que pongo aquí no me bloquea los puertos (aunque es cierto que se pierden datos, pero ya te digo que me da igual por ahora). Sin embargo, con las mismas funciones, se me bloquea si en el emisor, justo detrás de emitir y sin cambiar absolutamente nada añado:
while(STOP==FALSE){
res=RecibeDatos(fd, buf, 255);
buf[res]=0;
if(res>0){
printf(":%s:%d\n",buf,res);
STOP=TRUE;
}
}
Es decir, intento que ahora reciba, y en el receptor, igual que antes, tras recibir, añado:
EnviaDatos(fd, buf, 50);
Para que ahora envíe. Así intento simular la llegada de datos y una confirmación por parte del receptor que más adelante arreglaría convirtiendo en tramas y controlando la cantidad de datos que recibo.
Al hacer los cambios que te comento, me ocurre que el receptor se queda en el bucle while dando vueltas y nunca recibe nada del emisor, además de que me bloquea el puerto de tal modo que tengo que reiniciar el ordenador para poder usarlo. Si detrás de cada comunicación pongo:
RestauraPuerto(fd, &oldtio);
tcgetattr(fd,&oldtio);
bzero(&newtio,sizeof(newtio));
ConfiguraPuerto(fd, &newtio);
Entonces no se me bloquea, pero puedo perder datos porque el emisor emita antes de que el puerto esté abierto y los profesores no aceptan esta solución. Me han dicho que el error debo tenerlo en las funciones, pero no tengo ni idea de donde. ¿Se te ocurre algo que pueda ayudarme?
Muchísimas gracias y un saludo.
Si lo que comentas es así, la verdad es que no encuentro ningún error en las funciones. Lastima que no tenga un cable null modem para poder probarlo, pero si existe un error (y por lo que me cuentas así es) yo no lo encuentro. Si no es por no poner el tcflush justo después de enviar los datos en el emisor, no le veo el error.
Siento no poder serte de más ayuda.
Probaré lo que me dices de poner el tcflush justo después de enviar datos, y a ver si se me arregla. Cuando lo pruebe te cuento y, si eso, te puntúo, ¿de acuerdo? Muchas gracias de todas formas.
Lapsus lingue al escribir. Me refería aponer el tcflush en el receptor, para limpiar el buffer del puerto, peero de todas formas es una idea sin mucho fundamento. Se supone que el emisor escribe en el buffer y se pone en modo escucha. El recepto debería ser quien, una vez recibe los datos, vacía posibles bytes restantes y envía la contestación. Aparte de eso sigue sin ocurrirseme otra cosa.
No, tampoco me funciona eso. ¿No puede ser porque estén en el COM1 los driver del ratón o algo de eso? Es que empiezo a creer de verdad que el código está bien y que el problema es del cable, del ordenador o de los puertos.
He probado lo que me dices y no me va bien tampoco, me sigue pasando lo mismo. De todas formas, he estado pensando que eso no me puede valer porque, según mis profesores nos han dicho, el tcflush es para limpiar los puertos, y si lo pongo después de enviar me puede limpiar los puertos cuando haya aún en ellos información que necesite el receptor, ¿no? Bueno, ¿alguna otra idea? Si no se te ocurre nada más, no te preocupes. Agradezco mucho tu interés de todas formas. Un saludo.
Lo que me comentas de los puertos ya raya la extrañez total. Eso no hay quien lo entienda. ¿Te ha pasado en otros ordenaores o es en el tuyo solo? Te lo pregunto porque empiezo a pensar que pueda llegar a ser un bug del kernel de tu versión de linux.
Si el ratón o tienes puesto en el puerto ps2, entonces no tendrás ningún driver conectado a los com, así que eso no será. Una prueba que puedes hacer es no utilizar las funiones de C de escritura y lectura, sino utilizar los enlaces del propio linux (son los ficheros /dev/ttyS0 y /dev/ttyS1). Escribiendo en esos ficheros es como escribir en el puerto, pero pasando por el kernel obligatoriamente.
Los pipelines (o tuberias) son el simbolo "|" (Altgr+1). En linux se establecen las tuberías para poder pasar datos de un comando a otro, de forma que la salida estándar del primer comando se convierte en la entrada estándar del segundo y así scesivamente. Se utilizan cuando quieres encadenar varios comandos. Por ejemplo, si quieres mostrar todos los ficheros de un directorio, el comando es "ls". Si quieres que una salida en pantalla que es muy larga te aparezca paginada, el comando es "more". Si quieres mostrar el listado del directorio y que ese listado te aparezca paginado, el comando sera "ls | more". Los pipelines se pueden poner uno tras otro hasta conseguir generar una larga cadena de comandos.
Después de hacer varias pruebas, los resultados han sido un poco extraños. He obtenido resultados distintos ejecutando en un puerto y en el otro. He probado haciendo una única emisión y una única recepción (primero poniendo un puerto como receptor y luego poniéndolo como emisor) y en un caso llegaba a recibir más datos que en el otro. También he probado haciendo con el emisor varias emisiones y recibiendo con el receptor las veces necesarias también, y no se me ha bloqueado el ordenador, me ha recibido las dos veces con los dos puertos, pero de nuevo con uno me recibe más que con el otro. Y por último, he probado a que el emisor haga una emisión y una recepción y el receptor una recepción y una emisión y se me ha bloqueado en los dos casos, pero en uno de los casos el emisor no me llegaba ni siquiera a emitir la primera vez mientras que en el otro si me emitía y se colgaba tras esto. ¿Qué opinas de estos resultados? ¿Puede ser lo de los driver del ratón? ¿Cómo puedo limpiar los puertos de driver? Yo el ratón no lo tengo en ningún puerto serie, lo tengo en otro puerto, PS2 creo que se llama (uno con el conector redondo), pero creo que aunque lo tenga así Linux puede estar cargándome driver donde no debe.
Respecto al código en sí, he estado comparándolo con el de compañeros que sí les funciona y no le veo diferencias. Por lo pronto, voy a hacer la práctica escribiendo y leyendo de pipelines (un consejo que me han dado unos amigos y que dicen que les funciona) y para la entrega le pongo de nuevo los puertos, pero aún así me gustaría saber qué es lo que me está ocurriendo, ya por curiosidad más que nada. Por cierto, ¿entiendes de pipelines? ¿Puedes explicarme un poco como se usan?
Gracias de nuevo.
Problema del ordenador o del cable lo veo muy difícil. Que tengas el ratón configurado para el com1 si que podría ser. El receptor recibe el paquete por el com2 y lo envía, pero al llegar al com1, el kernel lo atrapa y lo envía como evento de ratón, por eso el emisor no recibe nada.
Una prueba bastante simple es poner el emisor en el com2 y el receptor en el 1. Si el receptor no recibe nada, ahí es donde está el problema.
Sí, lo de los puertos ya lo sabía, es lo que hago, si no me equivoco. Y en realidad es bastante sencillo, de ahí que me extrañara desde un principio lo que me pasaba.
Con la explicación de los pipelines lo he comprendido todo, y me parece que es una tontería ahora que me lo aclaras, pero si solo me sirve para un sentido, no puedo usarlo.
Voy a probar con el fichero y si todo me va bien y no tengo más preguntas, finalizo la pregunta y te puntúo, ¿de acuerdo?
Muchas gracias por tu ayuda y tu interés.
No sé, pero yo escribo usando /dev/ttyS0 y dev/ttyS1. Hago el open, el read y el write sobre los puertos como si fueran ficheros, tal y como tú dices, así que no sé que está pasando. Quizás sea lo del kernel de mi versión de Linux que dices, porque hay varios compañeros a los que les pasa lo mismo y creo que tienen todos la misma versión que yo, aunque no estoy segura. Desde luego, menuda paranoia con esta práctica.
Acerca de los pipelines: Si es tal y como tú me dices, ¿cómo puedo usarlos para enviar de unos y recibir en otros? Es que son bastante raros para mí. ¿Sería algo así como: envío(datos)|recibo(datos)|imprimo datos en pantalla para ver si todo ha ido bien? ¡Menudo lío!
Los fichero ttyS0 y ttyS1 son las conexiones con los puertos com1 y com2. Escribir es ese fichero es como indicar al núcleo que se quiere poner esos datos en el puerto, así que la parte más compleja la realiza el. El receptor solo tiene que esperar a encontrar datos en ese fichero en un bucle y te debería llegar en uno lo que has puesto en el otro.
Por el uso de pipelines, es bastante más sencillo que el uso de ficheros. Modifcas el emisor y el receptor para que lean y escriban sobre la salida estándar, con las funciones típicas de C. Entonces ejecutas "emisor|receptor". La salida de emisor (los datos) pasaran a ser la entrada del receptor, por lo que es lo mismo que usar puertos, aunque solo admite una dirección (del emisor al receptor).
Con el uso de ficheros puedes hacerlo también, haciendo lo mismo que con los puertos ttyS0 y ttyS1, solo que con un fichero intermedio de verdad.
Por lo de limpiar los puertos de drivers, me temo que no es posible, a excepción de mirar uno a uno todos los dispositivos en busca del causante, pero muchos de ellos forman parte del nuecleo, así que no podrás hacer nada con eso.
Una cosa que quería comentarte: ¿Podría también para probar leer y escribir directamente de un fichero fingiendo que es el puerto? Es que he pensado que es otra forma de hacerlo, en vez de con pipelines que entiendo menos, pero algunos compañeros me han dicho que a ellos le ha dado eso problemas por caracteres especiales que se meten en el fichero. ¿Eso es cierto o no tendría problemas?
Y otra cosa. ¿Cómo podría limpiar de todas formas los puertos de driver? ¡Por probar que no quede! Y si no me sirve, ya lo sé para otra vez.
Muchas gracias.
Ya que pruebas con un fichero de intercambio, asegurate nada más enviar de cerrarlo o hacerle un flush, dado que C se guarda lo que escribes en un buffer hasta que el fichero se cierra o se le obliga a escribirlo y el receptor no encontrará nada hasta que lo hagas.
Por el resto, buena suerte.
Muchas gracias por tu ayuda, tu interés y la rapidez en responderme. Al final, tal y como me dijiste, el problema no estaba en el código, ya que me funcionó ese programa en el aula de prácticas de la facultad. Gracias de nuevo y un saludo.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas