Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Sockets entre Java y C++

Estas en el tema de Sockets entre Java y C++ en el foro de C/C++ en Foros del Web. Muy buenas. Estoy programando un servidor en Java y un cliente en C++, de tal manera que se envían mensajes Strings entre ellos. El problema ...
  #1 (permalink)  
Antiguo 31/05/2014, 03:06
 
Fecha de Ingreso: mayo-2014
Mensajes: 2
Antigüedad: 10 años, 5 meses
Puntos: 0
Sockets entre Java y C++

Muy buenas.

Estoy programando un servidor en Java y un cliente en C++, de tal manera que se envían mensajes Strings entre ellos. El problema lo tengo con C++, que me está dando infinitos quebraderos de cabeza...

En mi servidor, creo el socket servidor que espera conexiones. Tras la conexión espero un mensaje del cliente con:

entrada = new DataInputStream(socketCliente1.getInputStream());
mensajeRecibido=entrada.readUTF();

En mi cliente creo el socket y lo conecto de la siguiente manera:

SOCKET ConnectSocket = INVALID_SOCKET;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
struct addrinfo *result = NULL, *ptr = NULL, hints;

ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;


iResult = getaddrinfo("192.168.0.17", DEFAULT_PORT, &hints, &result);
ptr = result;
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
freeaddrinfo(result);

El problema viene aquí... ¿Cómo envío el String al servidor Java?

Y si escribo con el servidor Java como:
salida = new DataOutputStream(socketCliente1.getOutputStream()) ;
salida.writeUTF("BLABLABLABLA");

¿Cómo podría recibirlo con mi cliente C++?

A ser posible no querría tocar el código en Java, puesto que tiene que ser a su vez compatible con clientes Java (los cuales no he tenido ningún problema en implementar).

Muchas gracias =)
  #2 (permalink)  
Antiguo 31/05/2014, 13:35
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 12 años, 3 meses
Puntos: 83
Respuesta: Sockets entre Java y C++

Envias el string con la funcion send:

Código C++:
Ver original
  1. int tlen, nbs;
  2. string texto;
  3.  
  4. texto = "un ejemplo";
  5. tlen = texto.length();
  6.  
  7. nbs = send(ConnectSocket, texto.c_str(), tlen, 0);

Y recibes datos con recv:

Código C++:
Ver original
  1. int nbr;
  2. char buffer[100];
  3.  
  4. nbr = recv(ConnectSocket, buffer, sizeof(buffer), 0);

Una cosa a tener en cuenta: mientras que para enviar puedes convertir los tipos string a char*, para recibir necesariamente debes tener reservado un bloque de memoria (ya sea con char* o con void*, pero no con string.c_str()).

Otra cosa a tener en cuenta: ambas funciones pueden no ejecutar la accion completa, es decir, si envias un string de 4000 caracteres puede que al primer intento solo envie 1000. Esto significa que debes prestar atencion al valor de retorno de la funcion send (en el ejemplo 'nbs'), que te indica el nº de bytes del buffer proporcionado que pudieron ser enviados; si no puede enviarlos todos, tienes que ejecutar otra vez el send pero omitiendo los caracteres enviados y volver a comprovar el nº de bytes enviados, y en caso necesario volver a repetir.

Lo mismo para recv: la funcion del servidor de java hará automaticamente lo que te comente del send (en java siempre viene todo implementado, pero de forma transparente para el programador hará las comprovaciones de bytes enviados y bytes pendientes), pero el cliente en c++ no sabe cuantos datos le envia el servidor porque el send puede implicar uno o mas envios. Para eso estan los protocolos. P.ej. el protocolo http define una cabecera con lineas de informacion terminadas con \r\n, a la vez que la cabecera finaliza con otro \r\n:

Código C:
Ver original
  1. dato1\r\n
  2. dato2\r\n
  3. \r\n

Esto implica que el cliente necesariamente ejecutará recv hasta que reciba \r\n\r\n. Esto es solo la parte superficial de http, te lo he comentado a modo de ejemplo, pero si te interesa puedes buscar mas informacion por ahi, o preguntar aqui mismo :) Puedes definir tu propio protocolo como transmisiones de texto finalizadas con la serie "qwerty", de forma que el servidor enviará el texto seguido de "qwerty" (independientemente de si requiere hacer una o mas llamadas send para completar el envio), entonces el cliente ejecutará recv tantas veces como sea necesario hasta que haya recibido la terminacion "qwerty", luego simplemente descartará la terminacion y ya tendrá el texto valido. Tambien puedes arriesgarte a suponer que todas las transmisiones se ejecutan a la primera y omotir lo del protocolo, esto depende de ti y de la fiabilidad que quieres que tenga tu aplicacion.

