Elkip
Elkip

Reputation: 70

Wrong int values using fwrite in C

I'm trying to create a keymanager for a simple UDP client-server application in C. I'm running into problems writing the keys to a text file. I'm currently storing structs into a txt file, but my output was the same when I used ints.

// ...
// if the principal is requesting to registering, store it in a file
if (req.request_type == regista) {
    printf("Writing key to file...\n");
    pFile = fopen("crypt.txt", "ab");
    fwrite(&req, sizeof(struct P2Key), 1, pFile);
    printf("Written: %u %i ", req.principal_id, req.public_key);
    fclose(pFile);
    printf("Done\n");
}

// if pincipal is requesting key
if (req.request_type == request_key) {
    pFile = fopen("crypt.txt", "rb");
    printf("Key requested for: %u %i ", req.principal_id, req.public_key);
    printf("Searching for Requested Key\n");

    while (fread(&res, sizeof(struct P2Key), 1, pFile) == 1) {
        printf("Read: %u %i\n", res.principal_id, res.public_key);
        printf("Line Number: %ld\n", ftell(pFile));

        // if this is the client requested, send the key
        if (req.principal_id == res.principal_id) {
            send_key.principal_id = req.principal_id;
            send_key.public_key = req.public_key;
            printf("Sending Key...\n");
            // ...
        }

Here's the structure being written to the file

typedef struct P2Key {
    enum {
        regista, request_key
    } request_type; /* same size as an unsigned int */
    unsigned int principal_id; /* client or server identifier */
    int public_key; /*  public key */
} P2Key;

The output I'm getting when I attempt to read the file:

 Writing key to file...
 Written: 2 7 Done
 ...
 Writing key to file...
 Written: 1 7 Done
 Key requested for: 2 7 Searching for Requested Key
 Read: 512 1792
 Line Number: 12
 Read: 256 1792
 Line Number: 24
 End of file reached.

I've been at this for hours, any and all help is appreciated.

Upvotes: 0

Views: 655

Answers (3)

Craig Estey
Craig Estey

Reputation: 33641

Your program appears to work here. Note that I'm using linux, but since you're using b in your fopen args, even if you are using WinX, it should still be the same.

I had to synthesize some missing elements to get a full program. I passed down req from main, so the [new] action function had to use req->* instead of req.*, but, otherwise it should be the same as your code:


#include <stdio.h>

typedef struct P2Key {
    enum {
        regista, request_key
    } request_type;                     /* same size as an unsigned int */
    unsigned int principal_id;          /* client or server identifier */
    int public_key;                     /* public key */
} P2Key;

void
action(P2Key *req)
{
    FILE *pFile;
    P2Key res;
    P2Key send_key;

    printf("request_type=%d regista=%d request_key=%d\n",
        req->request_type,regista,request_key);

// ...
// if the principal is requesting to registering, store it in a file
    if (req->request_type == regista) {
        printf("Writing key to file...\n");
        pFile = fopen("crypt.txt", "ab");
        fwrite(req, sizeof(struct P2Key), 1, pFile);
        printf("Written: %u %i ", req->principal_id, req->public_key);
        fclose(pFile);
        printf("Done\n");
    }

// if pincipal is requesting key
    if (req->request_type == request_key) {
        pFile = fopen("crypt.txt", "rb");
        printf("Key requested for: %u %i ", req->principal_id, req->public_key);
        printf("Searching for Requested Key\n");

        while (fread(&res, sizeof(struct P2Key), 1, pFile) == 1) {
            printf("Read: %u %i\n", res.principal_id, res.public_key);
            printf("Line Number: %ld\n", ftell(pFile));

            // if this is the client requested, send the key
            if (req->principal_id == res.principal_id) {
                send_key.principal_id = req->principal_id;
                send_key.public_key = req->public_key;
                printf("Sending Key...\n");
                // ...
            }
        }

        fclose(pFile);
    }
}

int
main(void)
{
    P2Key req;

    while (1) {
        printf("Cmd (regista=%d request_key=%d): ",regista,request_key);
        fflush(stdout);
        scanf("%d",&req.request_type);
        scanf("%u %d",&req.principal_id,&req.public_key);

        action(&req);
    }
}

Here is the output I got:

Cmd (regista=0 request_key=1): 0 5 6
request_type=0 regista=0 request_key=1
Writing key to file...
Written: 5 6 Done
Cmd (regista=0 request_key=1): 0 8 9
request_type=0 regista=0 request_key=1
Writing key to file...
Written: 8 9 Done
Cmd (regista=0 request_key=1): 1 8 9
request_type=1 regista=0 request_key=1
Key requested for: 8 9 Searching for Requested Key
Read: 1 2
Line Number: 12
Read: 5 6
Line Number: 24
Read: 8 9
Line Number: 36
Sending Key...
Cmd (regista=0 request_key=1): ^C

Upvotes: 1

paxdiablo
paxdiablo

Reputation: 882716

You need to check your file, it appears that there may be an extra byte (or bytes) in there for some reason, which is causing you to read the structures at an incorrect offset.

I base this on the following:

  1. You simply append a structure to the existing file, regardless of how many bytes are currently in the file; and
  2. The values you're reading are exact multiples of 256. More importantly, they are 256 multiplied by 1, 2 and 7 (256, 512 and 1792).

An easy way to check is to simply delete the file (or empty it) and run your program again. Provided you don't have anything else writing to the file, it should work okay.


There are actually ways you can catch this sort of issue, should you be so inclined. They include, amongst others:

  • checking that the file size is an exact multiple of the structure size when opening.
  • adding an indicator field (or fields) to your structure for checking each and every item, such as a field at the start called check_dead and another at the end called check_beef, which you always set to 0xdead and 0xbeef on writing a record and always check when reading one.

Upvotes: 0

softweyr
softweyr

Reputation: 38

You are writing in append mode; have you re-created the file since you switched to writing the structs? You don't show or even test the number of bytes written, so you have no idea if the write succeeded or not.

My first suspect is that you have been appending to this file over time, and it has old data near the beginning that doesn't line up with your structure. I suggest first emptying the file. On UNIX/Linux, you can do this with "> crypt.txt" from the shell.

Now on to debugging the code. Capture the return value of all reads and writes, they return size_t, so you can at least look at the return value in the debugger. You may want to log an error, etc., if you didn't write the number of bytes you expected to -- the sizeof the struct. Step over the write and close in your debugger, then look at the file with a hex dump or similar utility. Do the values in the file look like what you expect? Does the file contain the same number of bytes as the sizeof your struct?

Then proceed to the read code. Watch the code load each value from the file with fread. Watch the return value you get from fread -- is it the sizeof your struct? Look at the values in the struct after the read completes. Does it look like it read what you wrote?

Upvotes: 1

Related Questions