Programmazione socket: recv () non riceve i dati correttamente

Ho visto i thread simili ma non riesco a trovare nulla che possa risolvere il mio problema.

Sto programmando un server che potrebbe inviare un file immagine (jpg) dal percorso inviato dal client. Sto usando le funzioni send / recv in C per quello.

Leggo i file di una porzione di dati alla volta e li mando al client che riceve i contenuti e li scrive in qualche punto per creare il file.

Il problema è ‘recv’ non riceve il numero di byte inviati dal ‘send’.

Come parte del debug, ho provato diverse dimensioni del buffer e la dimensione del buffer ‘128’ non mi dà alcun problema, e il file è trasferito e costruito con successo.

Tuttavia, per i buffer di bit “32” e “64”, “recv” riceve i dati “32” bit o “64” bit nell’ultimo blocco, anche se i dati inviati dal server sono inferiori a “32” bit o ” 64 ‘bit. E, per ‘256’, ‘512’, ‘1024’ così via, ‘recv’ restituisce SOLO ‘128’ bit su ESATTAMENTE una delle risposte, anche se il server invia chunk completo, cioè ‘256’ o ‘512 ‘, a seconda della dimensione del buffer.

Apprezzerò qualsiasi consiglio per il debug. Il codice seguente è solo per le parti pertinenti, ma posso fornire di più se qualcuno lo richiede.

// Codice client