Con esto quiero que veas que aunque el servidor envie "una prueba de texto" el cliente no sabe cuando debe parar de leer. Tienes que implementar algun tipo de protocolo, por simple que sea.

Aun otra cosa: la funcion recv retorna el nº de bytes copiados al buffer proporcionado, pero el buffer proporcionado contendrá datos basura (y mas aun cuando ejecutes el recv dentro de un ciclo). Para asegurarte que te quedas solo con los datos recibidos tienes que truncar el buffer al nº de bytes recibidos:

Código C:
Ver original
  1. int nbr;
  2. char buffer[100];
  3.  
  4. if((nbr = recv(ConnectSocket, buffer, sizeof(buffer), 0)) < sizeof(buffer) {
  5.     buffer[nbr] = '\0';
  6. }

Hay mas formas de truncar el buffer, usa la que mas te guste.


"...A ser posible no querría tocar el código en Java..."

No, el servidor corre por una parte, el cliente por otra aun cuando esten en la misma maquina. Eso si, si debes implementar algun tipo de protocolo tendras que tocar el servidor.

Espero que te sea de ayuda
Saludos
vosk

Última edición por vosk; 31/05/2014 a las 13:51
  #3 (permalink)  
Antiguo 01/06/2014, 00:34
 
Fecha de Ingreso: mayo-2014
Mensajes: 2
Antigüedad: 10 años, 5 meses
Puntos: 0
Respuesta: Sockets entre Java y C++

Muchísimas gracias por tu ayuda. De verdad que te agradezco la dedicación que le has puesto a tu respuesta explicándome lo que ocurre cuando envías y tal.

De momento he probado a intentar enviar un string con el código que me has puesto, pero me da un error en el servidor:

Error: Connection reset

¿Por qué puede ser?

En C tengo las librerías:

Código:
#include <string.h>
#include <string>
Y el string lo he tenido que declarar como:

Código:
std::string texto;
Muchas gracias.
  #4 (permalink)  
Antiguo 01/06/2014, 13:27
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 12 años, 3 meses
Puntos: 83
Respuesta: Sockets entre Java y C++

Primero lo facil: añade el namespace std:

Código C++:
Ver original
  1. #include <string>
  2. using namespace std;
  3.  
  4. int main() {
  5.     string texto = "ok";//ya no necesitas std::
  6.     ...
  7. }


"...he probado a intentar enviar un string con el código que me has puesto, pero me da un error en el servidor: Error: Connection reset..."

Ok, añade todas las comprovaciones de error en las funciones de sockets. P.ej. la funcion 'connect' retorna SOCKET_ERROR cuando no puede conectar ademas de aplicar un error que recuperas con WSAGetLastError(). Todas las funciones de sockets funcionan de forma similar: comprueba que retorna el valor esperado, en caso contrario recuperas el codigo del error y buscas en la tabla de codigos de error la descripcion que le corresponde (asi sabes donde encarar la correccion).

Si la aplicacion llega a 'send' sin lanzar ningun error entonces asegurate de que se estan enviando datos: send retornará SOCKET_ERROR en caso de error y >=0 en caso de exito, pero si retorna 0 aun siendo caso de existo es que algo esta sucediendo con el buffer de datos proporcionado, en este caso texto.c_str()

Otra cosa: respeta el tiempo de dialogo cliente-servidor. Si defines que el dialogo es algo como esto:

s: escucha
c: pide conexion
s: acepta conexion
(s: escribe saludo inicial, no es necesario)
(c: lee saludo inicial, no es necesario)
c: escribe consulta
s: lee consulta
s: escribe recurso
s: finaliza conexion
c: lee recurso
c: finaliza conexion
(s: aqui sigue escuchando para nuevas conexiones)

entonces tienes que esperar a cerrar el socket del cliente hasta que hayas leido lo que el servidor te responde; si cierras antes el servidor intenta el envio pero encuentra que no existe el cliente al que debe responder. En cualquier caso en situaciones normales si el servidor intenta el envio pero el cliente ha desconectado simplemente se descarta la peticion y cierra el socket (aun cuando se notifique un error).

La forma facil de encontrar 'cosas' es printar por consola todos los resultados que vas obteniendo: los valores de retorno de las funciones, el texto que esperas enviar, los bytes realmente enviados, etc... De todas formas ejecuta la aplicacion en el depurador, puede que haya algo el el codigo que no esté del todo bien.

Y, tal vez ya lo hayas leido, puedes echar un vistazo a la guia beej de programacion de sockets; puedes encontrarla por ahi, y en varios idiomas :)
Prueba esto de volcar los datos por pantalla, a ver si ves algo sospechoso o te lanza algun error.

Suerte

Saludos
vosk

Etiquetas: int, java, programa, sockets, string
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 13:21.