Transferring files using UDP sockets in C - c

I'm fairly new to socket programming in C, so the code below may have a ton of newbie mistakes.
I'm trying to make a client-server application in which the server will transfer a file to the client using an UDP socket. Both the client and the server will run on Linux hosts. It's an assignment, so it has to be done that way. Other client-server communications may use a TCP socket, but the file transfer MUST be via UDP. The program works correctly for small files, but if I try to send a slightly larger file (say, a 600 kb text file), the client will stop receiving the packets, even though the server will send them all. Here's the file transfer part of the server code:
FILE* myFile;
long fileSize, readBytes, sentBytes, sizeCheck;
uint32_t encodedFileSize;
myFile = fopen(fileName, "rb");
if(myFile == NULL)
{
perror("Error when opening file.");
exit(1);
}
fseek(myFile, 0, SEEK_END);
fileSize = ftell(myFile);
encodedFileSize = htonl(fileSize);
rewind(myFile);
sizeCheck = 0;
write(myTCPSocket, &encodedFileSize, sizeof(encodedFileSize));
if(fileSize > 255)
{
while(sizeCheck < fileSize)
{
readBytes = fread(bufferRW, 1, 256, myFile);
sentBytes = sendto(sockfdUDP, bufferRW, readBytes, 0, (struct sockaddr*)&cli_addr, udpAddressSize);
sizeCheck += sentBytes;
}
}
else
{
readBytes = fread(bufferRW, 1, 256, myFile);
sentBytes = sendto(sockfdUDP, bufferRW, readBytes, 0, (struct sockaddr*)&cli_addr, udpAddressSize);
}
if(fileSize == sizeCheck)
{
printf("Success.\n");
}
else
{
printf("Fail.\n");
}
fclose(myFile);
fflush(stdout);
close(sockfdUDP);
As you can see, I used a TCP socket to send the client the file size. Here's the client code:
FILE *myFile;
long receivedBytes, writtenBytes, sizeCheck;
long fileSize, realFileSize;
char ack2[5] = "Ok";
sockfdUDP = socket(AF_INET, SOCK_DGRAM, 0);
read(socketTCP, &fileSize, sizeof(long));
realFileSize = ntohl(fileSize);
myFile = fopen(fileName, "wb");
if(myFile == NULL)
{
perror("Error when creating file.");
exit(1);
}
sizeCheck = 0;
if((realFileSize) > 255)
{
while(sizeCheck < (realFileSize))
{
receivedBytes = recvfrom(sockfdUDP, bufferRW, 256, 0, (struct sockaddr*)&serv_addr, &serv_addr_size);
writtenBytes = fwrite(bufferRW, 1, receivedBytes, myFile);
fflush(myFile);
sizeCheck += writtenBytes;
}
}
else
{
receivedBytes = recvfrom(sockfdUDP, bufferRW, 256, 0, (struct sockaddr*)&serv_addr, &serv_addr_size);
fwrite(bufferRW, 1, receivedBytes, myFile);
fflush(myFile);
}
if(realFileSize == sizeCheck)
{
printf("Success.");
}
else
{
printf("Fail.");
}
fclose(myFile);
close(sockfdUDP);
The "bufferRW" buffer was originally declared as char bufferRW[256] and passed to the function as an argument. Same goes for other undeclared variables.
Like I said before, the server will (apparently) send the whole file without any issues. However, the client will stop receiving packets after it's written about 423936 bytes (this may vary between executions). It'll just stay at the recvfrom line, without reading anything.
Now, I'm sure the problem is not being caused by a faulty connection since I'm testing both processes on the same host. And before you ask "What is it with the 256 byte packet size?", there's this weird bug that will throw me a segmentation fault on the realFileSize = ntohl(fileSize); client line if I use a buffer size of, say, 1500.
Could you please tell me what am I missing here?
EDIT: I'm trying with different file sizes now. It seems to handle files larger than 256 bytes without issue (it enters and exits the while loops correctly on both client and server), but the client will start having problems when the file is bigger than, say, 300 kb.
EDIT 2: I just debugged the program. Apparently, the server sends the entire file before the client can even enter its while loop.
EDIT 3: I think I know what's causing the issue. It seems like if the server sends a bunch of packets before the client starts reading, the client will read up to 278 packets, regardless of their size. If I try sending, say, 279 before the client starts reading it won't read the 279th packet. So if the server sends its packets fast enough, the number of packets that the client hasn't read yet will exceed 278 and the client won't finish reading all of the packets. Any ideas on how to fix this?

