Problemas con la recepción usando sockets

Iniciado por AncientDragon, 24 Junio 2014, 16:25 PM

0 Miembros y 1 Visitante están viendo este tema.

AncientDragon

Buenas tardes a todos.

Actualmente estoy desarrollando una clase en C++ que usa sockets con la finalidad de que me sirva para testear cabeceras HTTP. El problema es que creía que estaba bien implementada pero con su uso me he dado cuenta de un problema que no sé como resolver.

La situación es que intento recibir solamente la cabecera (HEAD) de un determinado servidor y todo funciona correctamente. Sin embargo, si intento realizar esta misma petición más de una vez, parece que la respuesta del servidor queda solapada por las respuestas anteriores.

Para entender mejor el problema adjunto el código que uso así como la salida obtenida.

TCPSocket.h


#ifndef _TCPSOCKET_H_
#define _TCPSOCKET_H_

class TCPSocket
{
private:
int localSocket;

public:
TCPSocket();
~TCPSocket();
void Connect(const char *host, int port);
void Write(const char *data);
char *Read();
};

#endif


TCPSocket.cpp


#include <iostream>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "TCPSocket.h"

using namespace std;

TCPSocket::TCPSocket()
{
localSocket = socket(AF_INET, SOCK_STREAM, 0);
if (localSocket == -1) throw "No se puede crear el socket";
}

TCPSocket::~TCPSocket()
{
int result = close(localSocket);
if (result == -1) throw "No se puede cerrar el socket";
}

void TCPSocket::Connect(const char *host, int port)
{
struct hostent *server = gethostbyname(host);
if (server == NULL) throw "No se encuentra el host";

struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port);
serverAddress.sin_addr = *(struct in_addr *)(server->h_addr_list[0]);
bzero(&(serverAddress.sin_zero), 8);

cout << "Conectando con " << host << endl;
cout << "Conectando con " << inet_ntoa(serverAddress.sin_addr) << endl;

int result = connect(localSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
if (result == -1) throw "No se puede conectar con el host";
}

void TCPSocket::Write(const char *data)
{
int dataLength = strlen(data);
int sent = send(localSocket, data, dataLength, 0);

if (sent == -1) throw "No se pueden enviar los datos al host";
if (sent != dataLength) throw "No se han podido enviar todos los datos al host";
}

char *TCPSocket::Read()
{
const int MAX_DATA_LENGHT = 16384;
char *data = new char[MAX_DATA_LENGHT];

int received = recv(localSocket, data, MAX_DATA_LENGHT, 0);
if (received == -1) throw "No se pueden recibir los datos del host";

return (data);
}


Main.cpp


#include <iostream>
#include "TCPSocket.h"

using namespace std;

int main()
{
try
{
cout << "HTTP REQUEST" << endl;
cout << "------------" << endl;

// Establecemos la conexion con el servidor
TCPSocket *tcpSocket = new TCPSocket();
tcpSocket->Connect("www.ubuntu.com", 80);

// Generamos la peticion HTTP
string request = "";
request.append("HEAD / HTTP/1.1\r\n");
request.append("Host: www.ubuntu.com\r\n");
request.append("User-agent: My User Agent\r\n");
request.append("\r\n");

for(int i = 0; i < 5; i++)
{
// Realizamos la peticion HTTP
tcpSocket->Write(request.c_str());

// Obtenemos la respuesta del servidor
char *data = tcpSocket->Read();
string response = string(data);

cout << endl << response << endl;
}

delete tcpSocket;

return (0);
}

catch (const char *e)
{
cout << "Excepcion: " << e << endl;
return (-1);
}
}


La salida obtenida es la siguiente:


HTTP REQUEST
------------
Conectando con www.ubuntu.com
Conectando con 91.189.90.58

HTTP/1.1 200 OK
Date: Mon, 23 Jun 2014 10:26:31 GMT
Server: Apache/2.2.22 (Ubuntu)
Cache-Control: public, max-age=300
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Age: 26
Content-Length: 17555
X-Cache: HIT from avocado.canonical.com
X-Cache-Lookup: HIT from avocado.canonical.com:80
Via: 1.1 avocado.canonical.com:80 (squid/2.7.STABLE7)
Via: 1.1 www.ubuntu.com



HTTP/1.1 200 OK
Date: Mon, 23 Jun 2014 10:26:31 GMT
Server: Apache/2.2.22 (Ubuntu)
Cache-Control: public, max-age=300
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Age: 26
Content-Length: 17555
X-Cache: HIT from avocado.canonical.com
X-Cache-Lookup: HIT from avocado.canonical.com:80
Via: 1.1 avocado.canonical.com:80 (squid/2.7.STABLE7)
Via: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com



HTTP/1.1 200 OK
Date: Mon, 23 Jun 2014 10:26:31 GMT
Server: Apache/2.2.22 (Ubuntu)
Cache-Control: public, max-age=300
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Age: 26
Content-Length: 17555
X-Cache: HIT from avocado.canonical.com
X-Cache-Lookup: HIT from avocado.canonical.com:80
Via: 1.1 avocado.canonical.com:80 (squid/2.7.STABLE7)
Via: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com



HTTP/1.1 200 OK
Date: Mon, 23 Jun 2014 10:26:31 GMT
Server: Apache/2.2.22 (Ubuntu)
Cache-Control: public, max-age=300
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Age: 26
Content-Length: 17555
X-Cache: HIT from avocado.canonical.com
X-Cache-Lookup: HIT from avocado.canonical.com:80
Via: 1.1 avocado.canonical.com:80 (squid/2.7.STABLE7)
Via: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com



HTTP/1.1 200 OK
Date: Mon, 23 Jun 2014 10:26:31 GMT
Server: Apache/2.2.22 (Ubuntu)
Cache-Control: public, max-age=300
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Age: 26
Content-Length: 17555
X-Cache: HIT from avocado.canonical.com
X-Cache-Lookup: HIT from avocado.canonical.com:80
Via: 1.1 avocado.canonical.com:80 (squid/2.7.STABLE7)
Via: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com

: 1.1 www.ubuntu.com




Como se ve, todas las respuestas deberían ser idénticas, pero se ven alteradas por las respuestas previas. Creo que el problema se puede encontrar en el método "Read" de la clase aunque no estoy seguro porque no veo ningún error.

Gracias de antemano y un saludo.

eferion

Lo que tú estás haciendo ahora mismo podría equipararse a 5 personas intentando hablar todas a la vez a través de la misma línea.

Para resolver el problema yo te propongo 2 opciones:

* Usar 5 instancias diferentes de TCPSocket. Cada una creará un socket diferente y no se interferirán entre sí.

* Usar solo un TCPSocket, en este caso la clase debería impedir nuevas conexiones hasta no haber cerrado la conexión actual.