I'm writing a toy client/server app and sending data over a UDP stream. Everything is working well when I send small messages like "test" or something like that. But when I go to transfer files, I seem to be transferring too much data.
The file that I am transferring is a little less than 4k in size (at least according to an ls command). But when my sendto operation completes, my confirmation message shows that I have sent exactly 5000 bytes (which is the max size of my buffer).
I'd rather not break the sendto up if I don't need to since UDP can potentially transfer messages out of order. Anyone see what I am doing wrong? I think that is has to do with my use of sizeof(buffer), but I don't know how to transfer exactly the number of bytes that I want. Thank you!
Here is the relevant part of the code. Please let me know if you need more:
char buffer[5000];
char path[] = "myfile";
FILE* fp;
if ((fp = fopen(path, "rb")) == NULL) {
printf(stdout, "Error opening the file: %s\n", strerror(errno));
exit(1);
}
memset(&buffer[0], 0, sizeof(buffer));
do {
fread(buffer, 1, MAXBUFSIZE, fp);
} while (!feof(fp));
if ( (numbytes = sendto(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&server_addr, sizeof(server_addr))) < 0)
{
perror("send error");
}
fprintf(stderr, "client sent %d bytes to server\n", numbytes);
As per the sendto command, UDP will send the same amount of data as specified in the send length; the third param is the send length: http://linux.die.net/man/2/sendto . The example is specifying sizeof(buffer) as the send length and sizeof(buffer) is 5000. Accordingly, UDP sends 5000 bytes from the buffer (which is actually the entire buffer). If we want to send the exact number of bytes as present in the file, then we should read the number of bytes in the file, and pass that as the send length.
numbytes = sendto(sockfd, buffer, sizeof(buffer), 0,
Related
I'm making a TCP socket programming.
client:
a client will receive a list of files from a server --> send a file name to the server --> receive the file from the server
My problems:
the server sends the list of files correctly. I printed out all and all of them are sent well. however, the client receives it well but while the loop isn't finished even if the server sending while loop is finished.
[client]
while ((read_cnt=read(sd, buf, BUF_SIZE)) != 0) {
read_cnt=read(sd, buf, BUF_SIZE);
printf("%s\n", buf);
}
[server]
while ((entry=readdir(dir)) != NULL) {
sprintf(buf, "%s\n", entry->d_name);
write(clnt_sd, buf, strlen(buf));
}
the server sends 17 messages but the client receives 15 messages and while loop is not finished. when while loop receives "finish", make client finishes the while loop --> still it receives 15 messages even if the server sent 17 messages.
[client]
while (1) {
read_cnt=read(sd, buf, BUF_SIZE);
fwrite((void*)buf, 1, read_cnt, fp);
printf("%s\n", buf);
if (strstr(buf, "fin") != NULL) {
break;
}
total_read += read_cnt;
pcnt = ((total_read/ file_size) * 100.0)/10;
printf("(%.0d %%) Send %.0f / %.0f bytes.\n", pcnt*10, total_read, file_size);
}
[server]
FILE *fp;
fp = fopen(file_name, "r");
if (fp == NULL) {
printf("File not Exist");
exit(1);
}
fseek(fp, 0, SEEK_END);
int file_size = ftell(fp);
sprintf(buf, "%d", file_size);
write(clnt_sd, buf, read_cnt);
fseek(fp, 0, SEEK_SET);
while (feof(fp) == 0) {
read_cnt = fread((void*)buf, 1, BUF_SIZE, fp);
write(clnt_sd, buf, read_cnt);
}
fclose(fp);
I tried memset, bzero to make buffer empty but it didn't work.
I think the problem is the client part because when I checked server side by using print, they are sending well and finished the while loops. But I don't know what's the problem in receiving process on the client side. Please, Let me know what's the problem is.
the server sends 17 messages but the client receives 15 messages
Completely normal.
TCP is a stream protocol, not a message passing protocol. What you "send" on one side may not be the exact number of bytes received at the other end. You have to expect messages to arrive chunked, coalesced, or segmented between and within read/recv calls. You can't even assume that the initial "size" write you send as a 4-byte message is received within the same read call either as an entire message.
Implement your client side code to expect read and recv to return a random number of bytes up to the size of the buffer you pass in. If you get less bytes than expected, you need to make another read/recv call to receive the rest - adjusting your destination buffer pointer as needed. Or use the MSG_WAITALL flag with recv (instead of read) to get all the data in one call. You still need to check the result of the buffer to make sure you got the expected number of bytes, that the stream didn't close, and to validate that an error (-1 return) didn't occur.
This is for a Linux system, in C. It involves network programming. It is for a file transfer program.
I've been having this problem where this piece of code works unpredictably. It either is completely successful, or the while loop in the client never ends. I discovered that this is because the fileLength variable would sometimes be a huge (negative or positive) value, which I thought was attributed to making some mistake with ntohl. When I put in a print statement, it seemed to work perfectly, without error.
Here is the client code:
//...here includes relevant header files
int main (int argc, char *argv[]) {
//socket file descriptor
int sockfd;
if (argc != 2) {
fprintf (stderr, "usage: client hostname\n");
exit(1);
}
//...creates socket file descriptor, connects to server
//create buffer for filename
char name[256];
//recieve filename into name buffer, bytes recieved stored in numbytes
if((numbytes = recv (sockfd, name, 255 * sizeof (char), 0)) == -1) {
perror ("recv");
exit(1);
}
//Null terminator after the filename
name[numbytes] = '\0';
//length of the file to recieve from server
long fl;
memset(&fl, 0, sizeof fl);
//recieve filelength from server
if((numbytes = recv (sockfd, &fl, sizeof(long), 0)) == -1) {
perror ("recv");
exit(1);
}
//convert filelength to host format
long fileLength = ntohl(fl);
//check to make sure file does not exist, so that the application will not overwrite exisitng files
if (fopen (name, "r") != NULL) {
fprintf (stderr, "file already present in client directory\n");
exit(1);
}
//open file called name in write mode
FILE *filefd = fopen (name, "wb");
//variable stating amount of data recieved
long bytesTransferred = 0;
//Until the file is recieved, keep recieving
while (bytesTransferred < fileLength) {
printf("transferred: %d\ntotal: %d\n", bytesTransferred, fileLength);
//set counter at beginning of unwritten segment
fseek(filefd, bytesTransferred, SEEK_SET);
//buffer of 256 bytes; 1 byte for byte-length of segment, 255 bytes of data
char buf[256];
//recieve segment from server
if ((numbytes = recv (sockfd, buf, sizeof buf, 0)) == -1) {
perror ("recv");
exit(1);
}
//first byte of buffer, stating number of bytes of data in recieved segment
//converting from char to short requires adding 128, since the char ranges from -128 to 127
short bufLength = buf[0] + 128;
//write buffer into file, starting after the first byte of the buffer
fwrite (buf + 1, 1, bufLength * sizeof (char), filefd);
//add number of bytes of data recieved to bytesTransferred
bytesTransferred += bufLength;
}
fclose (filefd);
close (sockfd);
return 0;
}
This is the server code:
//...here includes relevant header files
int main (int argc, char *argv[]) {
if (argc != 2) {
fprintf (stderr, "usage: server filename\n");
exit(1);
}
//socket file descriptor, file descriptor for specific client connections
int sockfd, new_fd;
//...get socket file descriptor for sockfd, bind sockfd to predetermined port, listen for incoming connections
//...reaps zombie processes
printf("awaiting connections...\n");
while(1) {
//...accepts any incoming connections, gets file descriptor and assigns to new_fd
if (!fork()) {
//close socket file discriptor, only need file descriptor for specific client connection
close (sockfd);
//open a file for reading
FILE *filefd = fopen (argv[1], "rb");
//send filename to client
if (send (new_fd, argv[1], strlen (argv[1]) * sizeof(char), 0) == -1)
{ perror ("send"); }
//put counter at end of selected file, and find length
fseek (filefd, 0, SEEK_END);
long fileLength = ftell (filefd);
//convert length to network form and send it to client
long fl = htonl(fileLength);
//Are we sure this is sending all the bytes??? TEST
if (send (new_fd, &fl, sizeof fl, 0) == -1)
{ perror ("send"); }
//variable stating amount of data unsent
long len = fileLength;
//Until file is sent, keep sending
while(len > 0) {
printf("remaining: %d\ntotal: %d\n", len, fileLength);
//set counter at beginning of unread segment
fseek (filefd, fileLength - len, SEEK_SET);
//length of the segment; 255 unless last segment
short bufLength;
if (len > 255) {
len -= 255;
bufLength = 255;
} else {
bufLength = len;
len = 0;
}
//buffer of 256 bytes; 1 byte for byte-length of segment, 255 bytes of data
char buf[256];
//Set first byte of buffer as the length of the segment
//converting short to char requires subtracting 128
buf[0] = bufLength - 128;
//read file into the buffer starting after the first byte of the buffer
fread(buf + 1, 1, bufLength * sizeof(char), filefd);
//Send data too client
if (send (new_fd, buf, sizeof buf, 0) == -1)
{ perror ("send"); }
}
fclose (filefd);
close (new_fd);
exit (0);
}
close (new_fd);
}
return 0;
}
Note: I've simplified the code a bit, to make it clearer I hope.
Anything beginning with //... represents a bunch of code
You seem to be assuming that each send() will either transfer the full number of bytes specified or will error out, and that each one will will pair perfectly with a recv() on the other side, such that the recv() receives exactly the number of bytes sent by the send() (or error out), no more and no less. Those are not safe assumptions.
You don't show the code by which you set up the network connection. If you're using a datagram-based protocol (i.e. UDP) then you're more likely to get the send/receive boundary matching you expect, but you need to account for the possibility that packets will be lost or corrupted. If you're using a stream-based protocol (i.e. TCP) then you don't have to be too concerned with data loss or corruption, but you have no reason at all to expect boundary-matching behavior.
You need at least three things:
An application-level protocol on top of the network-layer. You've got parts of that already, such as in how you transfer the file length first to advise the client about much content to expect, but you need to do similar for all data transferred that are not of pre-determined, fixed length. Alternatively, invent another means to communicate data boundaries.
Every send() / write() that aims to transfer more than one byte must be performed in a loop to accommodate transfers being broken into multiple pieces. The return value tells you how many of the requested bytes were transferred (or at least how many were handed off to the network stack), and if that's fewer than requested you must loop back to try to transfer the rest.
Every recv() / read() that aims to transfer more than one byte must be performed in a loop to accommodate transfers being broken into multiple pieces. I recommend structuring that along the same lines as described for send(), but you also have the option of receiving data until you see a pre-arranged delimiter. The delimiter-based approach is more complicated, however, because it requires additional buffering on the receiving side.
Without those measures, your server and client can easily get out of sync. Among the possible results of that are that the client interprets part of the file name or part of the file content as the file length.
Even though you removed it from that code I'll make an educated guess and assume that you're using TCP or some other stream protocol here. This means that the data that the servers sends is a stream of bytes and the recv calls will not correspond in the amount of data they get with the send calls.
It is equally legal for your first recv call to just get one byte of data, as it is to get the file name, file size and half of the file.
You say
When I put in a print statement,
but you don't say where. I'll make another educated guess here and guess that you did it on the server before sending the file length. And that happened to shake things enough that the data amounts that were sent on the connection just accidentally happened to match what you were expecting on the client.
You need to define a protocol. Maybe start with a length of the filename, then the filename, then the length of the file. Or always send 256 bytes for the filename regardless of how long it is. Or send the file name as a 0-terminated string and try to figure out the data from that. But you can never assume that just because you called send with X bytes that the recv call will get X bytes.
I believe the issue is actually a compound of everything you and others have said. In the server code you send the name of the file like this:
send (new_fd, argv[1], strlen (argv[1]) * sizeof(char), 0);
and receive it in the client like this:
recv (sockfd, name, 255 * sizeof (char), 0);
This will cause an issue when the filename length is anything less than 255. Since TCP is a stream protocol (as mentioned by #Art), there are no real boundaries between the sends and recvs, which can cause you to receive data in odd places where you are not expecting them.
My recommendation would be to first send the length of the filename, eg:
// server
long namelen = htonl(strlen(argv[1]));
send (new_fd, &namelen, 4, 0);
send (new_fd, argv[1], strlen (argv[1]) * sizeof(char), 0);
// client
long namelen;
recv (sockfd, &namelen, 4, 0);
namelen = ntohl(namelen);
recv (sockfd, name, namelen * sizeof (char), 0);
This will ensure that you are always aware of exactly how long your filename is and makes sure that you aren't accidentally reading your file length from somewhere in the middle of your file (which is what I expect is happening currently).
edit.
Also, be cautious when you are sending sized numbers. If you use the sizeof call on them, you may be sending and receiving different sizes. This is why I hard-coded the sizes in the send and recv for the name length so that there is no confusion on either side.
Well, after some testing, I discovered that the issue causing the problem did have something to do with htonl(), though I had still read the data incorrectly in the beginning. It wasn't that htonl() wasn't working at all, but that I didn't realize a 'long' has different lengths depending on system architecture (thanks #tofro). That is to say the length of a 'long' integer on 32-bit and 64-bit operating systems is 4 bytes and 8 bytes, respectively. And the htonl() function (from arpa/inet.h) for 4-byte integers. I was using a 64-bit OS, which explains why the value was being fudged. I fixed the issue by using the int32_t variable (from stdint.h) to store the file length. So the main issue in this case was not that it was becoming out of sync (I think). But as for everyone's advice towards developing an actual protocol, I think I know what exactly you mean, I definitely understand why it's important, and I'm currently working towards it. Thank you all for all your help.
EDIT: Well now that it has been several years, and I know a little more, I know that this explanation doesn't make sense. All that would result from long being larger than I expected (8 bytes rather than 4) is that there's some implicit casting going on. I used sizeof(long) in the original code rather than hardcoding it to assume 4 bytes, so that particular (faulty) assumption of mine shouldn't have produced the bug I saw.
The problem is almost certainly what everyone else said: one call to recv was not getting all of the bytes representing the file length. At the time I doubted this was the real cause of the behaviour I saw, because the file name (of arbitrary length) I was sending through was never partially sent (i.e. the client always created a file of the correct filename). Only the file length was messed up. My hypothesis at the time was that recv mostly respected message boundaries, and while recv can possibly only send part of the data, it was more likely that it was sending it all and there was another bug in my code. I now know this isn't true at all, and TCP doesn't care.
I'm a little curious as to why I didn't see other unexpected behaviour as well (e.g. the file name being wrong on the receiving end), and I wanted to investigate further, but despite managing to find the files, I can't seem to reproduce the problem now. I suppose I'll never know, but at least I understand the main issue here.
I am writing a simple client/server application and I ran into a very weird issue.
I am trying to send a .zip file to the client and some more data after that.
Sending the .zip works fine, the server writes to the socket, the client reads from the socket, just as intended.
The problem is right after that.
The server runs fine and keeps writing to the socket, but the client just won't read anything. It gets stuck on the very next read() call, no matter what I try to send.
I've checked to see if the socket descriptors are alright, and they are. I also thought that maybe there is not enough data in the socket for the client to read, but there definitely is.
I also tried doing the same write/read before and after sending that .zip file: it works fine before, but client doesn't see it after sending that .zip.
I am out of ideas.
Here's the function I use to send the .zip:
typedef struct thData{
int idThread; //thread ID
int cl; //client descriptor
}thData;
void send_info(struct thData tdL)
{
char file_path[256]="v1.zip";
char sd_buffer[256];
bzero(sd_buffer, 256);
FILE *fd = fopen(file_path, "rb");
if(fd == NULL)
{
printf("ERROR: %s not found.\n", file_path);
exit(1);
}
int read_size;
int write_size;
while((read_size = fread(sd_buffer, sizeof(char), 256, fd)) > 0)
{
if((write_size=write(tdL.cl, sd_buffer, read_size)) < 0)
{
perror("ERROR: writing to client: \n");
break;
}
bzero(sd_buffer, 256);
}
}
And what I use to receive the .zip:
void receive_info(int sd) //sd being the socket descriptor
{
char* file_path = "subject.zip";
char received_buffer[256];
int total_received=0;
int total_wrote=0;
FILE *fd = fopen(file_path, "wb");
if(fd == NULL)
printf("Cannot open %s\n", file_path);
else
{
bzero(received_buffer, 256);
int read_size = 0;
while((read_size = read(sd, received_buffer, 256)) > 0)
{
total_received=total_received+read_size;
int write_size = fwrite(received_buffer, sizeof(char), read_size, fd);
total_wrote=total_wrote+read_size;
if(write_size < read_size)
{
perror("ERROR: \n");
}
bzero(received_buffer, 256);
if (read_size == 0 || read_size != 256)
{
break;
}
}
if(read_size < 0)
{
perror("ERROR: reading: ");
exit(1);
}
fclose(fd);
}
}
Any help would be greatly appreciated.
I think that the problem is that you are reading too much.
In TCP there are no boundaries in the packets sent from one peer to the other. It is just a stream of bytes, and the pieces received from recv/read() bear no relation (in principle) to the pieces sent from send/write().
Now, imagine that your ZIP file is 300 bytes long, and your extra data is 10 bytes long. Your sender code will do:
write 256 bytes (first piece from ZIP).
write 44 bytes (last piece from ZIP).
write 10 bytes (the extra data).
And your receiver code will do:
read 256, get 256 bytes (first piece from ZIP).
read 256, get 54 bytes (last piece from ZIP plus the extra data).
read XXX bytes, wait forever!
If you look carefully to the ZIP files you'll probably see those extra bytes at the end of subject.zip.
The solution, if you don't want to close and open another socket, is to make the protocol a bit more complicated. For example, you could send a structure before the file (a header) that includes the size of the file. That way the receiver will know when to stop reading.
PS: Note that your code has a few risky edges. For example, write() might not write all the given bytes, but you are not checking that; you are not closing the file...
PS2: I find curious that you feel the need to write sizeof(char) instead of just 1 but you write 256 instead of sizeof(sd_buffer).
This is the snippet of client code:
connect(DescrittoreClient, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
strcpy(Buffer, filename);
send(DescrittoreClient, Buffer, strlen(Buffer), 0);
fd = open(filename, O_CREAT | O_WRONLY,0644);
while ((nread = read(DescrittoreClient, Buffer, sizeof(Buffer))) != 0) {
write(fd, Buffer, nread);
memset(Buffer,0,sizeof(Buffer));
}
int gg;
while( (gg = recv(DescrittoreClient, Buffer, sizeof(Buffer), 0)) == -1) continue;
printf("%d\n", gg);
printf("Risposta del server: %s\n", Buffer);
close(DescrittoreClient);
return EXIT_SUCCESS;
And this is the snippet of the server code:
while(1){
rc = recv(DescrittoreClient, filename, sizeof(filename), 0);
fd = open(filename, O_RDONLY);
fstat(fd, &stat_buf);
offset = 0;
rc = sendfile(DescrittoreClient, fd, &offset, stat_buf.st_size);
if (rc != stat_buf.st_size) {
fprintf(stderr, "incomplete transfer from sendfile: %d of %d bytes\n", rc, (int)stat_buf.st_size);
exit(1);
}
strcpy(Buffer, "Dati inviati correttamente");
if( (send(DescrittoreClient, Buffer, strlen(Buffer), 0)) == -1){
printf("Errore nell'invio della stringa\n");
close(DescrittoreClient);
close(fd);
exit(1);
}
}
close(DescrittoreServer);
return EXIT_SUCCESS;
This is the expected behaviour:
Client --Give me file X--> Server
Server --send the file X--> Client
Server -->send string "File has been sent"--> Client
But this is the real behaviour:
Client --Give me file X--> Server
Server --send the file X--> Client
Server -->_NOTHING_--> Client
So the problem is that the client doesn't receive "File has been sent". I've checked if the problem is server's side but it isn't (in fact the server send the string)
(based on the comments above) -- the thing to realize is that TCP communication is 100% stream based, with no built-in framing. That means that all the TCP layer does is make sure that the receiver receives the bytes in the same order that the sender sent them -- but it does not guarantee that the bytes will be received in the same intervals as they were sent. (For example, if you send() 10 bytes, then 20 bytes, then 50 bytes, the receiver might receive all 80 bytes at once, or 63 bytes followed 17 bytes, or any other combination of recv()s that sum up to 80 bytes total).
Therefore in order to send multiple separate items in such a way that the receiver can interpret them correctly, you need to define your own rules for framing the data. In your case you need to send n bytes of file data, followed by a string, and what your receiving program needs to know is how many bytes of file data to expect. That way, once it has received (that many) bytes, it knows the remaining bytes are string data and won't just write them to the file.
My suggestion would be to send the file's size first (either as a 4-byte integer, or an 8-byte long if you need to support file sizes greater than 4 gigabytes). Then the receiving code would be like this:
Receive 4 bytes
Interpret those four bytes as an integer (you may want to pass this value through htonl() on the sender and ntohl() on the receiver, in order to avoid possible endian-ness issues here)
Receive N bytes and write them to the file
Receive any remaining bytes as a string.
I have created an echo server under UDP, and am working on a client that splits up a given file into packets and transfers it to a server, which then returns the packets to be reassembled by the client.
Sending the individual packets works just fine. However, I am trying to use the UDP_CORK socket option to transfer as many parts of the file as possible in one packet. I am working with the following code:
#define LINE_SIZE 80
...
// s = socket descriptor.
...
int one = 1;
int zero = 0;
setsockopt(s, IPPROTO_UDP, UDP_CORK, &one, sizeof(one)); // cork
/* Send to server, receive from server, write to new file. */
FILE *orig, *copy;
char line[LINE_SIZE];
if ((orig = fopen(input + 3, "rb")) == NULL) // original file
print_error(input);
if ((copy = fopen(filename, "wb")) == NULL) // copy of file
print_error("fopen");
while (fread(line, sizeof(char), LINE_SIZE, orig) > 0) {
if (sendto(s, line, LINE_SIZE, 0, (struct sockaddr *)&srv, len) == -1)
print_error("sendto");
}
setsockopt(s, IPPROTO_UDP, UDP_CORK, &zero, sizeof(zero)); // uncork
if (recvfrom(s, line, LINE_SIZE, 0, (struct sockaddr *)&srv, &len) == -1)
print_error("recvfrom");
fwrite(line, sizeof(char), LINE_SIZE, copy);
The server indicates that it is only receiving the one "normal-sized" packet, and not the corked packet that I would like. It follows that it is only sending back this one normal packet, received by the client via the recvfrom() call.
I am not sure that I set up the UDP_CORK correctly. Is the second argument correct? I am unfamiliar with what the second option truly means, as the man page was not entirely clear. It is also possible that I am misunderstanding how the cork works.
A few things to note:
I consider myself an intermediate-level programmer, but I have little network programming experience.
I realize that UDP is not the best to transfer files. This is eventually going to be adapted to a different protocol that I am helping to develop.
Thank you!
How big is the file? You know that UDP datagrams are limited to 64K in size, right? And then anything over 1472 bytes (1500 bytes of available ethernet payload less minimum of 20 bytes of IP header, less 8 bytes of UDP header) is IP-fragmented.
Then you never check for the return value of the setsockopt(2). How do you know it's succeeding?
Then fread(3) tells you how much it read, but you still try to send LINE_SIZE bytes. This is wrong.