breaking up a char array into a structure - c

In case someone gets this issue in the future I'll leave this up.
*Note This approach wasn't going to work when going from C client to C server. This would have only worked with the Java client to C server. So I had to abandoned this approach.
Ok, I've been fighting with C for too long now. I'm passing some info with UDP from a java client to a C server. I can get the info there, but I'm not sure how to break apart the message to store into a struct like so
struct __attribute__((__packed__)) clientMessage
{
short tml;
short rid;
char op;
char message[MAXBUFLEN-5];
};
I recieve the message like this
Where test is a char test[MAXBUFLEN-5];
if ((numbytes = recvfrom(sockfd, test, MAXBUFLEN-1, 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);}
So, I need to take the message "7 2 1Yo" (two 2 byte shorts and a char followed by an unknown message length) and store it into it's appropriate parts in the struct. The message gets sent correctly, I just can't break it into the bits of info that I need. I'm currently attempting
memcpy(&cm.rid, &test, 2);
memcpy(&cm.tml, &test[1], 2);
memcpy(&cm.op, &test[4], 1);
memcpy(&cm.message, &test[5], MAXBUFLEN-5);
But my resutls end up being
Message: Yo
OP: 1Yo
RID: 7 1Yo
TML: 2 7 1Yo
it Should be
Message: Yo
OP: 1
RID: 2
TML: 7
I successfully get the message, but nothing else. I'm relatively new to C so forgive my ignorance. I'm guessing this is really easy but idk.

The first line should be memcpy(&cm.rid, &test[0], 2);, for the address is supposed to be the address of the first byte.
And the rest:
memcpy(&cm.tml, &test[2], 2); // you want to get the third and forth byte, begin with index 2.
memcpy(&cm.op, &test[4], 1); // the fifth byte, begin with index 4.
memcpy(&cm.message, &test[5], MAXBUFLEN-5); // the rest bytes.

If you are using a binary protocol, it would be better to use e.g. int16_t instead of short in the declaration of clientMessage because the size of short is not specified within the c standard.
To access the message stored in test you could do simply something like this:
struct clientMessage *cm = (struct clientMessage *) test;
You should also notice, that the endianness of network protocols is different from the endianness of a x86 and amd64 architecture, so instead of 42 you might recieve 10752.
To fix this you can use the ntohs() (Network TO Host Short) function to acces the tm and rid.

Use sscanf() for the first part and memcpy() for the text.
const char *message = "7 2 1Yo";
struct clientMessag cM;
int offset = 0;
int n;
n = sscanf(message, "%hd %hd %c%n", &cM.tml, &cM.rid, &cM.op, &offset);
size_t SuffixLength = strlen(&message[offset]);
if ((n != 3) || (SuffixLength >= sizeof(cM.message))) {
exit(1); // handle syntax error;
}
memcpy(cM.message, &message[offset], SuffixLength + 1);
// Additional field checks like IsOKOpCode(cM.op)
I avoided %s as it does not store whitespace into your message.
Error checking is always good to do. Recommend additional field checks.
BTW: its not clear if the cM.op field should be treat as text or a number. Does the OP want a 1 or '1' stored? The above assume text. Alternatively one could use "%hd%hd%hhd%n".

Since test contains the string "7 2 1Yo", you have to parse the string into the values you want. You can do this with sscanf():
char fmt[256];
snprintf(fmt, sizeof(fmt), "%%hd %%hd %%c%%%ds", MAXBUFLEN-5-1);
sscanf(test, fmt, &cm.rid, &cm.tml, &cm.op, cm.message);
In this code fragment above, we create a format string for the parsing. This is necessary to prevent sscanf() from scanning past the end of the message in the case that it is not \0 terminated. The result of the snprintf() call with a MAXBUFLEN of 128 is:
"%hd %hd %c%122s"
Which tells sscanf() to scan for two short decimal numbers, a char, and a string no longer than 122 characters.

Related

Convert blob of data to align with structure

char buf[512] = { 0 };
int ret = recv(gSock, buf, 512, 0);
typedef struct _STRUCT {
int package;
int version;
char string[512];
} STRUCT, *PSTRUCT;
PSTRUCT ok;
ok = (PSTRUCT)buf;
I am trying to accept a buffer from a socket (Code not here, but it is working). It accepts it and places it into buf. I then want to cast this buf as a structure STRUCT. I want the first 4 bytes to go into the first member, second 4 bytes into the second member, and then the remaining data to go into the last member. However this is not working like I expected. I am getting weird large numbers that are not what I am receiving.
I entered 1111111111 (10) and the results I got back were;
package = 825307441
version = 825307441
string = 11\n
I did a decimal to hex conversion on the package number and it comes back as '31313131', which is my first 4 1's. So I am not to sure why it is going from integer, to hex back to a integer. I want just exactly what sends to go into the structure.
You have to review the following functions:
htonl, htons, ntohl, ntohs - convert values between host and network byte order.

Two times send and recv are not working C

I was running into a problem i couldnt really solve so I restarted.
I had a problem with Data encapsulation or more specific with no encapsulation. So after I figured out, that encapsulation is useful, I started rewriting the code.
Now I run into a different Problem. Somehow my send and recv calls are not working as I want them to be.
Here is the part where I send:
char to_send[] = "hello. I am the Data.";
// get size of data
int len = strlen(to_send);
char slen[len];
sprintf(slen,"%d",len);
printf("%s\n",slen);
// send size of data
if(send(comm_fd,slen,len,0)<0){perror("Error on send"); exit(1);}
// send data
if(send(comm_fd,to_send,len,0)<0){perror("Error on send"); exit(1);}
And here Part where I recv:
// getting size of bytes to recv
char buf[1000];
bzero(buf,1000);
int rec = recv(comm_fd, buf, 100,0);
printf("rec\n: %i",rec);
printf("buf\n: %s\n", buf);
int buffsize;
buffsize = atoi(buf);
bzero(buf,1000);
printf("buffsize: %i\n",buffsize);
// recv the bytes
bzero(buf,1000);
rec = recv(comm_fd, buf, buffsize,0);
printf("rec\n: %i",rec);
printf("%s",buf);
So my problem now is: I can recv the size of the next Data and print it. But the Data itself is not showing up.
Can someone help me? I think I'm doing major things wrong (I'm new to C and to Network programming)
Thanks in advance
Two things with that first send call:
if(send(comm_fd,slen,len,0)<0){perror("Error on send"); exit(1);}
Here you send len number of bytes, but len is the length of to_send and not the length of slen. You will most likely send data from outside the initialized parts of slen which leads to undefined behavior
The second problem is that you send the length of to_send as a variable-length string, so the received doesn't actually know how much to receive. In your case you could actually (and probably do) receive the length and the string in a single recv call. At least if you're using TCP (streaming) sockets.
Both of these problems can be solved by making slen a fixed-size array, big enough to hold the largest numbers you can think of (ten digits is usually enough), and then send this fixed-length array using sizeof slen .
Perhaps something like this:
// Ten digits, plus string terminator
char slen[10 + 1];
// Prefix length with zeroes, and don't overflow the buffer
snprintf(slen, sizeof(slen), "%010d", strlen(to_send));
// Send the whole array, including terminator
send(comm_fd, slen, sizeof slen, 0);
Then on the receiving side, you could do
// Ten digits, plus string terminator
char slen[10 + 1];
// Receive the whole string, including terminator
recv(comm_fd, slen, sizeof(slen), 0);
// Convert to a number
size_t len = strtoul(slen, NULL, 10);
// Now receive `len` bytes
Note that I have no error checking, which you should have.