#define BUFFER_SIZE 4096 #define HEADER_LEN 512 const char * const scheme = "GETFILE"; const char* const method = "GET"; const char * const end_marker = "\\r\\n\\r\\n"; struct gfcrequest_t { int filelen; char cport[12]; char servIP[50]; gfstatus_t ret_status; char spath[1024]; int tot_bytes; char filecontent[BUFFER_SIZE]; void (*fl_handler)(void *, size_t, void *); void * fDesc; void (*head_handler)(void *, size_t, void *); void * headarg; }; static pthread_mutex_t counter_mutex; gfcrequest_t *gfc; // get sockaddr, IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } return &(((struct sockaddr_in6*)sa)->sin6_addr); } static char *stringFromError(gfstatus_t stat) { static const char *strings[] = {"GF_OK", "GF_FILE_NOT_FOUND", "GF_ERROR", "GF_INVALID"}; return strings[stat]; } int getFileRequestHeader(char * req_header) { return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,gfc->spath, end_marker); // return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,"/courses/ud923/filecorpus/yellowstone.jpg", end_marker); } gfcrequest_t *gfc_create() { gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t)); return gfc; } void gfc_set_server(gfcrequest_t *gfr, char* server) { strcpy(gfr->servIP, server); } void gfc_set_path(gfcrequest_t *gfr, char* path) { strcpy(gfr->spath, path); } void gfc_set_port(gfcrequest_t *gfr, unsigned short port) { snprintf(gfr->cport,12, "%u",port); } void gfc_set_headerfunc(gfcrequest_t *gfr, void (*headerfunc)(void*, size_t, void *)) { gfr->head_handler = headerfunc; } void gfc_set_headerarg(gfcrequest_t *gfr, void *headerarg) { /*have to change this...*/ gfr->headarg = headerarg; } int isEndMarker(char *iheader, int start) { char *marker = "\\r\\n\\r\\n"; int i = 0; int ind=0; while (ind ret_status = GF_OK; } else if (istatus == 400) { gfr->ret_status = GF_FILE_NOT_FOUND; } else if (istatus == 500) { gfr->ret_status = GF_ERROR; } if (!strcmp(scheme, "GETFILE") && (istatus == 200 || istatus == 400 || istatus == 500)) { int index = 10; while(1) { if (resp_header[index] == '\\') { end = isEndMarker(resp_header, index); } if (end) break; filelen[fileindex++] = resp_header[index++]; } filelen[fileindex] = '\0'; } int head_len = strlen(scheme) + strlen(status) + strlen(filelen) + 8; return atoi(filelen); } void gfc_set_writefunc(gfcrequest_t *gfr, void (*writefunc)(void*, size_t, void *)) { gfr->fl_handler = writefunc; } void gfc_set_writearg(gfcrequest_t *gfr, void *writearg) { gfr->fDesc = writearg; } int gfc_perform(gfcrequest_t *gfr){ struct addrinfo hints, *servinfo, *p; int sockfd, rv, totalBytesRcvd; char req_header[HEADER_LEN]; int bytesRcvd; int header_len; char s[INET6_ADDRSTRLEN]; memset(&hints, 0, sizeof(hints)); memset(req_header,0,sizeof(req_header)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; //use my IP if ((rv = getaddrinfo(NULL, gfr->cport, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } // loop through all the results and connect to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("client: socket"); continue; } if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("client: connect"); continue; } break; } if (p == NULL) { fprintf(stderr, "client: failed to connect\n"); return 2; } //printf("connected...\n"); inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s)); //Ahsan // printf("Before getFileRequestHeader...\n"); header_len = getFileRequestHeader(req_header); //printf("Header Description:%s, Header Len: %u\n", req_header, header_len); if (send(sockfd, req_header, header_len, 0) != header_len) perror("send() sent a different number of bytes than expected"); if ((bytesRcvd = recv(sockfd, gfr->filecontent, BUFFER_SIZE, 0)) filecontent); gfr->filelen = getFileLen(gfr->filecontent, gfr); //printf("File Length: %d\n", gfr->filelen); /* Receive the same string back from the server */ int req_no=1; gfr->tot_bytes = 0; while ( 1 ) { printf("Request: %d ", req_no++); ssize_t nb = recv( sockfd, gfr->filecontent, BUFFER_SIZE, 0 ); if ( nb == -1 ) err( "recv failed" ); if ( nb == 0 ) {printf("zero bytes received...breaking");break;} /* got end-of-stream */ gfr->fl_handler(gfr->filecontent, nb, gfr->fDesc); gfr->tot_bytes += nb; printf("Received Bytes: %zd Total Received Bytes: %d\n", nb, gfr->tot_bytes); } return 0; } /* * Returns the string associated with the input status */ char* gfc_strstatus(gfstatus_t status) { return stringFromError(status); } gfstatus_t gfc_get_status(gfcrequest_t *gfr){ return gfr->ret_status; } size_t gfc_get_filelen(gfcrequest_t *gfr) { return gfr->filelen; } size_t gfc_get_bytesreceived(gfcrequest_t *gfr) { return gfr->tot_bytes; } void gfc_cleanup(gfcrequest_t *gfr) { free(gfr); gfr=NULL; } void gfc_global_init() { ; // pthread_mutex_lock(&counter_mutex); // // gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t)); // // pthread_mutex_unlock(&counter_mutex); } void gfc_global_cleanup() { ; // pthread_mutex_lock(&counter_mutex); // // free(gfc); // // pthread_mutex_unlock(&counter_mutex); } 

