0

I have a python tcp server that accepts connections and generates a random string of length between (0,1M) characters, on the other side I have a c client that needs to listen on that socket and read the string and convert it into a single char of the same length as the string returned by the server


int receiver(int soc_desc, char * buffer)
{
    char *arr = (char *)malloc(sizeof(char));
    unsigned int received , total_received;
    while (1)
    {
        memset(arr, 0, MAX); // clear the buffer
        if ( received = recv(soc_desc, arr , MAX, 0) < 0)
        {
            break;
        }
        else
        {
            total_received += received;
        }
    }
    printf("%s\n",arr);
    return received; 
}
// soc_desc is the socket descriptor 
// buffer is the buffer that will hold the final output 

The only way that I can think of is using malloc to read chunks of the data returned from the server but I am having bad time trying to figure it out and I need to convert the array of char pointers into a single char when the client is done receiving data from the server

epic_rain
  • 1
  • 3
  • 1
    _...read the string and convert it into a single char..._ This makes no sense. A single `char` is just that, a single `char`. A string is sequence of `char`s. You can't convert a sequence of `char`s to a single one. `char *arr = (char *)malloc(sizeof(char));` only allocates room for one byte. The moment you write more data than that (`memset(arr, 0, MAX)` if `MAX>1`) you invoke [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). Instead, you need something like `char* arr = malloc(MAX+1)` (`sizeof(char) is always 1). – yano Apr 13 '22 at 17:17
  • @yano my bad i meant converting a it into a single array of chars – epic_rain Apr 13 '22 at 17:26
  • you also have not initialized `total_received`, so `total_received += received` is undefined behavior (altho it's not being used anyway). Furthermore, you only break on an error condition. `recv` returns 0 for an orderly shutdown. – yano Apr 13 '22 at 17:42
  • In general, this architecture will face problems. TCP is byte stream, meaning data can trickle in at arbitrarily-sized chunks. You're writing this data to `arr` every time, which will overwrite whatever was received before with each read. You need to write to something like `arr+total_received`. Furthermore, you need to process this data in some way. You only break out of the loop on error (`recv` returns -1). What if your buffer is exceeded before an error? What if there is never an error? You should at least `break` for an orderly shutdown too (`recv` returns 0). – yano Apr 13 '22 at 17:49
  • @yano this is exactly why i am asking, I know that i have to use `arr+total_received` but i don't know how to do so I am hoping someone could list the steps i don't need the full code just the proper steps to do this – epic_rain Apr 13 '22 at 17:53
  • @epic_rain Have you tried to work out the `arr+total_received` thing? Slow down and think: If the first `recv` call receives let's say 10 bytes, what do you want the computer to do after that? If the next one receives 30 bytes, what do you want the computer to do after that? Is there a pattern? Then: What happens at the end? – user253751 Apr 13 '22 at 18:30

2 Answers2

0

Reassembling network data, particularly from TCP, can get tricky. The following code is untested and surely doesn't account for all contingencies, but hopefully is down the right path of what you need to do.

ssize_t receiver(int soc_desc, char * buffer)
{
    // Whats the buffer argument used for?
    // what you had before only allocated space for 1 char. That's not what you want
    // This allocates for MAX+1 chars (I'm doing +1 for a NUL terminator)
    char *arr = malloc(MAX+1);
    // if MAX is small enough, you could do
    // char arr[MAX+1];

    // 0 buffer. You could use calloc instead of malloc + memset
    memset(arr, 0, MAX+1);
    // initialize total_received to 0
    ssize_t received , total_received = 0;
    size_t spaceLeftInBuf = MAX;
    while (1)
    {
        // don't memset here, you'll erase the data you received last iteration

        // write data to arr+total_receieved. This way you won't overwrite what
        // you received the last iteration
        received = recv(soc_desc, arr+total_received, spaceLeftInBuf, 0);
        if (received < 0)
        {
            // there was an error
            perror("recv failed: ");
            // do something with the data already received? Ok, break and
            // print what we've got
            break;
        }
        else if (received == 0)
        {
            // socket closed gracefully, suppose we can break again and print
            // what we've got
            break;
        else
        {
            // update counters
            total_received += received;
            spaceLeftInBuf -= received;
            // is our buffer full? This may not be the right check, you need to
            // decide when to process the data
            // total_received better not ever be > MAX...
            if (total_received >= MAX)
            {
                // "process" the data by printing it
                printf("%s\n", arr);
                
                // reset
                total_received = 0;
                spaceLeftInBuf = MAX;
                // not particularly necessary to reset this to all 0s, but should
                // make sure printing goes smoothly if we break out of this loop
                memset(arr, 0, MAX);  // arr[MAX] should already be '\0' from above
            }
            
        }
    }
    printf("%s\n",arr);
    return received; 
}

See Do I cast the result of malloc?

yano
  • 4,827
  • 2
  • 23
  • 35
  • I don't think you get my point, I want to put the whole string received from the server in a char array and be able to use after I print it – epic_rain Apr 13 '22 at 18:33
  • @epic_rain If `MAX` is one million, this does very close to that. `break` instead of printing and `memset`ing. But I'd also encourage you to think more broadly. Memory is cheap and easy these days, and 1M bytes isn't that much. But what if in the future you expect a billion? terabytes? You need to think about how to deal with data in chunks. I doubt you'll find many programs with receive buffers of 1M bytes. You're not going to get a TCP packet with 1M bytes, you need to read and accumulate as they come in. – yano Apr 13 '22 at 18:42
0

I found a way to do it but this is not tested enough and for sure will cause memory issues

    char *arr = malloc(sizeof(char));
    char tmp_buff[MAX];
    memset(arr,0,MAX);
    while (recv(soc_desc, tmp_buff , MAX, 0) > 0 )
    {
        strcat(arr , tmp_buff);
        printf("Size : %ld  arr : %s\n",strlen(tmp_buff),tmp_buff);
    }
epic_rain
  • 1
  • 3
  • 1
    Umm...no. 'char *arr = malloc(sizeof(char));' allocates only one byte:(( – Martin James Apr 13 '22 at 22:35
  • @MartinJames yes but when i use `printf` it outputs all of the string, is it overwriting the heap the same way as stack based BOF – epic_rain Apr 14 '22 at 05:32
  • Tell me what you think about: How does `strcat` know how much data to copy? – user253751 Apr 14 '22 at 09:35
  • @user253751 by using `\0`? – epic_rain Apr 14 '22 at 15:14
  • @epic_rain yeah and is there a `\0`? Who put it there? – user253751 Apr 14 '22 at 15:14
  • @user253751 `memset()`? – epic_rain Apr 14 '22 at 15:32
  • @epic_rain well, you don't call memset every time, do you? what if it receives "abcd" and then "ef", your arr will have "abcdefcd" at the end – user253751 Apr 14 '22 at 15:38
  • @user253751 can you elaborate more please – epic_rain Apr 14 '22 at 15:40
  • What does your code do if it receives "abcd" the first time, and then "ef" the next time? – user253751 Apr 14 '22 at 15:42
  • if you're trying to stuff a string into a single `char` and it's working, you're simply getting "lucky". [Writing to memory beyond what's allocated is undefined behavior](https://www.wikiod.com/w/C_Undefined_behavior#Accessing_memory_beyond_allocated_chunk), which first happens here with `memset(arr,0,MAX);` assuming `MAX>1`. But I have serious doubts you're writing 1M characters to `arr` without segfaulting. – yano Apr 14 '22 at 15:42