sending avro(avro c) encoded data through a socket

sending avro(avro c) encoded data through a socket
I'm trying to send an avro encoded data through a socket after converting it to byte array(using memcpy).What I have done is as given below
/client side : client.c/
avro_datum_t t_msg = avro_record(temp_schema);
avro_record_set(t_msg, "TIME", time_datum) // encoded like this
...
...
unsigned char *temp=(unsigned char *) malloc(sizeof(t_msg));
memcpy(temp,&t_msg,sizeof(t_msg));//converting to byte array
sendto(sock, &temp, strlen(temp), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));//send to socket
/server side :server.c/
avro_datum_t time_datum;
..
..
unsigned char *recv_data=(unsigned char *) malloc(sizeof(t_msg));
avro_datum_t t_msg;
bytes_read = recvfrom(sock,recv_data,1024,0,(struct sockaddr *)&client_addr, &addr_len);
memcpy(bgpmsg1,recv_data,sizeof(recv_data));
if (avro_record_get(t_msg, "TIME", &time_datum) == 0) {
printf("\nHi...0\n");
avro_string_get(time_datum, &p);
fprintf(stdout, "TIME: %s ", p)
}
//trying to decode and print message like this
but it's not getting anything...when I try to decode and print in clent.c itself it is getting printed.But after sending through socket it is not able to retrieve..
The data pointed to by temp is not actually a string, and so you can't use string functions like strlen with it.
Think about what happens if one byte in the middle is zero (i.e. the string terminator '\0'), the functions look for that to know when a string ends, and so will stop their processing there.
On the receiving side you have another problem, and is your use of sizeof(recv_data), which will always be 4 or 8 bytes depending on if you're on a 32 or 64 bit platform. The sizeof operator is a compile-time only operator, and for pointers it returns the size of the pointer and not what it points to.
On the receiving side you have a potentially even worse problem, which is that you tell recvfrom to receive up to 1024 bytes, instead of e.g. sizeof(t_msg).This might cause recvfrom to write beyond the allocated data if sizeof(t_msg) is less than 1024.