// Codice server

 struct gfcontext_t { int sockfd; int clntSock; }; struct gfserver_t { char port[12]; unsigned short max_npending; char fpath[256]; ssize_t (*fp_handler)(gfcontext_t *ctx, char *, void*); int *handler_arg; }; /*Variable decalation*/ static gfserver_t *gfserv; static gfcontext_t *gfcontext; int isEndMarker(char *iheader, int start) { char *marker = "\\r\\n\\r\\n"; int i = 0; int ind=0; while (ind fpath[pathindex++] = iheader[i++]; } gfs->fpath[pathindex] = '\0'; } } printf("Scheme: %s Method:%s Path:%s\n", scheme, method, gfs->fpath); return 0; } void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } return &(((struct sockaddr_in6*)sa)->sin6_addr); } ssize_t gfs_sendheader(gfcontext_t *ctx, gfstatus_t stat, size_t file_len) { char resp_header[MAX_REQUEST_LEN]; char end_marker[12] = "\\r\\n\\r\\n"; sprintf(resp_header, "GETFILE%d%zd%s",stat, file_len, end_marker); printf("Response: %s\n", resp_header); if (send(ctx->clntSock, resp_header, MAX_REQUEST_LEN, 0) != MAX_REQUEST_LEN) perror("send() failed"); return 0; } ssize_t gfs_send(gfcontext_t *ctx, void *data, size_t len) { size_t total = 0; size_t bytesLeft = len; size_t n; int debug_req=1; while (total clntSock, data+total, bytesLeft, 0); if (n == -1) { printf("Nothing to send...\n"); break; } fprintf(stderr, "Tries: %d Bytes Sent: %zu\n", debug_req++, n); total += n; bytesLeft -= n; } // if ( shutdown( ctx->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" ); return total; } void gfs_abort(gfcontext_t *ctx){ close(ctx->clntSock); close(ctx->sockfd); free(ctx); free(gfserv); perror("aborting..."); exit(1); } gfserver_t* gfserver_create() { gfserv = (gfserver_t*) malloc(1 * sizeof(gfserver_t)); gfcontext = (gfcontext_t*) malloc(1*sizeof(gfcontext_t)); return gfserv; } void gfserver_set_port(gfserver_t *gfs, unsigned short port) { //set port number in gfs structure snprintf(gfs->port,12, "%u",port); } void gfserver_set_maxpending(gfserver_t *gfs, int max_npending) { //set maxpending connections gfs->max_npending = max_npending; } void gfserver_set_handler(gfserver_t *gfs, ssize_t (*handler)(gfcontext_t *, char *, void*)) { gfs->fp_handler = handler; } void gfserver_set_handlerarg(gfserver_t *gfs, void* arg) { gfs->handler_arg = (int *)arg; } void gfserver_serve(gfserver_t *gfs) { struct addrinfo hints, *servinfo, *p; struct sockaddr_storage clntAddr; //connectors address information socklen_t clntSize; int yes = 1; char s[INET6_ADDRSTRLEN]; int rv; int rcvMsg = 0; char recvBuff[MAX_REQUEST_LEN]; char *req_path; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; //using stream socket instead of datagrams hints.ai_flags = AI_PASSIVE; //use my IP if ((rv = getaddrinfo(NULL, gfs->port, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; } // loop through all the results and bind to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((gfcontext->sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("server: socket"); continue; } //get rid of 'address already in use' error. if (setsockopt(gfcontext->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } if (bind(gfcontext->sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(gfcontext->sockfd); perror("server: bind"); continue; } break; } if (p == NULL) { fprintf(stderr, "server: failed to bind.\n"); return 2; } freeaddrinfo(servinfo); // no need of servinfo structure anymore if (listen(gfcontext->sockfd, gfs->max_npending) == -1) { perror("listen"); exit(1); } //printf("server: waiting for connetions...\n"); while(1) { clntSize = sizeof(clntAddr); gfcontext->clntSock = accept(gfcontext->sockfd, (struct sockaddr *)&clntAddr, &clntSize); if (gfcontext->clntSock == -1) { perror("accept"); continue; } inet_ntop(clntAddr.ss_family, get_in_addr((struct sockaddr *)&clntAddr), s, sizeof(s)); //printf("server: got connection from %s\n", s); if (!fork()) { // this is the child process if ((rcvMsg = recv(gfcontext->clntSock, recvBuff, MAX_REQUEST_LEN, 0)) fpath); if (gfs->fp_handler(gfcontext, gfs->fpath, NULL) clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" ); //close(gfcontext->clntSock); } } } //Server gf_send is being called from following function handler function: Handler: ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){ int fildes; size_t file_len, bytes_transferred; ssize_t read_len, write_len; char buffer[BUFFER_SIZE]; printf("Path: %s\n", path); if( 0 > (fildes = content_get(path))) return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0); /* Calculating the file size */ file_len = lseek(fildes, 0, SEEK_END); gfs_sendheader(ctx, GF_OK, file_len); /* Sending the file contents chunk by chunk. */ int req=1; bytes_transferred = 0; while(bytes_transferred < file_len){ read_len = pread(fildes, buffer, BUFFER_SIZE, bytes_transferred); if (read_len <= 0){ fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len ); gfs_abort(ctx); return -1; } printf("Request No: %d ", req++); write_len = gfs_send(ctx, buffer, read_len); if (write_len != read_len){ fprintf(stderr, "handle_with_file write error"); gfs_abort(ctx); return -1; } bytes_transferred += write_len; } printf("Total Bytes sent to client: %zu\n", bytes_transferred); return bytes_transferred; } 

