I have a problem that I'm hoping is a mistake as I am a bit new to linux C.
I have a server, that sends a file to a client. The code works perfectly fine over a wired connection, 100% success rate every time. However, when I try to run that same code over a 3G USB connection at -65dB (Strong), the client will usually only receive the first 1 or 2 thousand bytes of the file. (Forgive the syntax errors won't let me space properly)
// CLIENT CODE
char* fs_name = "/target/to/send";
char sdbuf[LENGTH]; // Send buffer, LENGTH == 512
printf("Sending %s to the Client... \n", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
fprintf(stderr, "ERROR: File %s not found on server. (errno = %d)\n", fs_name, errno);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs))>0)
{
if(send(client, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
exit(1);
}
bzero(sdbuf, LENGTH);
}
printf("File send Success\n");
// SERVER CODE
char* fr_name = "/home/file/to/save";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened.\n", fr_name);
else
{
// zero out bytes
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
while((fr_block_sz = recv(sd, revbuf, LENGTH, 0)) > 0) //LENGTH == 512
{
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed.\n");
}
bzero(revbuf, LENGTH);
if (fr_block_sz == 0 || fr_block_sz != 512)
{
break;
}
}
// error checking
if(fr_block_sz < 0)
{
if (errno == EAGAIN)
{
printf("recv() timed out.\n");
}
else
{
fprintf(stderr, "recv() failed due to errno = %d\n", errno);
}
fclose(fr);
If anyone out there could shed some light on what I'm doing wrong I would be most appreciative. Thanks for reading.
EDIT: The server is Ubuntu 10.04 and the client is Arch Linux ARM and the type of file being sent is an ARM binary.
I think your main problem is here in your code labeled server but which really looks to be the client. You assume that the recv is going to get all 512 bytes on each recv but that's not true - you can and will get short reads. A little below you break out of the read loop if you haven't read exactly LENGTH (i.e. 512). That explains why your pgm quits after a couple of thousand bytes.
while ((fr_block_sz = recv(sd, revbuf, LENGTH, 0)) > 0) //LENGTH == 512
{
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if (write_sz < fr_block_sz)
{
error("File write failed.\n");
}
bzero(revbuf, LENGTH);
if (fr_block_sz == 0 || fr_block_sz != 512)
{
break;
}
}
You want something more like this:
while ((fr_block_sz = recv(sd, revbuf, LENGTH, 0)) > 0) //LENGTH == 512
{
if (fr_block_sz < 0)
if (errno == EAGAIN)
continue;
else
error;
if (fr_block_sz == 0)
break; //done
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if (write_sz < fr_block_sz)
{
error("File write failed.\n");
}
bzero(revbuf, LENGTH);
}
Related
I am new in networking and I try to write my first http server on C. However i faced a problem. When i try to send image by chunks i recive an error GET http://localhost/img.jpg net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK), whith html it works fine.
void answerOnGet(int socketCopy, char *fileName)
{
struct stat sbuf;
char filePath[SIZE];
determinateFilePath(fileName, filePath);
stat(filePath, &sbuf);
char responseHeader[2*SIZE];
char fileType[SIZE];
if (strstr(fileName, ".html") || !strcmp(fileName, "/"))
{
strcpy(fileType, "text/html");
}
else if (strstr(fileName, ".jpg"))
{
strcpy(fileType, "image/jpg");
}
else if(strstr(fileName, ".ico"))
{
strcpy(fileType, "image/ico");
}
else
{
fprintf(stderr, "Type error\n");
exit(1);
}
FILE *fd = fopen(filePath, "rb");
if(fd == NULL)
{
perror("Opening error");
exit(1);
}
snprintf(responseHeader, sizeof(responseHeader),
"HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: chunked\r\n"
"Content-type: %s\r\n\r\n", fileType
);
if(write(socketCopy, responseHeader, strlen(responseHeader)) < 0)
{
perror("Write error");
exit(1);
}
long unsigned sizeOfChunk = 0;
if(sbuf.st_size > SIZE)
{
sizeOfChunk = sbuf.st_size/50;
}
else
{
sizeOfChunk = sbuf.st_size;
}
char *buf = malloc(sizeof(char) * (sizeOfChunk + 2));
printf("Size of chunk = %ld\n", sizeOfChunk);
char amountOfBytes[SIZE];
while(fread(buf, sizeof(char), sizeOfChunk, fd) == (sizeOfChunk))
{
sprintf(amountOfBytes, "%lX\r\n", sizeOfChunk);
puts("\nStart to write");
if(write(socketCopy, amountOfBytes, strlen(amountOfBytes)) < 0)
{
perror("Write bytes error");
exit(1);
}
strcat(buf, "\r\n");
if(write(socketCopy, buf, strlen(buf)) < 0)
{
perror("Write error");
exit(1);
}
puts("\nEnd of write");
bzero(buf, sizeOfChunk);
bzero(amountOfBytes, strlen(amountOfBytes));
}
puts("Send zero chunk");
if(write(socketCopy, "0\r\n\r\n", strlen("0\r\n\r\n")) < 0)
{
perror("Write zero chunk error");
exit(1);
}
fclose(fd);
free(buf);
}
Your mistake is most likely
if(write(socketCopy, buf, strlen(buf)) < 0)
What does strlen(buf) do? It counts the number of bytes up to the first 0 byte.
Is that the correct number of bytes to write? Only if there's a 0 byte at the end and none before that.
Is there? Well, no. Files can have 0 bytes in the middle and fread does not put one at the end.
Instead of strlen(buf) you should use sizeOfChunk.
Other bugs:
As #rveerd pointed out, it's possible that you call write and the kernel only writes part of the data. If that happens, you still have to call it again to write the rest of the data. This may happen multiple times so you need a loop. I suggest making a function actually_write which does the loop, then you can call actually_write(socketCopy, buf, sizeOfChunk)
Your program doesn't send some of the bytes at the end sometimes. If the file is, for example, 1001 bytes long, then you do 20-byte chunks, and after the last full chunk there is still one byte, and fread returns 1 but you don't send the extra byte.
I'm sending a file from the client to the server.
Client sends filename
Server receives filename
Client sends file size
Server receives file size
Client sends file contents
Server receives file contents
When I print out the file size that I sent, it is the exact number of bytes as the file I wish to send so that's fine. The issue is that the server (receives) doesn't seem to exit the while loop when writing to the new file. I know this because the final print statement printf("The server has received the requested document\n"); is never reached and it just hangs. What could be causing this?
Client snippet (sends):
else if(strcmp(shortCommand, "put") == 0){
char *tmp = buf + 4;
char filename[MAX_BLOCK_SIZE];
size_t size, bytes_read, bytes_written;
int x;
strcpy(filename, "filename ");
strcat(filename, tmp);
FILE *fp;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "rb");
if(fp == NULL){
printf("ERROR: Requested file does not exist.\n");
}
else{
printf("Client sending filename...\n");
if ((x = write(sd, buf, sizeof(buf))) < 0){ //sending the file name to the client first
printf("Error sending client's filename.\n");
}
printf("Client sending file...\n");
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
printf("Sending file size\n");
if((write(sd, &size, sizeof(size))) < 0){ //sending filesize
printf("error sending file size\n");
}
printf("Sending file\n");
while((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0){ //sending file contents
if ((bytes_written = write(sd, buf, bytes_read)) < 0){
printf("Error sending client file.\n");
}
}
printf("bytes written: %ld\n", bytes_written);
fclose(fp);
}
}
Server snippet (receives):
if(strcmp(shortCommand, "put") == 0){
char *tmp = buf + 4;
char filename2[MAX_BLOCK_SIZE];
size_t filesize;
size_t total_bytes_read = 0;
ssize_t bytes_read = 0;
size_t error;
FILE *fp;
strcpy(filename2, tmp);
printf("Server receiving file name...\n"); //filename is received on the first read before this IF
fp = fopen(filename2, "wb");
if(fp == NULL){
printf("File could not be opened.\n");
exit(1);
}
printf("Server receiving file size...\n");
if((error = read(sd, &filesize, sizeof(filesize))) < 0){ //receiving file size
perror("Error reading filesize\n");
exit(1);
}
printf("Filesize is: %ld \n", filesize);
while(total_bytes_read < filesize){
while((bytes_read = read(sd, buf, sizeof(buf))) > 0){ //receving file contents and writing to file
fwrite(buf, 1, bytes_read, fp);
total_bytes_read += bytes_read;
if(ferror(fp)){
perror("error");
fclose(fp);
}
}
}
printf("The server has received the requested document.\n");
fflush(stdout);
fclose(fp);
}
After I exit the program by force, I can actually see that the file has been copied. Just doesn't exit that while loop to let me go back to the client.
Time for some basic debugging. I'd suggest changing your read loop to something like this:
while (total_bytes_read < filesize) {
printf("DEBUG A: total=%zu, size=%zu\n", total_bytes_read, filesize);
while ((bytes_read = read(sd, buf, sizeof(buf))) > 0) {
printf("DEBUG B: read=%zd\n", bytes_read);
fwrite(buf, 1, bytes_read, fp);
total_bytes_read += bytes_read;
printf("DEBUG C: total=%zu\n", total_bytes_read);
if (ferror(fp))
printf("DEBUG D\n");
perror("error");
fclose(fp);
}
printf("DEBUG E\n");
}
printf("DEBUG F\n");
}
printf("DEBUG G\n");
Then run it, piping the output through less or some other pager, it should then hopefully become clearer what's actually happening.
Feel free to post the output of this modified code (in a comment, or in the actual question), we'll no doubt be able to help with the analysis.
I'm trying to download a file from my server; both the client and the server are Linux, yet ssh_scp_read() returns an incorrect integer. According to the documentation the function writes up to 65536 bytes, yet is only reading 16384 when the file is 37980, but that's not my main concern; near the end of this 16384 bytes it starts to fill the buffer with NULL garbage, that will then be written to the file.
The creation of recursive directories works fine; the problem is downloading files larger than 16384 bytes. At this point I'll use sftp instead of scp, but I would like to know what I am doing wrong.
This is the function code:
int get(ssh_session gno_ses,ssh_scp scp)
{
int rc;
int size, permissions;
char *buff, *filename, path[PATH_MAX];
while(1)
{
rc = ssh_scp_pull_request(scp);
switch (rc)
{
// cases [...]
case SSH_SCP_REQUEST_NEWFILE:
size = ssh_scp_request_get_size(scp);
printf("Size is %d\n",size);
filename = strdup(ssh_scp_request_get_filename(scp));
permissions = ssh_scp_request_get_permissions(scp);
FILE *file;
file = fopen(filename, "w+");
if (!file)
{
ssh_scp_deny_request(scp,"Unable to open");
fprintf(stderr, " %s: %s\n", filename, strerror(errno));
fclose(file);
break;
}
buff = malloc(size);
printf("Size of buffer is %d\n", size);
if (!buff)
{
fprintf(stderr, "\nBuff memory allocation error.\n");
return SSH_ERROR;
}
if( ssh_scp_accept_request(scp) != SSH_OK)
{
fprintf(stderr, "Error accepting request: %s\n", ssh_get_error(gno_ses));
break;
}
do
{
rc = ssh_scp_read(scp, buff, size);
if (rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n", ssh_get_error(gno_ses));
break;
}
if (fwrite(buff, 1, size, file) != size)
{
perror("Error at writting to file: ");
break;
}
printf("ssh_scp_read got %d\n",rc);
} while (rc != 0);
fclose(file);
free(filename);
free(buff);
break;
}
}
return SSH_OK;
}
And this is the output:
Size is 37980
Size of buffer is 37980
ssh_scp_read got 16384
ssh_scp_read got 16384
ssh_scp_read got 5212
Error receiving file data: ssh_scp_read called under invalid state
Any input would be appreciated.
The problem was that I was writing size bytes when indeed scp_scp_read() had reported that it had read less than that:
rc = ssh_scp_read(scp, buff, size);
fwrite(buff, 1, size, file)
The fix is to write only rc bytes:
int len_loop = size;
int len;
do
{
rc = ssh_scp_read(scp, buff, size);
if (rc == SSH_ERROR || rc < 0)
{
fprintf(stderr, "Error receiving file data: %s\n", ssh_get_error(gno_ses));
break;
}
else if (!rc)
{
break;
}
len = fwrite(buff, 1, rc, file);
if (len != rc)
{
perror("Error at writting to file: ");
break;
}
printf("ssh_scp_read got %d\n",rc);
len_loop -= rc;
} while(len_loop);
change your inner loop as
int len = size;
do
{
rc = ssh_scp_read(scp, buff, size);
if (rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n",
ssh_get_error(gno_ses));
break;
}
if (fwrite(buff, 1, rc, file) != size)
{
perror("Error at writting to file: ");
break;
}
printf("ssh_scp_read got %d\n",rc);
len-=rc;
} while (len);
I'm trying to send files using TCP from a windows client to a Linux server in C.
The size of the buffer I use to send the data is 65535. When the size of the file exceeds this value, I get an error saying 'connection reset by peer' or the error code 10054. When the size of the file is less than 65535 bytes, the server receives only a part of it (usually 2760 bytes).
I just want to send files with a maximum size of 50 MB.
This is the part of the windows client that I use to send data:
char *fileName; // pointer to filename
char buf[65535]; // buffer
int fileSize; // # bytes to send
for(i = 0; i < ARRAYSIZE; i++) {
if(selectList[i] != NULL) {
// select file
fileName= selectList[i]; // get path and filename from selectList
printf("=============================================\nSending: %s\n", fileName);
filefd = fopen(fileName, "rb"); // open file
if(filefd == NULL) {
printf("File %s not found\n", fileName);
exit(1);
}
// read and send file
memset(buf, '\0', 65535);
while((fileSize= fread(buf, sizeof(char), 65535, filefd)) > 0) { // read file
if((numberOfBytes = send(sockfd, buf, fileSize, 0)) < 0) { // send buffer
printf("send: %s (Error: %d)\n", filename, WSAGetLastError());
break;
}
printf("#bytes = %i \n", numberOfBytes);
memset(buf, '\0', 65535);
}
printf("File %s send!\n", filename);
// close file after sending it
if(fclose(filefd) < 0) {
printf("fclose: %i", WSAGetLastError());
}
} else if(selectList[0] == NULL) {
printf("no files selected");
}
}
The selectList contains multiple strings such as: C:\Windows\test.txt
The recieve part of the Linux server:
char* fr_name = "/home/MtFS/UploadedFiles/public/testFile.gif";
FILE *fr = fopen(fr_name, "wb");
if(fr == NULL)
printf("[Open_File]file %s cannot be created\n", fr_name);
else {
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
while((fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) > 0) {
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz) {
error("[Write] error\n");
}
bzero(revbuf, LENGTH);
if (fr_block_sz == 0 || fr_block_sz != 512) {
break;
}
}
if(fr_block_sz < 0) {
if (errno == EAGAIN) {
printf("[Receive] time out\n");
}
else {
printf("[Receive] error\n");
exit(1);
}
}
printf("[Receive] succesfull\n");
fclose(fr);
}
What am I doing wrong?
Your problem are those 3 lines of code. That's not the correct way to know that you're done:
if (fr_block_sz == 0 || fr_block_sz != 512) {
break;
}
Also you check against 512 instead of LENGTH. But only 0 means that you're done (assuming your connection is not NONBLOCKED.)
As a side note: you do not have to clear your buffers (bzero, memset) before using them with a read since the read/recv will overwrite the content of the buffers anyway.
I think the culprit is this line in your server
if (fr_block_sz == 0 || fr_block_sz != 512) {
fr_block_sz cab be anything between 1 to 65535 - the size block that you sent.
In your code, when its not 512 so your server is terminating the connection.
The below code works fine for smaller files where the last packet contains data less than maximum length, the function exit properly by displaying file received.
How ever if the last packet or buffer of file being transmitted contains exact number as the size of receiving buffer array 512 in my case. then th program keeps waiting for next packet.
All files with size multiple of 512 in my case stuck.
Below is the code:
CLIENT code for receiving:
void receiveFile() {
printf("inside receiveFile method\n");
char* fr_name = "final.txt";
int i;
FILE *fr = fopen(fr_name, "a");
int LENGTH = 512;
int fileLength=0;
char revbuf[LENGTH];
if (fr == NULL) {
printf("File %s Cannot be opened.\n", fr_name);
} else {
printf("starting to write the file\n");
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
i=0;
while ((fr_block_sz = recv(4, revbuf, LENGTH, 0)) > 0) {
fileLength+=fr_block_sz;
i++;
printf("Received buffer: %d, %d\n",fr_block_sz,i);
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if (write_sz < fr_block_sz) {
error("File write failed.\n");
}
bzero(revbuf, LENGTH);
if (fr_block_sz == 0 || fr_block_sz != 512) {
break;
}
}
if (fr_block_sz < 0) {
if (errno == EAGAIN) {
printf("recv() timed out.\n");
} else {
fprintf(stderr, "recv() failed due to errno = %d\n", errno);
}
}
printf("FILE RECEIVED....Total Bytes received:%d \n",fileLength);
}
fclose(fr);
}
Server for Receiving the file:
void sendFile() {
printf("inside sendFile method\n");
char* fs_name = "mb.txt";
int LENGTH = 512;
int sfileLength=0;
char sdbuf[LENGTH];
int i=0;
printf("[Client] Sending %s to the Server... \n", fs_name);
FILE *fs = fopen(fs_name , "r");
if (fs == NULL) {
perror("ERROR: File not found.\n");
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while ((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0) {
i++;
printf("Sent:%d , %d \n", fs_block_sz,i);
sfileLength+=fs_block_sz;
if (send(4, sdbuf, fs_block_sz, 0) < 0) {
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n",
fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("File sent.... Total Bytes:%d\n", sfileLength);
fclose(fs);
}
if (fr_block_sz == 0 || fr_block_sz != 512) {
break;
}
Remove this code. The first part of the test can never be true due to the 'while' condition, and the second part is unnecessary for the same reason.