C - Loading from file with fgets and sscanf

In a function of my program I am trying to load data from a file into this struct array:
/* database struct */
typedef struct node {
char name[MAX];
char address[MAX];
long int number;
}record_type;
record_type record[100];
The function is as follows:
/* load database from disk */
void load_database() {
char line[128];
/* Set up database */
database = fopen("database.txt", "r+w+a+");
if(database == NULL) {
printf("\n\tWARNING: No database found.");
exit(1);
}
/* Get database file from disk */
while(fgets(line, sizeof(line), database) != NULL) {
sscanf(line, "%s %s %lu", record[rec_num].name,
record[rec_num].address, &record[rec_num].number);
/* keeps track of array size */
rec_num++;
}
}
The issue I am having is inconsistencies with sscanf. If I include first and last name I cannot put a space between them or it places the first name in name[] and the last name in address[].
Here is a sample of the data I am trying to input from:
1. Name: james manes Address: 220 test addr Number: 5558889999
I need to get the "james manes" into the name[] field, 220 test addr into the address[] field and 5558889999 into the number field of the struct. Is this possible at all?
Is there a more efficient way of managing this type of input?
scanf("%s"... parses a whitespace delimited string in your input, so if you have spaces in the strings you want to parse, it won't work.
While you could use regexes to get what you want, since you use fixed strings as your markers, you could instead use strstr to pull out your strings:
while(fgets(line, sizeof(line), database) != NULL) {
char *Name = strstr(line, "Name:");
char *Address = strstr(line, "Address:");
char *Number = strstr(line, "Number:");
if (Name && Address && Number) {
Name += strlen("Name:");
*Address = '\0';
Address += strlen("Address");
*Number = '\0';
Number += strlen("Number:");
strcpy(record[rec_num].name, Name);
strcpy(record[rec_num].address, Address);
sscanf(Number, "%lu", &record[rec_num].number);
rec_num++; } }
Note that this will also pull in all the whitespace around the name and address -- you can trim off leading and trailing whitespace if you want it cleaner.
First of all, you might want to take another look at http://www.cplusplus.com/reference/cstdio/fgets/. Here you will see that the str argument is a pointer to a buffer (which you provided correctly) and then num is the amount of bytes you want to read max, which you did not provide correctly.
The problem with the amount of bytes passed to fgets in you piece of code is the incorrect use of the sizeof operator. The sizeof operator 'returns', as you probably know, the size of the give type. The type you are passing to sizeof is a pointer type (since an array in C for 99% the same as a pointer). The size of a pointer depends on the system you are running on (32 bits on Intel x86, 64bits on AMD64, 16bits on ATmega AVR, etc). So lets asume that you have a 64 bit machine, you will allow fgets to 'get' 64 bits (i.e. 8 bytes) of data, which is not what you want. What would be the correct statement then?
while(fgets(line, sizeof(*line)*128, database) != NULL) {
...
What I'm doing here is dereferencing the char pointer to a char and multipling by the size of this array.
Then, secondly, your question about if this is possible: yes it is. I would like to ask a question myself now too. Is it necessary to do this in C (i.e. platform does not support anything different, learning purposes, etc) or can you also implement this in C#, Java, Python. If so I highly suggest you do.
And last but not least you are asking us about the usefulness of your code. That answer is really simple: no. Not in its current or fixed state. The problem you are having and much more complicated are fixed using a 'real' database (like MySQL) + its API.

Parsing string to long in C

I've written a server in python and the client in C.
The python server send i.e. "1 1000\n" to the C client.
The C function that recieves this string should parse it into two long int's.
void receive_job() {
char tmp_buffer[256];
long start, stop;
recv(sock, tmp_buffer, 255, 0);
/*
here I wonder how I can parse tmp_buffer to set start and stop values.
*/
}
I am not very proficient in C, so I would appreciate a comment on this.
Have a look at strtol() or strtoul(), depending on your concrete data type:
char end;
long x = strtol(tmp_buffer,&end,10);
long y = strtol(end+1,NULL,10);
Use strtol.
char data[] = "1 1000\n";
char *next, *err;
long a = strtol(data, &next, 10);
long b = strtol(next, &err, 10);
Look up sscanf manual page - but check the return value!
It is better to clear the tmp_buffer using memset before calling recv or at least to add a null byte in it. Otherwise it would contain garbage that you might parse if something got transmitted wrongly.

Resources