long* fileSize declared a pointer to a long, but in your code, it's pointing nowhere. In fact, it's pointing to a random address. You should declare it as long fileSize, and call read(socketTCP, &fileSize, sizeof(long)) instead.
You should check the return value of read, write, etc, to guarantee they did not fail. For example, sendto returns -1 on error. You're ignoring this, and incrementing sizeCheck with this value anyway.
UDP is not a reliable protocol for file transfers, but if you cannot do without it, you better implement some controls that TCP already gives you for free, like packet reordering, data checksum, etc. And that can be a very complex task by itself.
Compile your code with -Wall -Wextra. The compiler will give you hints about what could be potentially wrong. I see you're still using *fileSize in a comparison, which is clearly wrong.
After you fix the *fileSize issue, your loop-condition is still using the wrong value (due to fileSize = ntohl(fileSize)). You need to store this value in another variable, or change your loop-condition to use the real file size.
Regarding your EDIT 3, you need to somehow synchronise your client & server, so they can start the transmission at the same time. However, a sender that is much faster than the receiver will still cause packet loss. To solve this, you'll also need to implement packet acknowledgement, and retransmit a packet if the sender doesn't receive an ACK for a respective sent packet after a timeout. This is something TCP already does for you.
A simpler (but not completely reliable) way, would be to slow down the sending process a bit - maybe using nanosleep between each call to sendto.

Related

Sockets "hanging up" in C/buffer smaller than usual

