Thread: Sending non-(char*) data

  1. #1
    He's trying.
    Join Date
    Apr 2005
    Location
    Missouri, US
    Posts
    70

    Sending non-(char*) data

    I'm starting to get (a little) into Network Programming with the Winsock API. It was pretty hard at first, actually very hard, but I'm starting to get a grip on things and was wondering - is there a way to send information (via send(), or something else) that is not in const char* format?

    Either by using a different function, or a way to convert data like a string or a structure/class to something that can be sent in const char* format and reassembled on the other side.

    I would search this, but I don't really know what terms I'd use as I don't know if it's a relevant question or what the words would be to describe it.

  2. #2
    Registered User
    Join Date
    Apr 2005
    Posts
    134
    You dont have to store your data in char * format to send it or receive it over network. If you have seen the prototypes for send() and recv() functions, you will see that the data or msg or buf is a void pointer. You can cast it to whatever you want

    Code:
    int send(int s, const void *msg, size_t len, int flags);
    
    int recv(int s, void *buf, size_t len, int flags);
    For. e.g you may use a data structure to compose your data at client end as below,


    Client:
    Code:
    /* message data structure */
    
    struct msg {
      int version;
      char command[10];
    };
    
    struct msg nmsg;
    
    /* Fill the data in nmsg */
    
    nmsg.version = 2;
    strcpy(nmsg.command,"HELO");
    
    /*Send the command to server */ 
    /*assuming sockfd is the socket connecting to the server */
    send (sockfd,(struct msg *)&nmsg,sizeof(struct msg),0);
    Similarly at server end you will have similar data structure defined

    Server:
    Code:
    /* message data structure */
    
    struct msg {
      int version;
      char command[10];
    };
    
    struct msg rmsg;
    
    int rc;
    
    /* Receive message from client */
    
    rc = recv (sockfd,(struct msg *)&rmsg,sizeof(struct msg),0);
    
    if (rc > 0)
    {
      printf(" Data Received: %d bytes\n Version: %d \t 
                              Command: %s\n",rc,msg.version,rmsg.command);
    } else if (rc==0)
    {
      printf("Connection closed\n');
    } else {
      printf("Error occured in recv()\n");
      exit(0);
    }

  3. #3
    He's trying.
    Join Date
    Apr 2005
    Location
    Missouri, US
    Posts
    70
    Ahh...The tutorial I was learning from, I think showed that, but I forgot about it because in the example he used a char array.

    Thank you very much. I will go forth and conquer now. >_>

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    nkhambal's example assumes that the sending and receiveing machine have the same byte order or endianess.

    You can also get into trouble if the structure is compiled with a different "packing" on one of the machines.

    Beej's Guide to Network Programming - #3

    gg

  5. #5
    Registered User
    Join Date
    Apr 2005
    Posts
    134
    Oh..yes.. I forgot to mention abt endieness in my last post. You need to make sure that any multibyte (more than one byte) data that you need to send across network, needs to be sent in network byte order or big endien format.

    Like for example in my last post the version is an integer which is a 4 byte data. When I pack version in the structure at client I would use htonl() (host-to-network-long) which will convert version from host byte order to network byte order which is required to send multi byte data across network. For short data (2 bytes) there is htons()

    Similarly at Server end to convert data back a into host byte order from network byte order, use ntohl() or ntohs().

    Client end:
    Code:
    nmsg.version = htonl(2);
    Server end:
    Code:
    int version;
    version = ntohl(rmsg.version);

    Char arrays are sent serially (1 byte at a time) so you actually don't need to convert it.

  6. #6
    He's trying.
    Join Date
    Apr 2005
    Location
    Missouri, US
    Posts
    70
    I'm familiar with byte-ordering (packing ports and addresses inside a sockaddr_in), but I definitely would've forgotten that...thanks again, both.

  7. #7
    Registered User
    Join Date
    Nov 2002
    Posts
    491
    He also assumes the structs will be represented the same on each system. A compiler is free to put padding between the members to properly align them. You need to serialize data if you plan on sending it over the wire. This is no easy task in C++ and even more difficult in C. The C++ FAQ Lite covers serialization fairly well if you really want to.

  8. #8
    He's trying.
    Join Date
    Apr 2005
    Location
    Missouri, US
    Posts
    70
    This seems to be growing more complex. Sending an array of characters worked fine, and so long as I'm doing simple things (like sending a structure of two int's) I shouldn't have to worry about this serializing so much aside from network ordering, right?

    Oh my. It might be time to start learning to do networking in Ruby or something...I need an excuse to spend more time writing in it.


    EDIT:
    Asking the server to send an std::string is going fine, but on the client end, when I use recv(), the compiler insists that it should be char*. No combination of casting and pointing that I've tried has helped...

    Code:
    string incomingData;
    
    int bytesIn = recv(clientSock, (string*) &incomingData, sizeof(incomingData), 0);
    Could anyone offer insight as to why this is happening and what I can do to get around it?


    EDIT 2:
    Day 137 - it occurs to me that perhaps using std::string wasn't the smartest thing to start with because even though the interface may be relatively simple, it's entirely possible the rest of it isn't. Nonetheless, my compiler will only accept types of (char *) for send() and recv() - wasn't send() working before?

    Anyway, figure I'll try using an integer or something not-char*-but-basic.


    EDIT 3:
    Got an integer to work! Praise the powers that be. It still involved casting everything to char*, but I'm happy enough that it compiles and gives the right results.
    Last edited by Nazca; 07-31-2005 at 01:20 AM.

  9. #9
    Registered User
    Join Date
    Nov 2002
    Posts
    491
    No, even sending a struct of 2 ints is by no means guranteed to work. Imagine sending to a machien where ints are 64bits wide from a machine where they are 32, the struct on the other machine is obviously going to be larger so the read will not work properly. Communicating between 2 computers requires some form of serialization.
    std::string is a class, A class is an aggregate of other objects. The only relevant information in a std::string to you is the actual data of the string. You have 2 problems. 1) Why are you trying to send a std::string. It is composed of pointers, integers, and other things. Since the internals of a std::string are implementation defined then how a std::string is laid out, and how large it is, is implementation defined. Consequence is that sending a std::string is obviously not portable at all for the same reasons sending a struct isn't. 2) A std::string has a function to convert it to a const char*, it is there so you can do useful things with it. this method is called c_str. If you want to get the contents of a string then use this for sending. To receive data is different, you need to read it into a char array or char* and then make a std::string out of it. Something like:
    bytesread = recv(...mychararray..); std::string mystring(mychararray, bytesread);

    casting an integer to a char* does *not* solve anything. What is more likely is that you are communicating the data to a machien exactly like yours or maybe even your own, so of coures an integer looks exactly the same. If you tried to send it to a PPC machien then it would not work. You need to serialize the data. a char* does not solve endian problems.
    Try looking at htonl and ntohl for simple integer serialization techniques.

  10. #10
    He's trying.
    Join Date
    Apr 2005
    Location
    Missouri, US
    Posts
    70
    When I sent the int to myself, yes, I did serialize it with htonl & ntohl, and yes, I now see why sending an std::string was so silly...and I do know about .c_str(), I just wanted to see if I could send it.

    Btw - wasn't talking about just sending a structure, I thought putting the two ints it was comprised of into network byte-order (that's big-endian, right?) and then sending the structure might work, provided the same structure was on the other end?

    As for casting my int in network byte-order as char* (because that's all the function would accept), it did work...I don't see why it shouldn't, as long as when I receive the data on the other end I cast it back to int and put it in host byte-order, right?

    Read some of the C++ FAQ Lite and found some other things on putting non-integers into byte order with unions.
    Last edited by Nazca; 07-31-2005 at 12:55 PM.

  11. #11
    Registered User
    Join Date
    Nov 2002
    Posts
    491
    Because an int does not have a guaranteed size, it has a minimum size. If an int is 64bits on one machine and 32bits on another how do you plan on handling this without any form of decent serialization technique.

    I'd suggest looking at a language like Erlang, or Oz, or even Python for decent networking infrastructures.

  12. #12
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> ...and then sending the structure might work?
    Simply sending the structure will work if:
    * The structure is a POD-struct (defined below)
    * The structure doesn't contain a pointer to data that you're trying to send
    * (C++) The structure does not contain any members after an access specifier (public, protected, private)
    * The receiving program has the same binary layout for that struct (this includes data type sizes, endianess, and structure packing.

    If someone wanted to write a client using another language then they'd have to figure out the binary layout of your structures.

    It's better to define your protocol in a language and architecture neutral manner. For 2 unsigned integers, you could state:
    The packet consists of two 4-byte, unsigned integers in network byte order.
    To implement this, you first need to know that you're working with unsigned 4-byte integers. You can use C99's uint32_t from <stdint.h> or spin your own typedef. Then when sending the two integers, it's up to you to ensure that they are in the defined format. The only portable way to do that is to use ntohl() and send() the two integers individually.

    If you have to send negative integers, then you'll also have to worry about their binary representation - 1's complement, 2's complement, sign magniture, etc...

    Having said all that, you could define your protocol to be:
    Whatever my compiler turns struct A into on my PC
    What's a POD-struct? [edit]Quoting the C++ standard:[/edit]
    3.9.10:

    Arithmetic types (3.9.1), enumeration types, pointer types, and pointer to member types (3.9.2), and cv-qualified versions of these types (3.9.3) are collectively called scalar types. Scalar types, POD-struct types, POD-union types (9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called POD types.
    9.0.4:

    A POD-struct (the acronym POD stands for ³plain olı data²) is an aggregate class that has no non-static data members of type pointer to member, non-POD- struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type pointer to member, non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor. A POD class is a class that is either a POD-struct or a POD-union.
    gg
    Last edited by Codeplug; 07-31-2005 at 03:38 PM.

  13. #13
    Registered User
    Join Date
    Nov 2002
    Posts
    491
    In short: Sending a struct over the wire is going to work much less than it's not.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Lame null append cause buffer to crash
    By cmoo in forum C Programming
    Replies: 8
    Last Post: 12-29-2008, 03:27 AM
  2. sending n no of char data uning send() function,
    By thebrighter in forum Windows Programming
    Replies: 1
    Last Post: 08-22-2007, 12:26 PM
  3. C diamonds and perls :°)
    By Carlos in forum A Brief History of Cprogramming.com
    Replies: 7
    Last Post: 05-16-2003, 10:19 PM
  4. Replies: 1
    Last Post: 02-06-2003, 03:33 PM
  5. sending data to printer port
    By lliero in forum C Programming
    Replies: 3
    Last Post: 11-20-2001, 04:57 AM