Avatar billede milandt Nybegynder
11. august 2004 - 14:15 Der er 21 kommentarer og
1 løsning

Download en fil fra en webserver vha c++

Hej

som titlen antyder har jeg brug for at kode noget i c++ der kan downloade og gemme en fil fra en webserver. Hvordan griber jeg det an? Ville gerne hvis det kunne lade sig gøre at skrive en forholdsvis simpel funktion der så kunne kaldes med addressen som parameter.

Mvh
Dennis
Avatar billede arne_v Ekspert
11. august 2004 - 14:18 #1
Sagtens du laver en socket connection, sender din HTTP request og læser resultatet.

Her er et super simpelt eksempel som requester en kendt side og udskriver den til
skærmen:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <windows.h>
#include <errno.h>

void get(char *hostname,int port,char *path)
{
  int sd,status,len,ix,tmp;
  char cmd[512],resp[51200];
  struct sockaddr local,remote;
  struct hostent *hostinfo;
  /* create socket */
  sd=socket(AF_INET,SOCK_STREAM,0);
  if(sd<0) {
      printf("Error creating socket: %s\n",strerror(errno));
      goto fin;
  }
  /* bind socket */
  local.sa_family=AF_INET;
  memset(local.sa_data,0,sizeof(local.sa_data));
  status=bind(sd,&local,sizeof(local));
  if(status<0) {
      printf("Error binding socket: %s\n",strerror(errno));
      goto fin;
  }
  /* lookup host */
  hostinfo=gethostbyname(hostname);
  if(!hostinfo) {
      printf("Error looking up host: %s\n",hostname);
      goto fin;
  }
  /* connect to host */
  remote.sa_family=hostinfo->h_addrtype;
  memcpy(remote.sa_data+2,hostinfo->h_addr_list[0],hostinfo->h_length);
  *((short *)remote.sa_data)=port;
  tmp=remote.sa_data[0];
  remote.sa_data[0]=remote.sa_data[1];
  remote.sa_data[1]=tmp;
  status=connect(sd,&remote,sizeof(remote));
  if(status!=0) {
      printf("Error connecting to host: %s port: %d\n",hostname,port);
      goto fin;
  }
  /* send GET request */
  sprintf(cmd,"GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",path,hostname);
  status=send(sd,cmd,strlen(cmd),0);
  if(status<0) {
      printf("Error sending GET request\n");
      goto fin;
  }
  /* read response */
  ix=0;
  while ((len=recv(sd,resp+ix,sizeof(resp)-ix-1,0))>0) {
      ix = ix + len;
  }
  resp[ix]='\0';
  printf("%s",resp);
fin:
  closesocket(sd);
  return;
}

int main(int argc,char *argv[])
{
  WSADATA WSAData;
  WSAStartup(0x0101,&WSAData);
  get("www.eksperten.dk",80,"/");
  WSACleanup();
  return 0;
}

Du kan nemt rette det til så den gemmer til en fil i.s.f. at udskrive til skærmen.
Avatar billede arne_v Ekspert
11. august 2004 - 14:27 #2
Ah. Du skal selvfølige skrælle HTTP headerne af i svaret.

Her er en tilrettet download version:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <windows.h>
#include <errno.h>

void get(char *hostname,int port,char *path, char *fnm)
{
  FILE *fp;
  int sd,status,len,ix,tmp;
  char cmd[512],resp[51200],*body;
  struct sockaddr local,remote;
  struct hostent *hostinfo;
  /* create socket */
  sd=socket(AF_INET,SOCK_STREAM,0);
  if(sd<0) {
      printf("Error creating socket: %s\n",strerror(errno));
      goto fin;
  }
  /* bind socket */
  local.sa_family=AF_INET;
  memset(local.sa_data,0,sizeof(local.sa_data));
  status=bind(sd,&local,sizeof(local));
  if(status<0) {
      printf("Error binding socket: %s\n",strerror(errno));
      goto fin;
  }
  /* lookup host */
  hostinfo=gethostbyname(hostname);
  if(!hostinfo) {
      printf("Error looking up host: %s\n",hostname);
      goto fin;
  }
  /* connect to host */
  remote.sa_family=hostinfo->h_addrtype;
  memcpy(remote.sa_data+2,hostinfo->h_addr_list[0],hostinfo->h_length);
  *((short *)remote.sa_data)=port;
  tmp=remote.sa_data[0];
  remote.sa_data[0]=remote.sa_data[1];
  remote.sa_data[1]=tmp;
  status=connect(sd,&remote,sizeof(remote));
  if(status!=0) {
      printf("Error connecting to host: %s port: %d\n",hostname,port);
      goto fin;
  }
  /* send GET request */
  sprintf(cmd,"GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",path,hostname);
  status=send(sd,cmd,strlen(cmd),0);
  if(status<0) {
      printf("Error sending GET request\n");
      goto fin;
  }
  /* read response */
  ix=0;
  while ((len=recv(sd,resp+ix,sizeof(resp)-ix-1,0))>0) {
      ix = ix + len;
  }
  body = strstr(resp,"\r\n\r\n") + 4;
  fp = fopen(fnm,"wb");
  fwrite(body,ix-(body-resp),1,fp);
  fclose(fp);
  resp[ix]='\0';
fin:
  closesocket(sd);
  return;
}

int main(int argc,char *argv[])
{
  WSADATA WSAData;
  WSAStartup(0x0101,&WSAData);
  get("www.eksperten.dk",80,"/img/elogo.png","elogo.png");
  WSACleanup();
  return 0;
}
Avatar billede milandt Nybegynder
11. august 2004 - 14:44 #3
kan jeg bruge det i en projekt fra Borland C++ Builder 6.0 ?
Avatar billede arne_v Ekspert
11. august 2004 - 15:24 #4
Ja da.

