This part of code is used by a client when receiving a file:
void do_retr_cmd(int f_sockd){
int fd;
ssize_t nread = 0;
uint32_t fsize, fsize_tmp, total_bytes_read, size_to_receive;
char *filename = NULL, *conferma = NULL, *filebuffer = NULL;
char buf[256], dirp[256], t_buf[256];
memset(dirp, 0, sizeof(dirp));
memset(buf, 0, sizeof(buf));
memset(t_buf, 0, sizeof(t_buf));
printf("Write the name of file to download: ");
fgets(dirp, BUFFGETS, stdin)
filename = NULL;
filename = strtok(dirp, "\n");
sprintf(buf, "RETR %s", dirp);
if(send(f_sockd, buf, strlen(buf), 0) < 0){
perror("Errore durante l'invio del nome del file");
onexit(f_sockd, 0, 0, 1);
}
fsize = 0;
recv(f_sockd, t_buf, sizeof(t_buf), 0)
fsize = atoi(t_buf);
fd = open(filename, O_CREAT | O_WRONLY, 0644);
fsize_tmp = fsize;
filebuffer = (char *)malloc(fsize);
total_bytes_read = 0;
nread = 0;
for(size_to_receive = fsize; size_to_receive > 0;){
nread = read(f_sockd, filebuffer, size_to_receive);
if(nread < 0){
perror("read error on retr");
onexit(f_sockd, 0, 0, 1);
}
if(write(fd, filebuffer, nread) != nread){
perror("write error on retr");
onexit(f_sockd, 0, 0, 1);
}
size_to_receive -= nread;
}
close(fd);
fflush(stdout);
fflush(stdin);
memset(buf, 0, sizeof(buf));
recv(f_sockd, buf, 21, 0)
printf("%s", buf);
memset(buf, 0, sizeof(buf));
memset(t_buf, 0, sizeof(t_buf));
memset(dirp, 0, sizeof(dirp));
free(filebuffer);
}
And this part of code is used by a server when sending a file:
void do_server_retr_cmd(f_sockd, m_sockd){
int fd, rc;
uint32_t fsize, size_to_send;
char *filename = NULL, *other = NULL;
char buf[512], t_buf[256];
off_t offset;
struct stat fileStat;
memset(buf, 0, sizeof(buf));
memset(t_buf, 0, sizeof(t_buf));
recv(f_sockd, buf, sizeof(buf), 0)
other = NULL;
filename = NULL;
other = strtok(buf, " ");
filename = strtok(NULL, "\n");
if(strcmp(other, "RETR") == 0){
printf("Ricevuta richiesta RETR\n");
} else /* do something */
fd = open(filename, O_RDONLY);
memset(&fileStat, 0, sizeof(fileStat));
fileStat.st_size = 0;
fstat(fd, &fileStat)
fsize = fileStat.st_size;
snprintf(t_buf, 255, "%" PRIu32, fsize);
send(f_sockd, t_buf, sizeof(t_buf), 0)
offset = 0;
for (size_to_send = fsize; size_to_send > 0; ){
rc = sendfile(f_sockd, fd, &offset, size_to_send);
if (rc <= 0){
perror("sendfile");
onexit(f_sockd, m_sockd, fd, 3);
}
size_to_send -= rc;
}
close(fd);
fflush(stdout);
fflush(stdin);
memset(buf, 0, sizeof(buf));
strcpy(buf, "226 File transfered\n");
send(f_sockd, buf, strlen(buf), 0)
memset(buf, 0, sizeof(buf));
memset(t_buf, 0, sizeof(t_buf));
}
–> Errors checking have been omitted <–
I have a big problem with these 2 pieces of code. When i start the main program i have to write:
1. retr and then i press enter
2. Write the filename to download: i write the filename and then i press enter
The problem is that sometimes the file is downloaded correctly and sometimes it is not downloaded but a part of it is displayed on stdout (on the terminal).
I don’t understand why i got this strange behavior.
PS: i know my code is ugly but i a C-newbie!
I’m developing on Ubuntu amd64 and using GCC-4.6.3 (C language).
TCP connections gives you a reliable bi-directional stream of bytes, but the boundaries of your “application messages” are not preserved, meaning one
send()can be received in multiplerecv()s on the other side, and the other way around, severalsend()s can be collapsed into onerecv()(and you can receive part of the last chunk you sent). The good thing is you do receive bytes you sent, in the order you sent them.The line
recv(f_sockd, buf, sizeof(buf), 0);in the server code assumes that you read the file name here, while in fact you can get up to256bytes of whatever your client sent.You need to impose some sort of an application-level protocol on top of bare TCP. A very simplistic one would be to send a textual header in front of your file content in the form:
So your server can look up the first newline, split the line on the first space and have the number of bytes to expect, and the file name to save those bytes to. Do not ignore the rest of the receive buffer after that newline, save it to the file. This also gives you a possibility of re-using that connection for multiple file transfers.
Hope this helps.