[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Sending struct via TCP in C

Posted on 2011-04-26
22
Medium Priority
?
316 Views
Last Modified: 2012-05-11
I have a program to send records to a tcp server from a client. The problem is the server is not receiving the correct bytes.

Ex. Struct to Send

struct a {
uint16_t id;
char name[20];
 }m;

struct b {
uint32_t id;
char fname[20];
char lname[20];
}n;

write(fd,&m, sizeof(struct a));
File Format
123
Kim
Lee
234
John
Lee

Run Client code like: ./a.out <IP> <PORT> < record.txt
How do I send buffer file to the server to ensure correct padding and endianness?
See code example below. What I doing wrong??
/* C Libraries to include in the program. */
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/socket.h>

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#define BUFFSIZE    256

/* Declaring header stuctures. */

struct header{
uint16_t ver;
uint16_t cnt;
};

/* Declaring version stuctures. */


struct ver1{
uint16_t id;
char name[20];
}v[1024];

struct ver2{
uint32_t pid;
char fname[20];
char lname[20];
}r[1024];

struct ver1 *v1_ptr;
struct ver2 *v2_ptr;


 /* Start of main () .*/

int main ( int argc, char *argv[])
{

 /* Declaring important variables*/

int sockfd,portnum,n=0, j=0;
int i = 0,x=0;
socklen_t len;
char *string;
struct sockaddr_in a;
struct sockaddr *ptr;
struct header h;
ptr = (struct sockaddr *)&a;
len = sizeof(a);
char *host;
host = argv[1];
portnum = atoi(argv[2]);
FILE *fp;
int  version;
version = atoi(argv[3]);
char buff[BUFFSIZE];
h.ver = htons(version);
// h.cnt = htons(5);

char temp[BUFSIZ];

/* Instructions to run program from command prompt. */
if (argc < 4 ) {
    printf("usage:%s <IP> <PORT> 0 <FILE> \n", argv[0]);
}

/* Notify of socket connection error. */

if (( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ) {
      printf("Socket Connection Error");
      exit (0);
}

/* Set up the server address.*/

bzero(&a, sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(portnum);
if (inet_pton(AF_INET, host, &a.sin_addr) <= 0 )
printf ("inet_pton error for %s", argv [1]);

/* Connecting to the server. */

 connect (sockfd, (struct sockaddr *) &a, sizeof(a));

// Open and read text file.

    if((fp = fopen("record.txt","r"))==NULL) {
    printf("Cannot open file.\n");
      exit(1);
  }

// Begin read for ver2
 

   while(fscanf(fp,"%s",buff) != EOF)
   {

         if(x == 0)
         {
         r[n].pid =htonl( atoi(buff));
        printf("r.pid-->%d\n",htonl(r[n].pid));

         }
         if(x == 1){
        strcpy(r[n].fname,buff);
        printf("r.fname = %s\n",r[n].fname);
                     }
         if(x == 2){
         strcpy(r[n].lname,buff);

 printf("r.lname = %s\n",r[n].lname);
        }
        x++;
       n++;
        if(x > 2)
        x=0;
             }


   // begin read for ver1

    if((fp = fopen("record.txt","r"))==NULL) {
    printf("Cannot open file.\n");
     exit(1);
  }
    while(fscanf(fp,"%s",buff) != EOF)
   {
       if(x == 0)
         {
       v[n].id = htons(atoi(buff));
      printf("v.id-->%d \n",htons(v[n].id));


          }
       if(x >= 1){
       strcpy(v[n].name,buff);
       printf("v.name = %s\n",v[n].name);

 }
     x++;
     n++;
     if(x > 2)
 x=0;
// count line (should equal 15)
     j++;
}
     // Number of records to send (should = 5)
      h.cnt = htons(j)/3;

      fflush(fp);


// Start to write to the server.

      write(sockfd,&h,sizeof(struct header)); // write header

      if(h.ver == 0){
 
// Write version 1 (I use 22 to push out all the data but you can
// change, if I dont it sends nothng to server)

       write(sockfd,&v[22],htons(sizeof(v[22])));
      }
       if(version > 0){

//  write version 2


    write(sockfd,&r[0],htonl(sizeof(r[6])));
   }
          close(sockfd);
          return(0);
    }

Open in new window

0
Comment
Question by:Jgreer2011
  • 8
  • 8
  • 4
  • +1
21 Comments
 
LVL 53

Accepted Solution

by:
Infinity08 earned 2000 total points
ID: 35472967
>> How do I send buffer file to the server to ensure correct padding and endianness?

Instead of sending the block of memory that represents the struct on one machine (including padding and endianness specifics as you correctly note) to the other machine (which might have different padding rules and/or endianness), you should serialize the data before sending, and unserialize it after receiving.

There are many serialization libraries around. A good one is the boost serialization library eg. :

        http://www.boost.org/doc/libs/release/libs/serialization/


However, in your case, you could do it relatively easily yourself. As long as you follow these rules :

(a) use only fixed-width datatypes (you already are, so that's ok)

(b) write every member of the struct separately (so instead of one write for the header struct, you'd have two writes - one for each of its data members ver and cnt)

(c) before sending an integer value, convert it to network byte order, using htons or htonl for 16bit and 32bit values resp. (you're already using these, but slightly incorrectly - you probably shouldn't store the converted value into the local struct, because that might make it unreadable locally - instead, perform the conversion just before sending)

On the receiving end, you can do things the other way around.
0
 

Author Comment

by:Jgreer2011
ID: 35473030
Yes, I have tried everything , and more even reading the structs individually with getch() and store to a buffer, check my code below. But I still get spaces between the receive input and also garbage characters.
if((fp = fopen("record.txt","r"))==NULL) {
    printf("Cannot open file.\n");
    exit(1);
  }


while(1)
 getch();

while(c != '\n' && c != EOF){
buff[i]=c;
i++;
c = fgetc(fp);
}
buff[i] = '\0';

a.id = atoi(buff);
a.id = atoi(buff);

strcpy(a.name,buff);
strcpy(a.name,buff);

b.pid = atoi(buff);
b.pid = atoi(buff);

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35473042
>> even reading the structs individually with getch()

That won't help you, since you're still depending on how the data is stored in the file.

In my previous post, I tried to very clearly describe the two changes you have to make. Please let me know if any of what I said requires further clarification.
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:Jgreer2011
ID: 35473171
Can you please elaborate on part b).. detailing how to write the struct separately with code.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35473218
>> Can you please elaborate on part b).. detailing how to write the struct separately with code.

Sure.

Right now, you're sending the header struct like this :

>> h.ver = htons(version);
<SNIP>
>> h.cnt = htons(j)/3;
<SNIP>
>> write(sockfd,&h,sizeof(struct header));

Instead you should do something like below :
h.ver = version;                /* <--- store the actual values in the struct (ie. don't convert to network byte order) */

/*<SNIP>*/

h.cnt = j / 3;                  /* <--- store the actual values in the struct (ie. don't convert to network byte order) */

/*<SNIP>*/

uint16_t h_ver_n = htons(h.ver);                /* <--- convert to network byte order just before sending */
uint16_t h_cnt_n = htons(h.cnt);
write(sockfd, &h_ver_n, sizeof(h_ver_n));       /* <--- and send ... */
write(sockfd, &h_cnt_n, sizeof(h_cnt_n));

/* I've left out error checking for the two write function calls, but you should add that ! */

Open in new window

0
 
LVL 8

Expert Comment

by:ssnkumar
ID: 35474506
Can you dump your screen here?
I want to see the inputs you are giving and the outputs you are getting.
0
 

Author Comment

by:Jgreer2011
ID: 35476877
The problem is not with the header struct, it is reading and sending perfect
 THe problem is with the other structs below.  When I read the file:
File format:
123
Kim
Lee
234
Bob
Lee

I send to the server, ./a.out <IP> <PORT>  0 <file.txt
The output should look like:
Version = 1 Count = 2
ID 123 (Kim, Lee)
ID 234 ( Bob, Lee)

However this is my incorrect input:
Version = 1 Count = 2
ID 123 (Kim,      )
ID 0     (,    Lee  )
ID (        )
etc.....

The padding is wrong . I need to store the data to the struct according to the different version types.
struct ver1{
uint16_t id;
char name[20];
}v1;

struct ver2{
uint32_t pid;
char fname[20];
}v2;

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35478388
>> The problem is not with the header struct, it is reading and sending perfect

That's quite possible. However - that's by chance rather than by design.

The serialization technique I showed for the header struct should be applied to all data you send over the network. Not just for the header struct.
0
 
LVL 8

Expert Comment

by:ssnkumar
ID: 35481032
In your client code, you are sending the version number using:
> write(sockfd,&h,sizeof(struct header));
And for sending the file, at one place you are using:
> if(h.ver == 0){
> // Write version 1 (I use 22 to push out all the data but you can
> // change, if I dont it sends nothng to server)
>        write(sockfd,&v[22],htons(sizeof(v[22])));
> }
and in the next place:
>        if(version > 0){
> //  write version 2
>      write(sockfd,&r[0],htonl(sizeof(r[6])));
> }

And for getting the version number you are doing:
> version = atoi(argv[3]);

Also, you have given an example of how you are executing your client code:
> Run Client code like: ./a.out <IP> <PORT> < record.txt

From this, argv[3] is record.txt.
But, your version is trying to convert this to integer!

So, your 3rd argument has to be an integer value.
Suppose, your 3rd argument is (that is version) is 2:
version will get the value 2.
But, h.ver becomes 512!

So, I think, the way you are using version before writing to socket is making some issues.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35481088
>> So, I think, the way you are using version before writing to socket is making some issues.

That is because htons is used incorrectly, as I tried to explain before.

The same happens here eg. :

>> h.cnt = htons(j)/3;

which is completely wrong. Don't do calculations on a value that you've just converted to network byte order. If the host byte order happens to be different from the network byte order, you'll get very strange results !

As I said earlier : Do the htons just before sending the value - DON'T use it anywhere else.



And I just noticed another irregularity : you try to have support for two versions. So you read the file twice, storing the same data in two different ways. A few remarks about that :

(a) you're not resetting the counters between the two loops, so the second loop will behave strangely.

(b) if you implement the approach I described earlier, you won't need to read the file twice, or store the data twice. You can store the data once in your version two struct, but send the data differently depending on the version number.
0
 

Author Comment

by:Jgreer2011
ID: 35481136
Ok will implement
0
 
LVL 8

Expert Comment

by:ssnkumar
ID: 35481158
Jgreer2011,

I have got one general suggestion for you.
Before starting to write code, write down the steps that you are going to follow (in plain english).
After this, try to write a pseudo code and get it reviewed by somebody.
After finalizing the steps/pseudo code only, start writing the C code.

And when you write code, please follow indentation.
That will greatly improve readability.
For example, your struct declaration:
> struct ver1{
> uint16_t id;
> char name[20];
> }v[1024];

After you open a braces, give one tab space for each next line till the braces is closed.
So, with this change, your struct will look like:
struct ver1 {
    uint16_t id;
    char name[20];
} v[1024];

Without this, reading your code and understanding it and also debugging it will become very difficult.

So, when you are done with modification, please make sure that, you indent the code properly and post it here.
Post both your client code and server code.
0
 

Author Comment

by:Jgreer2011
ID: 35485108
Ok I will post code after modify, but I am a beginner and the comments are more suited for medium to experts programmer. I will reimplement the program in a different way without getch() to read the file:
as so ./a.out <IIP> <PORT> 0 < file.txt    from the command line. I would like to see more examples for concept of struct sending using my different versions.
/* C Libraries to include in the program. */
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/socket.h>

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#define BUFFSIZE    256

/* Declaring header stuctures. */

struct header {
uint16_t ver;
uint16_t cnt;
};

/* Declaring version stuctures. */


struct ver1 {
uint16_t id;
char name[20];
} v;

struct ver2 {
uint32_t pid;
char fname[20];
char lname[20];
} r;


 /* Start of main () .*/

int main ( int argc, char *argv[])
{

 /* Declaring important variables*/

int sockfd,portnum,n=0, j=0;
int i = 0,x=0;
socklen_t len;
char *string;
struct sockaddr_in a;
struct sockaddr *ptr;
struct header h;
ptr = (struct sockaddr *)&a;
len = sizeof(a);
char *host;
host = argv[1];
portnum = atoi(argv[2]);
FILE *fp;
int  version;
version = atoi(argv[3]);
char buff[BUFFSIZE];
char c;

h.ver = htons(version);
h.cnt = htons(2);


/* Instructions to run program from command prompt. */
if (argc < 3 ) {
    printf("usage:%s <IP> <PORT> 0 <FILE> \n", argv[0]);
}

/* Notify of socket connection error. */

if (( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ) {
      printf("Socket Connection Error");
      exit (0);
}

/* Set up the server address.*/

bzero(&a, sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(portnum);
if (inet_pton(AF_INET, host, &a.sin_addr) <= 0 )
printf ("inet_pton error for %s", argv [1]);

/* Connecting to the server. */

 connect (sockfd, (struct sockaddr *) &a, sizeof(a));

// Open and read text file.

    if((fp = fopen(argv[3],"r"))==NULL) {
    printf("Cannot open file.\n");
      exit(1);
  }
  
   while(1)
     getch();

   // Do I need to declare getch() = c; ?

while(c != '\n' && c != EOF){
   buff[i]=c;
   i++;
  }
  
  buff[i] = '\0';

// Assign val in buff to the structs types

 v.id = atoi(buff);
 r.id = atoi(buff);

 strcpy(v.name,buff);
 strcpy(r.fname,buff);
 strcpy(r.lname,buff);

// Start to write to the server.

   write(sockfd,&h,sizeof(struct header)); // write header

      if(h.ver == 0){
 
// Write version 1 
 
      write(sockfd,&v,(sizeof(struct ver1)));
       }
  
// Write version 2 

     if(version > 0){

  write(sockfd,&r,(sizeof(struct ver2)));
     }
          close(sockfd);
          return(0);
   }

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35485181
>> I would like to see more examples for concept of struct sending

Did you understand my example in http:#35473218 ? The idea is to send each struct member separately, and if it's an integer value, to convert it to network byte order using htons or htonl. There's nothing more to it.

If anything is unclear in that example, please ask.


>>  using my different versions.

As I said earlier : if you make the modifications I suggested, you won't have to maintain two separate structs for the separate versions. You can make the decision about what to send (and how), at the moment you need to send it.
0
 

Author Comment

by:Jgreer2011
ID: 35488272
Ok I will try to implement it. But I am using the strcpy and v.id = atoi(buff) correctly or should I modify that too?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35489501
If you just strcpy strings, and use atoi for integer values, then that's fine for storing the data in the struct.
As long as you don't use htons or htonl at that point, it's ok.
0
 

Author Comment

by:Jgreer2011
ID: 35490026
Are you able to debug my code for me ? I can ensure endless points. I am very inexperience.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35490041
>> I am very inexperience.

The only way to gain experience, is by doing the debugging yourself :)


That said, I've already explained how you can fix your code. And I've offered to give you clarification for any part that is still unclear - you need but ask.
0
 

Author Comment

by:Jgreer2011
ID: 35498125
Ok I have been working on the client code and have a question.
Command to run client program :
./a.out <IP> <PORT> <Version>  < text.file

My input is a text file.
Text Format In File
123
Bob
Greer

Which function will be best to use if I wanted to get the characters one by one and after a newline
separate the words. getc(), getchar, fgets etc.

Also should I use a dynamic buffer to hold the data after reading from the file, so there is no room for
garbage characters to occur in the buffer?
0
 
LVL 8

Expert Comment

by:ssnkumar
ID: 35499461
> Which function will be best to use if I wanted to get the characters one by one and after a newline
> separate the words
Why do you want to read char by char and then separate the words?
Just use fscanf() and you can directly get words by words.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 37419109
This question has been classified as abandoned and is closed as part of the Cleanup Program. See the recommendation for more details.
0

Featured Post

NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
Preface I don't like visual development tools that are supposed to write a program for me. Even if it is Xcode and I can use Interface Builder. Yes, it is a perfect tool and has helped me a lot, mainly, in the beginning, when my programs were small…
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use nested-loops in the C programming language.
If you're a developer or IT admin, you’re probably tasked with managing multiple websites, servers, applications, and levels of security on a daily basis. While this can be extremely time consuming, it can also be frustrating when systems aren't wor…

872 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question