Non hai specificato, quindi sto assumendo che stai usando TCP qui (inviare / ricevere semantica è diverso con UDP),

Si soffre di un malinteso molto comune che un invio su un’estremità di un socket TCP corrisponda a una ricezione di un numero di byte inviato all’altro capo. Questo è sbagliato.

In realtà, il socket TCP è un stream bidirezionale di byte senza nozioni di messaggi. Una scrittura può corrispondere a molte letture sull’altra estremità e viceversa. Tratta come un stream.

È necessario conservare il numero di byte inviati e ricevuti come restituiti dall’invio e dalla ricezione di chiamate di sistema.

È anche importante far sapere all’altra parte quanti dati stai inviando, in modo che sappia quando, per esempio, l’immagine è completamente trasferita. Questo è il lavoro di un protocollo a livello di applicazione che si deve presentare o utilizzare uno esistente.

Modifica 0:

Ecco cosa sembra necessario anche prima di impostare qualsiasi protocollo significativo tra client e server. Prima il codice di invio:

 size_t total = 0; while ( total != len ) { ssize_t nb = send( s, data + total, len - total, 0 ); if ( nb == -1 ) err( "send failed" ); total += nb; } if ( shutdown( s, SHUT_WR ) == -1 ) err( "socket shutdown failed" ); /* also need to close client socket, see below */ 

Quindi il codice ricevente:

 char buffer[BUFFER_SIZE]; /* somewhere, might be static */ size_t total = 0; /* everything received */ while ( 1 ) { ssize_t nb = recv( s, buffer, BUFFER_SIZE, 0 ); if ( nb == -1 ) err( "recv failed" ); if ( nb == 0 ) break; /* got end-of-stream */ if ( write( file_fd, buffer, nb ) == -1 ) err( "file write failed" ); total += nb; } /* send an ack here */ if ( close( s ) == -1 ) err( "socket close failed" ); if ( close( file_fd )) err( "file close failed" ); printf( "received and saved total of %zu bytes\n", total ); 

Quindi il tuo protocollo a livello di applicazione potrebbe essere semplice come il server che invia, ad esempio, la lunghezza del file a 64 bit immediatamente dopo aver accettato una nuova connessione client (devi decidere quale endianness usare per quello), quindi dopo aver inviato quel numero di byte al client e spegnendo la scrittura sul socket, in attesa che il client confermi il corretto ricevimento dei dati. Potrebbe essere lo stesso numero indietro, o solo un singolo byte – fino a te, quindi chiudere definitivamente il socket. In questo modo il client sa in anticipo quanti byte aspettarsi e il server sa che il trasferimento ha avuto successo.

Dopo aver ottenuto il funzionamento di questa semplice versione, è ansible estenderla per consentire più trasferimenti di file per connessione e / o immergersi nel multiplexing IO con select(2) / poll(2) .

Spero che questo ti aiuti.

Prima di tutto: recv () non riceve sempre i dati nei blocchi che è stato inviato da send (), infatti – raramente lo fa – a causa del buffering (ad esempio, si inviano 256-byte ricevono due buffer di 128 byte ciascuno)

Ora per il tuo errore: penso che il problema è che non stai chiamando select () con un FD_SET per ripristinare il tuo socket in uno stato “pronto per ricevere” prima di chiamare recv () una seconda volta.

Ho un codice metrico di codice winsock / c-socket sul mio sito se si vuole scavare attraverso di esso.

Fammi sapere se posso ampliare questa risposta, sarei felice di fornire ulteriore assistenza!

 gfr->fl_handler(gfr->filecontent, BUFFER_SIZE, gfr->fDesc); 

Problema usuale Stai assumendo che la lettura abbia riempito il buffer. Dovrebbe essere:

 gfr->fl_handler(gfr->filecontent, bytesRcvd, gfr->fDesc);