So I'm having a bit of trouble wrapping my head around growing a buffer, from what I'm seeing I'm definitely reading all of the bytes from the file descriptor, but it seems I'm not storing them in the buffer properly. Can someone help point me in the right direction?
char *read_all(int fd, int *nread){ //nread tracks total bytes read
int max_size = 1;
*nread = 0;
char *buf = malloc(max_size*sizeof(char));
int bytes_read = read(fd, buf, max_size*sizeof(char));
while(bytes_read > 0){
*nread+=bytes_read;
if(*nread >= max_size*sizeof(char)){
max_size*=2;
buf = realloc(buf, max_size*sizeof(char));
}
bytes_read = read(fd, buf, max_size*sizeof(char));
}
return buf;
}
==== EXPECT ==== ==== ACTUAL ====
{ {
// Tests the read_all() function to ensure that // Tests the read_all() function to ensure that
// it properly accumulates all data from an // it properly accumulates all data from an
// arbitrary input FD including allocating memory // arbitrary input FD including allocating memory
// for the data. // for the data.
int fd = open("test-data/quote.txt", O_RDONLY); int fd = open("test-data/quote.txt", O_RDONLY);
int bytes_read = -1; int bytes_read = -1;
char *actual_read = read_all(fd, &bytes_read); char *actual_read = read_all(fd, &bytes_read);
int result = close(fd); int result = close(fd);
printf("result: %d\n", result); printf("result: %d\n", result);
printf("bytes_read: %d\n", bytes_read); printf("bytes_read: %d\n", bytes_read);
actual_read[bytes_read] = '\0'; actual_read[bytes_read] = '\0';
printf("actual_read:\n" ); printf("actual_read:\n" );
printf("--------------------\n" ); printf("--------------------\n" );
printf("%s",actual_read); printf("%s",actual_read);
printf("--------------------\n" ); printf("--------------------\n" );
free(actual_read); free(actual_read);
} }
result: 0 result: 0
bytes_read: 125 bytes_read: 125
actual_read: actual_read:
-------------------- --------------------
Object-oriented programming is an exceptionally bad idea which could | could
only have originated in California. only have originated in California.
-- Edsger Dijkstra -- Edsger Dijkstra
-------------------- --------------------
ALERTS: ALERTS:
(
Somewhat bizzare...
I changed read_all() so it works more like expected: (full here)
char *read_all2(int fd, int *nread){ //nread tracks total bytes read
int max_size = 1;
*nread = 0;
char *buf = malloc(max_size*sizeof(char));
char *ptr = buf;
int bytes_read = 0;
//while(bytes_read > 0)
do
{
fprintf(stderr, "bytes_read=%d\n", bytes_read);
*nread += bytes_read;
ptr += bytes_read;
if(*nread >= max_size*1){
max_size *= 2;
fprintf(stderr, "realloc(buf=%08x, max_size=%d)\n", buf, max_size);
buf = realloc(buf, max_size*1);
}
} while((bytes_read = read(fd, ptr, max_size*1)) > 0);
return buf;
}
but when it realloc(buf, 16); it crashes.
$ gcc -o readynbuf2 readynbuf2.c ; ./readynbuf2
bytes_read=0
bytes_read=1
realloc(buf=016ba008, max_size=2)
bytes_read=2
realloc(buf=016ba008, max_size=4)
bytes_read=4
realloc(buf=016ba008, max_size=8)
bytes_read=8
realloc(buf=016ba008, max_size=16)
*** Error in `./readynbuf2': realloc(): invalid next size: 0x016ba008 ***
I'm out of ideas why would it happen.
Thanks guys, I managed to solve it, the key was passing &buf[*nread] into read().
char *read_all(int fd, int *nread){
int bytes_read = 0, size_mult = 1;
*nread = 0;
char * buf = malloc(1024);
bytes_read = read(fd, &buf[*nread], 256);
while(bytes_read > 0){
*nread += bytes_read;
if(*nread > size_mult*512){
size_mult*=2;
buf = realloc(buf, size_mult*1024);
}
bytes_read = read(fd, &buf[*nread], 256);
}
return buf;
}
Related
I can't seem to make partial write() work. It goes out of the memory and I don't know why.
int fd = open(path, O_RDONLY);
if(fd == -1) {error handling}
const size_t read_size = 100;
size_t size = read_size;
size_t offset = 0;
size_t res = 0;
char *buff = malloc(size+1);
int lines = 0;
int pos = 0;
while((res = read(fd, buff + offset, read_size)) > 0)
{
if(res == -1){error handling}
offset += res;
buff[offset] = '\0';
if (offset + read_size > size)
{
size *= 2;
buff = realloc(buff, size+1);
}
}
for(size_t i = 0;buff[i] != '\0'; i++) // counting the buff lines
{
if(buff[i] == '\n')
{
lines++;
}
}
size = read_size;
offset = 0;
res = 0;
if(lines < 10)
{
while((res = write(STDOUT_FILENO, buff+offset, read_size)) > 0)
{
offset += res;
}
}
buff[offset] = '\0';
else{another case where the position is found where the write() needs to start printing}
This is a part of a tail implementation in c. There is also another function which handles stdin and does the same thing (this one handles files).
This is what it might look like:
// Returns 0 on success.
// Returns -1 and sets errno on error.
int write_full(int fd, void *a_buf, size_t count) {
const char *buf = (char *)a_buf;
while ( count > 0 ) {
ssize_t chunk_size = write(fd, buf, count);
if ( chunk_size < 0 )
return -1;
buf += chunk_size;
count -= chunk_size;
}
return 0;
}
Testing is tricky. I've only been able to generate a partial write when using a non-blocking handle writing to a pipe with a blocked consumer.
But that results in error EAGAIN or EWOULDBLOCK so if we temporarily add code to immediately try again (which would be bad to do in practice), we can see the partial writes working.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Returns 0 on success.
// Returns -1 and sets errno on error.
int write_full(int fd, void *a_buf, size_t count) {
const char *buf = (char *)a_buf;
while ( count > 0 ) {
ssize_t chunk_size = write(fd, buf, count);
if ( chunk_size < 0 && ( errno == EAGAIN || errno == EWOULDBLOCK ) ) continue; // DEBUG
if ( chunk_size < 0 )
return -1;
fprintf(stderr, "Wrote %zd\n", chunk_size); // DEBUG
buf += chunk_size;
count -= chunk_size;
}
return 0;
}
int main(void) {
int fd = STDOUT_FILENO;
fcntl(fd, F_SETFL, O_NONBLOCK); // Make non-blocking
const size_t n = 100000;
char *buf = malloc(n);
if (!buf) {
perror("Can't allocate memory");
exit(1);
}
for (size_t i=n; i--; )
buf[i] = 'x';
if ( write_full(fd, buf, n) < 0 ) {
perror("Write error");
exit(1);
}
free(buf);
return 0;
}
$ gcc -Wall -Wextra -pedantic a.c -o a && ./a | perl -e'print while <>' >/dev/null
Wrote 65536
Wrote 8192
Wrote 8192
Wrote 16384
Wrote 1696
Perl takes longer to load than the C program allowing the 64 KiB pipe buffer to fill up. You can ensure this bad adding sleep 2; to the start of the Perl program.
Perl reads in 8 KiB chunks, and it takes longer to do so than it takes for the C program to write, so the C program is constantly running out of space in the pipe buffer.
I'm trying to do a client/server program using sockets, with protobuf messages, for school.
However one of the commands is(put) isn't working properly. It works with size, and returns 0, as it should but when I try put, it doesn't seem to work properly. I wrote some printf's in the code to try and check what's happening, and read_all doesn't seem right.
Here's the code.
int read_all (int socket, char *buf, int len){
int left;
int used;
char *prov;
prov = buf;
left = len;
printf("read all len: %d\n",len);
while (left > 0) {
printf("left : %d\n",left);
used = read(socket, prov, left);
printf("used: %d\n",used);
if ( used < 0) {
perror("readall err\n");
printf("errno here %d \n",errno);
return -1;
} else if (used == 0) break;
left -= used;
printf("left 2 : %d\n",left);
prov += used;
}
printf("read_all %d\n",len-left);
return (len - left);
}
When I use put , on the server terminal it shows :
read all len: 6
left: 6
and then it stops, that makes me think that read_all isn't working well, but I don't understand why. While on the client terminal it just shows that put wasn't successful.
Is read_all right or is something wrong.
Edit, read calls.
read_all for the size of msg buff:
read_all(client_socket, &rec, sizeof(rec) )
read_all for msg buff:
read_all(client_socket,(char *) rbuf, len )
rec is an int
len is an unsigned = ntohl(rec)
rbuf is a uint8_t = malloc(len)
edit 3:
write_all code:
int write_all(int socket, char *buf, int len){
int bufsize = len;
char *prov = buf;
while(bufsize > 0){
int res = write(socket, prov, bufsize);
if(res < 0){
perror("writeall err");
return -1;
}
prov += res;
bufsize -= res;
}
return len;
}
write calls:
sends size:
write_all(descr, &sen , sizeof(len))) != len)
send msg buf:
write_all(descritor,(char *) buf, len)) != len)
variable:
unsigned len = message_t__get_packed_size(msg)
uint8_t *buf = malloc(len)
unsigned sen = htoml(len)
I'm facing a very odd issue when trying to read a file in C.
I've parsed a file path via a command line argument, and gotten the size using the stat() function, and this works fine. However, after I read the file, all of my integers become arbitrarily large, and I cannot for the life of me figure out why!
Here is the relevant code from my main function:
int main( int argc, char *argv[] ) {
char *filepath = argv[1];
startaddress = strtol(argv[2], &endptra, 16);
endaddress = strtol(argv[3], &endptrb, 16);
int filesize = getfilesize(filepath);
printf("Filesize is %d\n", filesize);
unsigned char *mem = malloc( filesize );
printf("Filesize here is %d\n", filesize);
int size2 = filesize;
int test3 = 18;
printf("Size 2 set to %d\n", size2);
printf("Test 3 set to %d\n", test3);
// READ FILE
loadimage(filepath, &mem, filesize);
printf("Right after load image, file size is %d\n", filesize);
printf("%s\n", strerror(errno));
printf("Filesize is %d\n", filesize);
printf("size2: %d\n", size2);
printf("test3: %d\n", test3);
exit(0);
}
"getfilesize" is a relatively simple function that appears to work well:
int getfilesize(char *path) {
struct stat sbuffer;
int filesize = 0;
filesize = stat(path, &sbuffer);
if (filesize == -1) {
return 0;
} else {
return sbuffer.st_size;
}
}
Here is the loadimage function:
int loadimage(char *path, unsigned char *mem[], int size) {
int fdin, retval;
unsigned char buf[2048];
int nread;
printf("Path is: %s\n", path);
printf("Size is: %d\n", size);
fdin = open(path, O_RDONLY);
printf("fdin: %d\n", fdin);
if(fdin == -1) {
die(strerror( errno ));
}
int count = 0;
nread = read(fdin, buf, 2048);
for(; count < nread; count++) {
mem[count] = &buf[count];
}
if(nread == -1) {
die(strerror( errno ));
}
retval = close(fdin);
printf("Size is now %d\n", size);
return 1;
}
And this is the output of the result:
Filesize is 39
Filesize here is 39
Size 2 set to 39
Test 3 set to 18
Path is: test_file.txt
Size is: 39
fdin: 3
Size is now 39
Right after load image, file size is 32765
Success
Filesize is 32765
size2: 1418855892
test3: 32765
This is baffling to me and I cannot figure it out! It's confusing that even integers that I don't pass to the function are being modified as well. I'm assuming there's some sort of memory overflow happening somewhere, but I'm not used to working in the file system in C.
Thanks!
It seems to me that the problem is here:
int loadimage(char *path, unsigned char *mem[], int size) {
The mem argument should be just a pointer, or just an array, but not both. Aside from that, you're doing the same with the buf local variable, you're dereferencing it more than once when you use it (however that case might be harmless, I'm not sure). loadimage() should be:
int loadimage(char *path, unsigned char *mem, int size) {
int fdin, retval;
unsigned char buf[2048];
int nread;
printf("Path is: %s\n", path);
printf("Size is: %d\n", size);
fdin = open(path, O_RDONLY);
printf("fdin: %d\n", fdin);
if(fdin == -1) {
die(strerror( errno ));
}
int count = 0;
nread = read(fdin, buf, 2048);
for(; count < nread; count++) {
mem[count] = buf[count]; //no need for derefencing
}
if(nread == -1) {
die(strerror( errno ));
}
retval = close(fdin);
printf("Size is now %d\n", size);
return 1;
}
then, when calling it, do not dereference mem:
loadimage(filepath, mem, filesize);
Sorry, I don't have time so I haven't compiled it, but you get the idea, most probably that's the problem.
I'm trying to get the source code of my website using c, I'm able to connect and everything but when I implement the recv() code, it only receives the last few bytes of the source code. I'd like to dynamically allocate space for the buffer to receive more using the C functions malloc and realloc.
This is the code I have so far:
char *buffer = NULL;
unsigned int i = 0;
unsigned long LEN = 200;
unsigned long cur_size = 0;
buffer = (char*)malloc(sizeof(char)*LEN);
do
{
if( status >= LEN )
{
cur_size += status;
buffer = (char*)realloc(buffer, cur_size);
}
status = recv(cSocket, buffer, LEN, 0);
if( status == 0 )
{
printf("Bye\n");
}
else if( status > 0 )
{
printf("%d\n", status);
}
else
{
printf("socket error=%d\n", WSAGetLastError());
break;
}
}while( status > 0 );
printf("%s\n", buffer);
It still doesn't print the whole source code. How should I go about this?
Pseudocode:
buffer = 'len chars';
loop:
if( status >= buffer ) buffer = 'resize to status chars';
status = recv(sock, buffer, len, 0);
end loop
As you resize the buffer in advance this needs to be reflected by its size. Which currently is not the case.
To fix this you could, for example, initialise cur_size with LEN by changing
unsigned long cur_size = 0;
to
unsigned long cur_size = LEN;
Assuming the fix above, you want to append to the buffer and not overwrite it with every call to recv().
To do so change this line
status = recv(cSocket, buffer, LEN, 0);
to be
status = recv(cSocket, buffer + cur_size - LEN, LEN, 0);
A more straight forward approach would be to not track the size of the buffer, but the number of bytes received and just always increase the buffer by a constant size.
Also the two calls to allocate memory can be replaced by one:
char *buffer = NULL;
unsigned long LEN = 200;
unsigned long bytes_received = 0;
unsigned long cur_size = 0;
int status = 0;
do
{
if (bytes_received >= cur_size)
{
char * tmp;
cur_size += LEN;
tmp = realloc(buffer, cur_size);
if (NULL == tmp)
{
fprintf(stderr, "realloc error=%d\n", WSAGetLastError());
break;
}
buffer = tmp;
}
status = recv(cSocket, buffer + bytes_received, LEN, 0);
if (status == 0)
{
printf("Bye\n");
}
else if (status > 0)
{
bytes_received += status;
printf("%d\n", status);
}
else /* < 0 */
{
fprintf(stderr, "socket error=%d\n", WSAGetLastError());
}
} while (status > 0);
printf("%s\n", buffer);
Well, after a bit of research, I came across this website and finally found what I was looking for.
Binary tides
Although it uses linux's fcntl, the windows equivalent is ioctlsocket which is used to set the socket's non-blocking mode.
To see the exact function, visit the website. I modified the version and set my socket to blocking mode.
int total_recv(SOCKET s)
{
int size_recv = 0, total_size = 0, block = 00;
char chunk[BUFLEN];
ioctlsocket(s, FIONBIO, (unsigned long*)&block); // set mode to block
// not necessary but clarification of function, mode is block by
// default
while( 1 )
{
memset(chunk, 0, BUFLEN);
if( ( size_recv = recv(s, chunk, BUFLEN, 0) ) == SOCKET_ERROR )
{
printf("Error receiving\n");
}
else if( size_recv == 0 )
{
break;
}
else
{
total_size += size_recv;
// i used file since console wouldn't show full source code
FILE *fp = NULL;
fp = fopen("source.txt", "a");
fprintf(fp, chunk);
fclose(fp);
}
}
return total_size;
}
So I programmed a multi threaded web server, here is one function from the program. This function takes output file descriptor (fd), content type, pointer to data to be served (*buf) and size of the data (numbytes). It always gets stuck at 5775 bytes! I've tried using write() instead of send(), but no avail! I tried to send whole buf at a time, and even tried to transfer it in chunks, but wget shows that it gets stck at 5775 bytes! Here is the code:
int return_result(int fd, char *content_type, char *buf, int numbytes)
{
char out_buf[BUF_SIZE], numb[6];
int buf_len, total = 0, buf_size;
long int i = 0;
sprintf(numb, "%d", numbytes);
strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: ");
strcat(out_buf, content_type);
strcat(out_buf, "\nContent-Length: ");
strcat(out_buf, numb);
strcat(out_buf, "\nConnection: Close\n \n");
printf("\nSending HTTP Header\n %d bytes sent!",
send(fd, out_buf, strlen(out_buf), 0));
char *start = NULL, *str = NULL, *temp = NULL;
start = buf;
printf("\n Start Pointer Val = %ld", &start);
while (start != NULL) {
printf("\n While Loop");
if (i + 2048 * sizeof(char) < numbytes) {
printf("\n If 1");
str = (char *)malloc(sizeof(char) * 2048);
memcpy(str, start, sizeof(char) * 2048);
i = i + 2048 * sizeof(char);
buf_size = send(fd, str, 2048, 0);
free(str);
printf("\n Sent %d bytes total : %d", buf_size, total =
total + buf_size);
temp = start + sizeof(char) * 2048;
start = temp;
} else {
i = numbytes - i * sizeof(char);
if (i > 0) {
printf("\n If 2");
printf("\n Value of i %d", i);
str = (char *)malloc(sizeof(char) * i);
memcpy(str, start, sizeof(char) * i);
printf("Total bytes finally sent:%d", total =
total + send(fd, str, i, 0));
if (total == numbytes) {
printf("\nTransfer Complete!");
}
free(str);
}
start = NULL;
}
}
printf("out of loop!");
return 0;
}
I'd like to suggest replacing your code with the following writen() function from Advanced Programming in the Unix Environment, 2nd edition:
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
This code is already debugged and known working, and further allows write(2) to write PIPE_BUF bytes at a go for better speed when things are working well.
send(2) should block if it cannot send all the data you have requested, though. I think more interesting would be debugging the version with plain send(2) without any of the surrounding efforts to break things into blocks.
Better than both write(2) and send(2) would be sendfile(2) -- open the file, pass the descriptor and socket to sendfile(2), and let the kernel handle it all for you, using zero-copy mechanisms if possible.
One last point: HTTP uses CRLF, not plain carriage returns. Each \n should be replaced with \r\n.
Try something like this (printf() statements omitted for clarity):
int send_buf(in fd, void *buf, int numbytes)
{
char *start = (char*) buf;
while (numbytes > 0)
{
int sent = send(fd, start, numbytes, 0);
if (sent <= 0)
{
if ((sent == -1) && (errno == EAGAIN))
{
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1)
continue;
}
return -1;
}
start += sent;
numbytes -= sent;
}
return 0;
}
int return_result(int fd, char *content_type, void *buf, int numbytes)
{
char out_buf[BUF_SIZE],
int len = sprintf(out_buf,
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Close\r\n"
"\r\n",
content_type,
numb);
if (send_buf(fd, out_buf, len) != 0)
return -1;
if (send_buf(fd, buf, numbytes) != 0)
return -1;
return 0;
}