We help IT Professionals succeed at work.

Sending struct via TCP in C

Jgreer2011
Jgreer2011 asked
on
394 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

Comment
Watch Question

CERTIFIED EXPERT
Top Expert 2009
Commented:
Unlock this solution and get a sample of our free trial.
(No credit card required)
UNLOCK SOLUTION

Author

Commented:
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

CERTIFIED EXPERT
Top Expert 2009

Commented:
>> 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.

Author

Commented:
Can you please elaborate on part b).. detailing how to write the struct separately with code.
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> 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

Commented:
Can you dump your screen here?
I want to see the inputs you are giving and the outputs you are getting.

Author

Commented:
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

CERTIFIED EXPERT
Top Expert 2009

Commented:
>> 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.

Commented:
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.
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> 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.

Author

Commented:
Ok will implement

Commented:
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.

Author

Commented:
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

CERTIFIED EXPERT
Top Expert 2009

Commented:
>> 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.

Author

Commented:
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?
CERTIFIED EXPERT
Top Expert 2009

Commented:
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.

Author

Commented:
Are you able to debug my code for me ? I can ensure endless points. I am very inexperience.
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> 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.

Author

Commented:
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?

Commented:
> 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.
evilrixSenior Software Engineer (Avast)
CERTIFIED EXPERT

Commented:
This question has been classified as abandoned and is closed as part of the Cleanup Program. See the recommendation for more details.
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a sample view!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.