void download(char *file)
{
int size = getsize(file);
printf("Got size %d\n", size);
sprintf(buff, "GET %s\n", file);
send(sockfd, buff, strlen(buff), 0);
rsize = recv(sockfd, buff, 1000, 0);
sscanf(buff, "%d", &resultcode);
printf("%s", buff);
if (strcmp(buff, "+OK\n") != 0)
{
printf("download failed\n");
}
FILE *dlfile = NULL;
if ((dlfile = fopen(file, "r")) != NULL)
{
dlfile = fopen(file, "w");
do
{
rsize = recv(sockfd, buff, 1000, 0);
for (int i = 0; i < rsize; i++)
{
fprintf(dlfile, "%c", buff[i]);
}
size = size - rsize;
} while (size != 0);
}
fclose(dlfile);
}
I am trying to make the download function print out contents of file user typed, then save it to their current directory. I did a debug line printf("%s", buff); and it prints out +OK\n(filename). It is supposed to print out +OK\n. It also prints out download failed then a segmentation fault error. What am I missing?
Several things going on here. First, recv and send basically operate on arrays of bytes so they do not know about line endings and such. Also note that recv is not guaranteed to fill the buffer - it generally reads what is available up to the limit of the buffer. For your strcmp against "+OK\n", you could use strncmp with a length of 4 but that is a bit direct (see below). Next note that the buff string is not null terminated by recv so your printf could easily crash.
When you go in to your loop, the buffer already has part of the rest of your I/O in it. May include other fields or parts of the file. You need to process it as well. It is not clear to me what getsize does - but using that size to drive your loop seems off. Also, your loop to fprintf the values can be replaced by a call to fwrite.
Overall, you need to properly buffer and then parse the incoming stream of data. If you want to do it yourself, you could look at fdopen to get a FILE object.
Related
I'm writing a simple text-based protocol interpreter for server. The server will receive a response in the format of METHOD [filename]\n[file_size][data] from socket, where the string length of METHOD, filename, and data vary. file_size is the number of bytes of data. Since the length of METHOD and filename vary, I try to use fscanf() to capture them. After that, I use read() to get file_size and data. However, the problem is read() never prints. I wrote a small case to illustrate my problem:
#define MSGSIZE 16
char* msg1 = "hello world\n#1";
int main()
{
char inbuf[MSGSIZE], *buf_1, *buf_2;
int p[2], i;
if (pipe(p) < 0)
exit(1);
write(p[1], msg1, MSGSIZE);
FILE *fd = fdopen(p[0], "r");
fscanf(fd, "%ms %ms\n", &buf_1, &buf_2);
printf("buf_1: %s, buf_2: %s\n", buf_1, buf_2);
// read never prints
read(fileno(fd), inbuf, 3);
printf("buf: %s\n", inbuf);
return 0;
}
I would expect fscanf() to move the character pointer to '#' in "hello world\n#1", then read should be able to print out following characters, which didn't happen. I'm a bit confused because if I call fscanf() instead of read, it would print. Is this because fscanf() manipulates file indicator differently from read()?
You should not mix operations on the FILE * with operations on the underlying file descriptor. If you just replace read with an fread, it should behave as you expect. The fscanf (probably, almost certainly) reads everything from the pipe and stores it in an internal buffer, so your read has no data left to consume. To access the internal buffer, use fread or fgetc or fgets, but don't try to work on the underlying file descriptor.
Mixing FILE* stream I/O with system calls on the underlying file descriptor is the problem. Here is a change I made that shows one way to avoid it in your code.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MSGSIZE 16
char* msg1 = "hello world\n#1\n";
int main()
{
char inbuf[MSGSIZE], *buf_1, *buf_2;
int p[2], i;
if (pipe(p) < 0)
exit(1);
write(p[1], msg1, MSGSIZE);
FILE *fd = fdopen(p[0], "r");
fscanf(fd, "%ms %ms\n", &buf_1, &buf_2);
printf("buf_1: %s, buf_2: %s\n", buf_1, buf_2);
// read never prints
//retVal = read(fileno(fd), inbuf, 3);
// replace read() with fgets()
if(fgets(inbuf, sizeof(inbuf), fd) == NULL)
{
printf("Error with fgets()\n");
}
else
{
printf("buf: %s\n", inbuf);
}
return 0;
}
I'm trying to print out the contents of a file, however the program will pause at the read() function and won't continue until I hit the enter key. Once the enter key is pressed, nothing is printed to the terminal. The rest of the program is not shown, but here is the method that is causing the issue.
{
char buff[1024];
ssize_t bytesRead = 0;
int readFile, error;
if((readFile = open(file,O_RDONLY)<0))
{
printf("can't open %s\n",file);
error = errno;
return -1;
}
do{
memset(buff,0,1024);
if((bytesRead=read(readFile,buff,1024))==-1)
{
printf("error reading file");
error = errno;
printf("%d",error);
}
else
printf("%s",buff);
}while(bytesRead==1024);
printf("\n");
close(readFile);
return 1;
}
Alternatively, if I change the read() function to pread(file,buff,1024,0) it throws an illegal seek 29 error.
Hitting the enter key should not effect the read call unless you are reading from stdin (standard input). In that case, the input you provided - whitespace - may be printed out in the printf("%s", buff); call. If you could include some steps on how you found out that this is the method causing the issue or how you found out that it pauses at the read line (and if you are reading from /dev/stdin), it may be easier to help.
Consequently, the same printf call may never return, if the bytes read do not contain a null and the bytesRead count is 1024 - the string in buff would not be null terminated. You can fix this by either doing buff[1023] = '\0'; or by setting a length limit in the printf call like printf("%.1024s", buff);
do{
memset(buff,0,1024);
if((bytesRead=read(readFile,buff,1024))==-1)
{
printf("error reading file");
error = errno;
printf("%d",error);
}
else
printf("%s",buff);
}while(bytesRead==1024);
Your loop isn't correct. It assumes that read() fills the buffer; it fails if the file isn't a multiple of 1024 bytes; it corrupts errno before printing it; and it does unnecessary memset() operations. Honey it's a mess. It should look like this:
while ((bytesRead=read(readFile, buff, sizeof buff)) > 0)
{
printf("%.*s", bytesRead, buff);
}
if (bytesRead < 0)
{
error = errno;
perror("error reading file");
printf("errno=%d\n",error);
}
I tried and looked up TONS of pages over the net, found NOTHING that fits windows and is working always, I tried this one over the TCP protocol (streaming byte-by-byte untill you bump into 3 - I tried it on files with no 3 in it :PPPP and by 3 I mean the ASCII value 3 and not the digit '3').
Server side:
int sendFile(SOCKET s, const char* file_path)
{
FILE* fp = fopen(file_path, "rb");
int i, err = 0, bytesSent, isOk = 1;
char ch = 0;
if(!fp)
{
fclose(fp);
return 1;
}
while(ch != EOF && isOk)
{
fread(&ch, sizeof(char), 1, fp);
if(ch != EOF)
{
bytesSent = send(s, &ch, sizeof(char), 0);
if(bytesSent <= 0)
{
return 1;
}
}
else
{
isOk = 0;
}
}
ch = 3;
bytesSent = send(s, &ch, sizeof(char), 0);
fclose(fp);
return 0;
}
Client side:
int recvFile(SOCKET s, const char* file_path)
{
FILE* fp = fopen(file_path, "wb");
int bytesRecieved;
char ch;
if(!fp)
{
fclose(fp);
return 1;
}
bytesRecieved = recv(s, &ch, sizeof(char), 0);
if(bytesRecieved <= 0)
{
return 1;
}
while(ch != 3)
{
fwrite(&ch, sizeof(char), 1, fp);
putch(ch);
bytesRecieved = recv(s, &ch, sizeof(char), 0);
if(bytesRecieved <= 0)
{
return 1;
}
}
fclose(fp);
return 0;
}
The sockets are functioning well and sending and receiving well (I'm talking about sending regular messages, without the functions).
It's not returning 1, it's just turns into an infinite loop.
No idea why it's not working, any idea ? I'm totally desperate.
Instead of comparison with EOF, you should use the feof function while reading from file. EOF is just an error code returned by some functions, not an actual character in the file.
Also, I notice that the thing with the character 3 seems a way to signal the end of the file. You should consider normalizing the data transfer. You could send at the beginning of the communication the size of the file, and the client reads exactly the size of the file, to avoid having problems with files that contain that character (3 is still a valid character).
For inspiration, take a look at an old project of mine that implements this behavior (although it's linux only).
I once fell in trouble reading on tcp socket by chunks smaller than what was used for writing. On a socket you write packets of a determined length (write or send), and implementation may discard the end of the packet if read size is shorter.
From man page on recv : All three routines [recv, recvfrom, recvmsg] return the length of the message on successful completion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from.
When reading, you should allways use a buffer of a size at least equals of the longest buffer used in writing.
You could dump what you receive to confirm.
I have to write a SSL client in C that connects to a server,and gets either a html,either a file. I managed to get the html,but i can't download a binary file. For example,i'm trying to download a 3.8mb file from https://www.openssl.org/source/openssl-1.0.0d.tar.gz and my code only manages to download 1.1mb of them,and i don't even know if i get the right data in it.
Here is the function that i made for it:
char *sslReadfile (connection *c)
{
const int readSize = 1024;
char *rc = NULL;
int received, count = 0;
char buffer[1024];
char filename[40];
printf("Input the file name to be saved:\n");
scanf("%s",filename);
FILE *fp;
fp = fopen(filename, "wb");
if (c)
{
while (1)
{
if (!rc)
rc = malloc (readSize * sizeof (char) + 1);
else
rc = realloc (rc, readSize * sizeof (char) + 1);
received = SSL_read (c->sslHandle, buffer, readSize);
buffer[received] = '\0';
if (received > 0)
fprintf(fp,"%s",buffer);//strcat (rc, buffer);
if (received < readSize)
break;
//count++;
}
}
printf("\nFile saved!! %s !!!\n\n",filename);
fclose(fp);
return rc;
}
oh, and i call it like that:
char command[50];
sprintf(command,"GET /%s\r\n\r\n",relativepath);
sslWrite (c, command);
response = sslReadfile (c);
where c is my connection.
Don't use fprintf to write binary data. Use fwrite. The reason your output is smaller is that fprintf is stopping at the first null character, skipping any characters that remain in the 1024 byte buffer. Also, you don't appear to be using, and don't need the mallocd rc buffer.
So, after the call to SSL_read, you want something like this:
if (received <= 0) break;
fwrite(buffer, 1, received, fp);
You break the loop when received < readSize, instead you should only break the loop when received <= 0 and you have inspected SSL_shutdown() and/or SSL_get_error().
Also, you shouldn't NUL terminate your buffer and use fprintf, but keep the buffer as is while using fwrite. You are now introducing NULs in your data that weren't there.
I am writing a very simple webserver in c (winsock2).
I am able to return the contents of my html pages.
Currently, what I am doing is writing the contents of a file into a char* buffer and sending it using "send()"
Although when I try to read an image (jpg, bmp), I can't write the characters into a buffer a some characters are "null" (0).
How can I send a whole image file ?
Thanks.
You can store null character in a char* buffer. You just have to use a counter to remember how many characters were written, instead of recomputing it by counting number of non-null characters (this can either be an integer or a pointer to the next point of insertion in the buffer).
To send a file, you'll do something like that:
int sendFile(int sock, const char* filename) {
FILE* file = fopen(filename, "rb");
if (file == NULL)
return -1;
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return -1;
}
off_t size = ftello(file);
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return -1;
}
if (SendBinaryFileHeaderAndSize(sock, size) < 0) {
fclose(file);
return -1;
}
char buffer[4096];
for (;;) {
size_t read = fread(buffer, 1, sizeof(buffer), file);
if (read == 0) {
int retcode = 0;
if (ferror(file))
retcode = -1;
fclose(file);
return retcode;
}
for (size_t sent = 0; sent < read;) {
int ret = send(sock, buffer + sent, read - sent, 0);
if (ret < 0) {
fclose(file);
return -1;
}
assert(ret <= read - sent);
sent += ret;
}
}
}
You need to understand how send() and fread() work. 0s in the buffer are not a problem for send or fread - they do not interpret their buffers as null-terminated strings.
Depending on how you load the image into your webserver, you would need to use either Winsock:TransmitPackets or Winsock:TransmitFile, also also wrapping the image in the appropriate HTTP headers
Note that these are MS specific extensions.
Also see c++ - Bitmap transfer using winsock getdibits and setdibits