When I send() and recv() data from my program locally it works fine.
However, on my remote server, the same program, which usually receives data in chunks of 4096, will receive in buffers capped at 1428, which rarely jump above this number.
Worse of all, after a minute or so of transferring data the socket just freezes and stops execution, and the program perpetually stays in this frozen state, like so:
Received: 4096
Received: 4096
Received: 3416
The server is simple, it accepts a connection from a client and receives data in chunks of 4096, which works absolutely fine locally, but on my remote server it is failing consistently, unless I only send a small chunk of data (sending 1000 byte files worked fine).
int main()
{
while(1){
int servSock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
if(servSock < 0){
fprintf(stderr, "Socket error.\n");
continue;
}
struct sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(atoi(STANDARD_PORT));
if(bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0){
fprintf(stderr, "Bind error.\n");
close(servSock);
continue;
}
if(listen(servSock, BACKLOG) < 0){
fprintf(stderr, "Listen error.\n");
close(servSock);
continue;
}
printf("%s", "Listening on socket for incoming connections.\n");
struct sockaddr_in clntAddr;
socklen_t clntAddrLen = sizeof(clntAddr);
while(1) {
int newsock = accept(servSock, (struct sockaddr*) &clntAddr, &clntAddrLen);
if(newsock < 0){
fprintf(stderr, "Accept connection error");
return 1;
continue;
}
char clntName[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL)
printf("Handling client %s:%d\n", clntName, ntohs(clntAddr.sin_port));
char file[17];
memset(file, 0, 17);
int recvd = recv(newsock, file, 16, 0);
file[17] = '\0';
char local_file_path[200];
memset(local_file_path, 0, 200);
strcat(local_file_path, "/home/");
strcat(local_file_path, file);
printf(local_file_path);
FILE* fp = fopen(local_file_path, "wb");
char buffer[4096];
while(1)
{
memset(buffer, 0, 4096);
recvd = recv(newsock, buffer, 4096, 0);
printf("Received: %d\n", recvd);
fwrite(buffer, sizeof(char), recvd, fp);
if(recvd == -1 || recvd == 0) {
fclose(fp);
break;
} else
}
close(newsock);
}
close(servSock);
}
return 1;
}
EDIT: For more context, this is a Windows server I am adapting to linux. Perhaps the recv() call is blocking when it shouldn't be, I'm going to test with flags.
However, on my remote server, the same program, which usually receives data in chunks of 4096, will receive in buffers capped at 1428, which rarely jump above this number.
Insufficient context has been presented for confidence, but that looks like a plausible difference between a socket whose peer is on the same machine (one connected to localhost, for example) and one whose peer is physically separated from it by an ethernet network. The 1428 is pretty close to the typical MTU for such a network, and you have to allow space for protocol headers.
Additionally, you might be seeing that one system coallesces the payloads from multiple transport-layer packets more or differently than the other does, for any of a variety of reasons.
In any case, at the userspace level, the difference in transfer sizes for a stream socket is not semantically meaningful. In particular, you cannot rely upon one end of the connection to read data in the same size chunks that the other sends it. Nor can you necessarily rely on receiving data in full-buffer units, regardless of the total amount being transferred or the progress of the transfer.
Worse of all, after a minute or so of transferring data the socket just freezes and stops execution, and the program perpetually stays in this frozen state, like so:
"Worst" suggests other "bad", which you have not described. But yes, your code is susceptible to freezing. You will not see EOF on the socket until the remote peer closes their side, cleanly. The closure part is what EOF means for a network socket. The cleanness part is required, at the protocol level, for the local side to recognize the closure. If the other end holds the connection open but doesn't write anything else to it then just such a freeze will occur. If the other side is abruptly terminated, or physically or logically cut off from the network without a chance to close their socket, then just such a freeze will occur.
And indeed, you remarked in comments that ...
Both the client and the server are hanging. The client program just stops sending data, and the server freezes as well.
If the client hangs mid-transfer, then, following from the above, there is every reason to expect that the server will freeze, too. Thus, it sounds like you may be troubleshooting the wrong component.
Perhaps the recv() call is blocking when it shouldn't be, I'm going to test with flags.
There is every reason to think the recv() call is indeed blocking when you don't expect it to do. It's highly unlikely that it is blocking when it shouldn't.
It is possible to set timeouts for socket operations, so that they eventually will fail instead of hanging indefinitely when the remote side fails. Doing so would allow your server to recover, but it would not resolve the client-side issue. You'll need to look into that more deeply.*
*You might see the client unfreeze after the server times out and closes the connection on its end. Don't take that as a resolution.

Using recv() for receiving a uint32 network byte order number works sometimes and others not (using same commands)

I'm building a client/server over tcp for file transfer (C language): client sends request, server sends file size (uint32 network byte order) and binary file. I need clarifications for the reading of a uint32 by the client
-Client-
uint32_t filesize;
...
Creation of socket s and connection with the server, for cycle for sending all the files
...
if((recv(s, &filesize, sizeof(uint32_t), 0)) != sizeof(uint32_t))
{
printf("Filesize error\n"); continue;
}
filesize=ntohl(filesize);
...
-Server-
uint32_t filesize, filesize_t;
...
get filesize using fstat
...
filesize_n = htonl(filesize);
if((send(s, &filesize_n, sizeof(uint32_t), 0)) != sizeof(uint32_t))
{
printf("Filesize error\n"); continue;
}
The problem is, the client sometimes receives a wrong filesize, even if the previous run worked. Using Wireshark i can see that the payload sent is right, and Valgrind doesn't show anything strange regarding memory.
The server is already tested with a client given by my professor, so it should work as intended, while the client doesn't, i assume the problem is probably there.
The ifs on send and recv should confirm that the quantity of bytes is right. Any idea why the recv is not deterministic?
Not only that, if the filesize is right, getting the actual file always works, so maybe a problem with handling the uint32 format or the network byte order?

Printf makes program work in C, htonl and ntohl not working?

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.

Transfer files with sockets