Du skal bare copy paste get funktionen ind i din kode.
Avatar billede milandt Nybegynder
11. august 2004 - 19:58 #5
Får bare en del compilerfejl, og tænkte at jeg måske sætter det ind det forkerte sted. Jeg har prøvet at compile med en command line compiler, og det virker som det skal..

Første fejl i builderen er her:

struct sockaddr local,remote;

lige efter local
Avatar billede arne_v Ekspert
11. august 2004 - 20:00 #6
Har du inkluderet de rigtige header filer ?

Har du andre ting som hedder local og remote ?

(prøv evt. at omdøbe til mylocal og myremote)
Avatar billede milandt Nybegynder
11. august 2004 - 20:28 #7
Hvis disse linier er i starten af min kode,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <windows.h>
#include <errno.h>
så har jeg vel inkluderet de rigtige filer.. nogen libs jeg skal tage med?
prøver mig frem igen i morgen :)
Avatar billede arne_v Ekspert
11. august 2004 - 20:34 #8
Header filerne ser rigtige nok ud.

Hvad er det for nogen fejl du får ?
Avatar billede milandt Nybegynder
12. august 2004 - 09:23 #9
Jeg har fået det til at virke.. går ud fra at jeg måske har inkluderet headerfilerne et forkert sted. Bortset fra det er det meget overskuelig kode i dit eksempel. Jeg har bare ikke mega meget erfaring med c++ Builderen :)

Tak
Avatar billede milandt Nybegynder
12. august 2004 - 09:32 #10
lige et opfølgende spørgsmål: 

hvad gør følgende kode?

  WSADATA WSAData;
  WSAStartup(0x0101,&WSAData);

og 

  WSACleanup();

Mvh
Dennis
Avatar billede arne_v Ekspert
12. august 2004 - 09:44 #11
Det er noget winsock specifikt. De to første skal være der inden man starter
på at bruge TCP/IP og den sidste skal være der efter at man er færdig med
at bruge TCP/IP.
Avatar billede milandt Nybegynder
12. august 2004 - 11:07 #13
Er ked af at jeg bliver ved at vade rundt i det her spgs, men jeg har problemer med at hente filer fra nogen servere, og ikke fra andre.. fx kan jeg godt hente ekspertens logo, men der er andre steder hvor det ikke virker. Den crasher ved denne linie:

  while ((len=recv(sd,resp+ix,sizeof(resp)-ix-1,0))>0) {
      ix = ix + len;
  }

En fil den fx ikke kan hente rigtigt: www.wogaming.com/test.gif

Kan der være noget galt i den while løkke?
Avatar billede arne_v Ekspert
12. august 2004 - 11:12 #14
Mystisk jeg kan godt hente lige netop den.

Der er en buffer size på 51200 til både billede og HTTP headere.

Hvis det er store billeder skal den sættes op.

(ved meget store billeder skal logikken nok ændres så der løbende skrives
til disk)
Avatar billede milandt Nybegynder
12. august 2004 - 11:15 #15
Har prøvet at ligge elogo.png over på en anden server og hente den derfra hvor den fejlede.. ligner altså ikke så meget at det har noget med filen at gøre, men kan ikke gennemskue det, for det virker lidt tilfældigt hvar der virker og hvad der ikke gør.
Avatar billede arne_v Ekspert
12. august 2004 - 11:26 #16
Svært at sige hvad der er galt.

Jeg tror at du bliver nødt til at debugge lidt og finde ud af hvad resp og ix
indeholer når det går galt.
Avatar billede milandt Nybegynder
12. august 2004 - 11:41 #17
prøv med denne her: http://www.robinhus.dk/upload/boligfoto/store/65845.jpeg

hvis du også kan hente den uden problemer, så må jeg jo bare igang med at debugge fra en ende af :)
Avatar billede arne_v Ekspert
12. august 2004 - 11:54 #18
Ah. Den hang også hos mig.

Så rettede jeg sprintf til:

  sprintf(cmd,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",path,hostname);

og så virkede det igen.

Prøv lige det !
Avatar billede milandt Nybegynder
12. august 2004 - 12:14 #19
Tak! Havde ikke selv kunne regne ud at når den hænger i en while løkke så var det faktisk GET requestet der var en fejl i..

:o)

Case closed.. (for nu)
Avatar billede milandt Nybegynder
13. august 2004 - 11:03 #20
case open..

/* read response */
  ix=0;
  while ((len=recv(sd,resp+ix,sizeof(resp)-ix-1,0))>0) {
      ix = ix + len;
  }
  body = strstr(resp,"\r\n\r\n") + 4;
  fp = fopen(fnm,"wb");
  fwrite(body,ix-(body-resp),1,fp);
  fclose(fp);
  resp[ix]='\0';


^^ hvordan får jeg det til at skrive til filen i flere omgange så jeg ikke får en stack overflow hvis jeg prøver at sætte bufferen op.. jeg vil gerne kunne hente større filer :o)

Dennis
Avatar billede arne_v Ekspert
13. august 2004 - 11:26 #21
Utestet:

  int first;
...
  fp = fopen(fnm,"wb");
  first = 1;
  while ((len=recv(sd,resp,sizeof(resp),0))>0) {
      if(first) {
          body = strstr(resp,"\r\n\r\n") + 4;
          first = 0;
      } else {
          body = resp;
      }
      fwrite(body,len-(body-resp),1,fp);
  }
  fclose(fp);
Avatar billede milandt Nybegynder
13. august 2004 - 12:00 #22
hermed testet.. :o) og det virker.

tak. sad og rodede rundt i pointere og buffere og kunne ikke få den til at skrive det rigtigt.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester