Avatar billede mollevp Nybegynder
13. august 2005 - 13:25 Der er 23 kommentarer og
1 løsning

Socket spm..

Hej vi har et lille problem med vores app.
Selve koden har jeg desværre ikke lige fået med hjem i weekenden - men jeg ville bare lige høre om der er nogle der har en ide til et fix.

Setup: Vi har en maskine der kører en netværk-routnings-service af pakker. Vi kan så oprette en socket forbindelse ned til routnings-servicen og sende nogle forskellige kommandoer - så vi fx. modtager statestik oplysning om trafik-flow (per klient i vores netværk) osv. de oplysninger bruger vi til at tegne nogle grafer i en GUI.

Problemer: Når vi sender en kommando til servicen, og bagefter læser resultatet med en recv(). Men nogle gange er det som om den simpelthen ikke når at sende alle resultaterne tilbage.. Nogle gange får vi måske kun halvdelen af det forventede - næste gange vi læser får vi så det, vi manglede fra før...

Yderligere oplysninger:
Det er et variabelt antal bytes der skal læses - da mængden af statestik oplysninger afhænger af antal klienter..
Vores GUI kører i samme tråd, så vi vil derfor helst undgå 'blocking' kald.

Ideer:
Kan man lave en slags flush på en socket?
Kan man lave en read der læser til der ikke er mere at læse..
Skal vi have fat i en bestemt type socket?

Håber det giver mening :)

Desværre har jeg ikke lige koden foran mig, så præcise tekniske detaljer må vente til mandag..

MVH Morten
It's not the software that's buggy - it's me :D
Avatar billede arne_v Ekspert
13. august 2005 - 20:59 #1
recv'er I i en while løkke ?

med rå sockets skal man altid læse i en while løkke indtil man har
fået det man skal bruge (et bestemt antal bytes eller indtil \r\n eller
noget lignende)
Avatar billede arne_v Ekspert
13. august 2005 - 21:04 #2
jeg poster lige lidt kode fra lageret til inspiration

først et eksempel med en while løkke (HTTP GET til binær fil):

#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,first,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\nConnection: close\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 */
  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);
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;
}

så et eksempel på det samme men både blocking og non blocking (HTTP GET
til print, 10 sekunder timeout på nonblocking read):

#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 noblock)
{
  int sd,status,len,ix,tmp;
  char cmd[512],resp[51200];
  struct sockaddr local,remote;
  struct hostent *hostinfo;
  time_t t;
  int one = 1;
  /* 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 POST request\n");
      goto fin;
  }
  /* read response */
  ix=0;
  if(noblock) {
      status=ioctlsocket(sd, FIONBIO, &one);
      if(status<0) {
        printf("Error setting socket nonblocked: %s\n",strerror(errno));
        goto fin;
      }
      t=time(NULL);
      while (time(NULL)<(t+10)) {
        len=recv(sd,resp+ix,sizeof(resp)-ix-1,0);
        if(len<0) len=0;
        ix = ix + len;
        printf("no blocking read %d bytes\n",len);
        Sleep(1000);
      }
  } else {
      while ((len=recv(sd,resp+ix,sizeof(resp)-ix-1,0))>0) {
        ix = ix + len;
        printf("blocking read %d bytes\n",len);
      }
  }
  resp[ix]='\0';
  /*printf("%s",resp);*/
fin:
  closesocket(sd);
  return;
}

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

og du har helt ret - det er ca. samme kode !
Avatar billede mollevp Nybegynder
14. august 2005 - 10:40 #3
Det ser godt ud Arne, jeg vil lige sammenholde koden med den vi har fået lavet på mandag.. Det nemmeste ville være hvis vi gjorde så vi altid fik en slags terminering af output fx. \r\n - så vi altid bare skal læse indtil vi får den - tror du ikke?
Avatar billede arne_v Ekspert
14. august 2005 - 10:41 #4
linier er en god velgennemprøvet protokol
Avatar billede mollevp Nybegynder
15. august 2005 - 10:45 #5
Jeg sidder nu lige og prover at faa det til at virke, men har lidt problemer - min kode ser saadan her ud:

  printf("Before loop\n");
  ix = 0;

  while((recv_status = recv(sd, buf+ix, sizeof(buf)-ix-1, 0)) > 4){
    ix = ix + recv_status;
    printf("Bytes read %d\n", recv_status);
  }
  printf("After loop\n");

Den dor ligesaa snart den faar noget at laese - vi starter nemlig forst vores service efter noget tid og i starten faar vi bare -1 tilbage fra recv().. og det er jo fint nok..

Men naar vi saa starter selve servicen - og der kommer noget data, saa gaar den kold..

Before loop
Bytes read 724
Bytes read 607

Saa kommer den ikke videre.. Jeg vil lige naevne at det ikke er os der har kodet routnings-service.. og grunden til jeg korer while-loopet < 4 .. er at vi altid faar 4 bytes - har provet at printf'e og det er vist et 0 den smider tilbage..
Avatar billede mollevp Nybegynder
15. august 2005 - 10:53 #6
Vi bruger folgende til at oprette forbindelse til servicen:

  /*
  * Open socket to Switch Device
  */
 
  sd = socket(PF_INET, SOCK_STREAM,0);
  peer.sin_family = AF_INET;
  peer.sin_addr.s_addr = inet_addr("127.0.0.1");
  peer.sin_port = htons(2000);

  rc = connect(sd, &peer, sizeof(peer));
Avatar billede mollevp Nybegynder
15. august 2005 - 11:12 #7
Vi har ogsaa snakket om at lade laesningen fra servicen kore i sin egen traad (m. pthreads) og saa saette et flag naar der er noget at hente.. Det vil self. involvere at vi skal have sat noget mutex op - tror I det kan betale sig for en enkelt leasning paa max 1000 bytes hver halve sek (hvilket vi godt kan saette lidt op hvis det er).. vi det ikke godt kunne kore uden at vi blokere vores traad for meget..?
Avatar billede arne_v Ekspert
15. august 2005 - 12:10 #8
jeg forstår ikke

I kommer kun ind i while løkken når recv_status > 4

i while løkken laves  ix = ix + recv_status

før while løkken er ix 724

efter while løkken er ix 607

det hænger ikke sammen !
Avatar billede arne_v Ekspert
15. august 2005 - 12:12 #9
strengt taget kan I ikke være sikker på at I får alle 4 bytes i en recv, men
monstro ikke at I gør det

mit andet eksempel viser brug af en non blocking socket

og en tråd er tit en god løsning

pthreads ? jeg troede at det var windows ?
Avatar billede mollevp Nybegynder
15. august 2005 - 12:22 #10
printf("Before loop\n");
  ix = 0;

  while((recv_status = recv(sd, buf+ix, sizeof(buf)-ix-1, 0)) > 4){
    ix = ix + recv_status;
    printf("Bytes read %d\n", recv_status);
  }
  printf("After loop\n");

- Printer: Before loop
- Vi kommer ind i while løkken da recv returnere at den har læst 724 bytes
- det ligger vi til vores ix så vi ikke overskriver det vi har læst
- og printer det: Bytes read 724
- så iterere løkken igen og vi læser 607 bytes
- det ligger vi igen til ix
- og printer igen..

nu dør den på mystisk vi vi kommer aldrig ud af løkken - After loop bliver ikke printet..

>> strengt taget kan I ikke være sikker på at I får alle 4 bytes i en recv, men
>> monstro ikke at I gør det
Det har du ret i.. jeg er ikke nogen socket ekspert - så jeg er lidt i tvivl om den bedste løsning..

>> pthreads ? jeg troede at det var windows ?
Vores setup er et par Ubuntu maskiner..
Avatar billede arne_v Ekspert
15. august 2005 - 12:25 #11
dør ?

du er opmærksom på at recv i blocking mode venter på input og kun
returnerer negativ ved fejl ?
Avatar billede arne_v Ekspert
15. august 2005 - 12:25 #12
ah - linux