I'm trying to transfer files (in this case, a .jpg file) with sockets in C. I have my client and my server code, but I don't know why the transfer file has more bytes than the original file. Here's the code:
SERVER
/***************************************************
*
* Starting to send data file
*
***************************************************/
// Read the size of the file
recv(client.fd_client, &file.size_file, sizeof(int), 0);
recv(client.fd_client, &file.size_string, sizeof(int), 0);
recv(client.fd_client, &file.name_of_file, file.size_string, 0);
printf("Size of the file: %.3f kB\n", file.size_file/1000.);
printf("Receiving the file \"%s\"...\n", file.name_of_file);
if((file.fd_file = creat(file.name_of_file, S_IRWXU)) < 0) {
error("Can't open the file: ");
}
while(1) {
bzero(buffer, SIZE_BUF);
if((numb_bytes = read(client.fd_client, buffer, SIZE_BUF)) < 0) {
error("Can't read the file.");
}
write(file.fd_file, buffer, numb_bytes);
received += numb_bytes;
printf("Received: %.3fkB (%.3f/%.3f) kB\n", numb_bytes/1000., recibed/1000., file.size_file/1000.);
if(numb_bytes == 0)
break;
}
printf("Recibidos %.3f/%.3f kB\n", recibed/1000., file.size_file/1000.);
CLIENT
/***************************************************
*
* Starting to send data file
*
***************************************************/
// Use strncpy!
strcpy(file.name_of_file, argv[3]);
file.fd_file = open(file.name_of_file, 0);
fstat(file.fd_file, &stat_file);
file.size_file = (unsigned int) stat_file.st_size;
file.size_string = (unsigned int) strlen(file.name_of_file) + 1;
printf("Size of the file: %.3f kB.\n", file.size_file/1000.);
printf("Sending the file \"%s\"...\n", file.name_of_file);
// Send data file
write(conection.sockfd, &file.size_file, sizeof(file.size_file));
write(conection.sockfd, &file.size_string, sizeof(file.size_string));
write(conection.sockfd, &file.name_of_file, sizeof(file.name_of_file));
while(1) {
sended += sendfile(conection.sockfd, file.fd_file, NULL, SIZE_BUF);
printf("send: %d\n", sended);
printf("num_bytes: %d\n", num_bytes);
if(sended == file.size_file)
break;
}
printf("Sended %.3f/%.3f kB\n", sended/1000., file.size_file/1000.);
And I have a couple of questions:
The transfer of the size and the name of the file happen correctly. What I don't understand is why when I use ntohl it gives me garbage. If I'm right, ntohl and ntos both use before transfer numbers with sockets because of the Little and Big Endian. So why does that happen?
The transfer of the file happens correctly too, but the client transfers or the server receives more bytes than the original file size. I try to transfer an image, but it can't open. So I tried with the source code of the client and it arrives fine to the server (with more bytes, as before), but when I open it has garbage at the top and then the source code. I think that the transfer occurs fine but arrives more bytes than needed. Can anyone give me a clue why this happens?
I'm transferring the file in 1024kB blocks, because when I tried to send the whole file with a single call I never can send it all. I saw examples on the Internet where they fixed some kind of offset until where it transfer and start to transfer again from this point. Is this necessary? At first I think it isn't, because I don't use it and the file arrives "good", like it automatically knows from where to continue the read of the file.
You're sending the entire name buffer:
write(conection.sockfd, &file.name_of_file, sizeof(file.name_of_file));
but reading only the length sent from the previous field:
recv(client.fd_client, &file.size_string, sizeof(int), 0);
recv(client.fd_client, &file.name_of_file, file.size_string, 0);
The difference between the buffer size and the string length is likely you're differential of additional file length. You can address this by changing this:
write(conection.sockfd, &file.name_of_file, sizeof(file.name_of_file));
to this, in your client code:
write(conection.sockfd, &file.name_of_file, file.size_string);
I'll review the additional sub-questions, but this is the primary reason your file size is wrong. I can't really make an educated decision on the correctness of your use of htonl and ntohl because you didn't include the code that transmogs those fields. It could be a problem with that. you may way to keep the sizes native in the structures at all times, and read them into temp vars, assigning the results afterward by sending said-vars through ntohl on the receiving side, and just sending the temp vars (after they receive the translations via htonl) on the sending side.

How to Properly Set up a UDP Socket using UDP_CORK

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.

Resources