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
Código:
#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
Código:
#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
Código:
#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.