så har fundet ud af at undlade WSA* og kalde close fremfor closesocket
Avatar billede mollevp Nybegynder
15. august 2005 - 12:32 #13
>> dør ?
Den fryser simpelhen bare programmet - GUI'en går kold..

>> du er opmærksom på at recv i blocking mode venter på input og kun
>> returnerer negativ ved fejl ?
Det er måske nok det er galt egentligt - jeg regner altid med at jeg får de 4 bytes.. Det kan være
at jeg ikke får dem i det tilfælde hvor programmet går kold.. og da den er i blocking mode derfor stopper programmet.. Også lidt dumt at satse på at de altid kommer, når man ikke selv har kodet det der smider dem ud - jeg prøver lige at ændre den til en non-blocking - kan det gøres med det ioctl kald du bruger i dit eksempel..? - eller er det windows specifikt?

>> så har fundet ud af at undlade WSA* og kalde close fremfor closesocket
Yes, det er på plads..
Avatar billede mollevp Nybegynder
15. august 2005 - 12:35 #14
fcntl(sd, F_SETFL, O_NONBLOCK); ikke?
Avatar billede arne_v Ekspert
15. august 2005 - 12:35 #15
det skulle også virke på Linux
Avatar billede arne_v Ekspert
15. august 2005 - 12:39 #16
det ser rigtigt ud

måske husker jeg forkert og ioctlsocket er win32 only
Avatar billede arne_v Ekspert
15. august 2005 - 12:41 #17
jep

samme program i Linux version:

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

#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>

void get(char *hostname,int port,char *path,int noblock)
{
  int sd,status,len,ix,tmp;
  char cmd[512],resp[51200];
  struct sockaddr local,remote;
  struct hostent *hostinfo;
  time_t t;
  /* 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 POST request\n");
      goto fin;
  }
  /* read response */
  ix=0;
  if(noblock) {
      status=fcntl(sd,F_SETFL,O_NONBLOCK);
      if(status<0) {
        printf("Error setting socket nonblocked: %s\n",strerror(errno));
        goto fin;
      }
      t=time(NULL);
      while (time(NULL)<(t+5)) {
        len=recv(sd,resp+ix,sizeof(resp)-ix-1,0);
        ix = ix + len;
        printf("no blocking read %d bytes\n",len);
        sleep(1);
      }
  } else {
      while ((len=recv(sd,resp+ix,sizeof(resp)-ix-1,0))>0) {
        ix = ix + len;
        printf("blocking read %d bytes\n",len);
      }
  }
  resp[ix]='\0';
  /*printf("%s",resp);*/
fin:
  close(sd);
  return;
}

int main(int argc,char *argv[])
{
  get("www.xxxx.dk",80,"/",0);
  get("www.xxxx.dk",80,"/",1);
}
Avatar billede mollevp Nybegynder
15. august 2005 - 12:45 #18
Yes, nu fryser den ikke mere.. laekkert! Jeg takker for super hjaelp - som altid! Saa maa vi se om jeg kan faa resten til at virke ;) Lig et svar.
Avatar billede arne_v Ekspert
15. august 2005 - 12:54 #19
ok
Avatar billede mollevp Nybegynder
15. august 2005 - 12:57 #20
Arne jeg har lige et spm. til dette mere.. naar den returnere -1 er der fejl, hvordan laeser man hvilken fejl der er tale om.. jeg kan se i man siderne at der er en del forskellige muligheder, men hvordan ser man dem..?
Avatar billede arne_v Ekspert
15. august 2005 - 13:00 #21
prøv og check den globale variabel errno
Avatar billede mollevp Nybegynder
15. august 2005 - 13:00 #22
skal man include <errno.h> og saa printe fejlen med en perror("socket")?
Avatar billede arne_v Ekspert
15. august 2005 - 13:01 #23
Avatar billede mollevp Nybegynder
15. august 2005 - 13:02 #24
ok, kan se du bruger: strerror(errno) det prover jeg..
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