0

I have a structure:

struct one
{
    char name[10];
    int age;
};

struct two
{
    int X;
    int Y;
};

And now i want to send this structure for example first 'one', second 'two' and receive from socket.

But how receive it, if i dont know which structure i send (stuct 'one' or 'two' )?

user2652995
  • 57
  • 2
  • 15
  • 2
    Prepend a byte containing information on which structure follows. For example "1" if a `one` structure follows or a 2 if a `two` struct comes next. – nikeee Apr 17 '14 at 20:41
  • Don't use structs as network protocols. You are introducing half a dozen dependencies. Use network protocols as network protocols. Design an application protocol in octets and write yourself a library to send and receive it. – user207421 Apr 18 '14 at 00:30

3 Answers3

5

You could add identifiers to the data you're sending:

enum StructID {
    STRUCT_ONE,
    STRUCT_TWO,
};

And send that before you send your data.

Uint16     id;
struct one dataOne;

id = STRUCT_ONE;
send(&id, sizeof(id));
send(&dataOne, sizeof(dataOne));

And on the receiving end:

char buffer[256];
unsigned nbRecv;

nbRecv = recv(buffer, sizeof(buffer));
if (nbRecv > sizeof(Uint16))
{
    char * p = buffer;

    Uint16 *pId = (Uint16*)p;
    p += sizeof(*pId);

    if (*pId == STRUCT_ONE)
    {
        struct one * pOne = (struct one *)p;
        p += sizeof(*pOne);

        if (nbRecv >= sizeof(*pId) + sizeof(*pOne))
        {
           // deal with pOne.
        }
        else
        {
           // Deal with too little data; pOne is incomplete....
        }
    }
    else if (*pId == STRUCT_TWO)
    {
        struct two * pTwo = (struct two *)p;
        p += sizeof(*pTwo);

        if (nbRecv >= sizeof(*pId) + sizeof(*pTwo))
        {
           // deal with pOne.
        }
        else
        {
           // Deal with too little data; pTwo is incomplete....
        }
    }
    else
    {
        // Error, unknown data.
    }
}
else
{
    // Deal with too little data....
}

Essentially you're defining a protocol at this point and the identifier is just a very simple "header" that exists to identify your data. A lot of protocols like this send the size of the data to follow as well so you can tell how much of the data there is before the next identifier/header.

Another common method besides integers is to send 4 ASCII characters, since they're easy to read when you are looking at raw data (Wireshark, hexdump, bytes in a debugger, etc). For your example I would suggest:

const char STRUCT_ONE_FOURCC[4] = { 'O', 'N', 'E', ' ' };
const char STRUCT_ONE_FOURCC[4] = { 'T', 'W', 'O', ' ' };

(Note, they're not strings per-say because they're not NULL terminated. They're fixed size character arrays.)

Note: in the above code I've left out most error checking and endian swap (to/from network byte order).

See Also:

  • Alternatively, I have seen `const int STRUCT_ONE_FOURCC = 'ONE\0'; const int STRUCT_TWO_FOURCC = 'TWO\0';`, so they're copiable, but that's a little wierder. – Mooing Duck Apr 17 '14 at 21:08
  • Multi-character constants aren't technically portable, so be careful: http://stackoverflow.com/questions/7755202/multi-character-constant-warnings. Granted without some htons() & ntohs() added above, neither are my Uint16's containing enum values. – Philippe Chaintreuil Apr 17 '14 at 21:38
  • Thanks but, when i try to `struct ST_ConInitChar * a = (struct ST_ConinitChar *) p;` it return `cannot convert from 'main_pipe_server::ST_ConinitChar *' to 'ST_ConInitChar *'` func main_pipe_server is a thread where i recv data from pipe – user2652995 Apr 25 '14 at 14:59
  • Hmm i make: `bytes = send(maindll_socket,(char *) &id, sizeof(id), 0);` `bytes2 = send(maindll_socket, (char *)&a, sizeof(a), 0);` and recv dont work fine: ` if (bytesRecv > sizeof(int)){ char * p = wiadomosc; int *pId = (int*)p; p += sizeof(*pId); if (*pId == STRUCT_INIT_NUM){ ST_ConInitChar * a = (ST_ConInitChar *)p; if (sizeof(a) > 0){ //messagebox with a->name return - nothing } } }` – user2652995 Apr 25 '14 at 23:54
  • "int pId = (int)p;" should be "int * pId = (int *)p;" Not sure how your compiler didn't scream about the dereferencing of an int in the following if statement. I'm also not sure what you're trying to do with "if (sizeof(a) > 0)" -- that's testing if the size of a pointer is larger than 0. – Philippe Chaintreuil Apr 30 '14 at 21:31
  • Also, I just changed code above to add checks to make sure nbRecv is enough for the id & which ever payload is expected for that id. – Philippe Chaintreuil Apr 30 '14 at 21:38
0

You need to perform a serialization, adding an identifier. A (not so) simple solution would be:

template<int index>
struct serializable {
  static const int identifier = index;
}

struct A :
  public serializable<1>
{
  int a, b;
  char * serialize() { char *buffer = new char[sizeof (A) + sizeof (int)]; memcpy(buffer, this, sizeof(A)); }
  static A deserialize(const char *in) { return A{&in[sizeof(int)], &in[sizeof(int) * 2]}; }
}
Bruno Ferreira
  • 1,621
  • 12
  • 19
0

You should define a protocol which you will use in your communication, don't send struct because of the different datatypes and memory alignment in a different systems. You can use xml instead, serialize your struct and send the xml string. Also check different libraries which are implementing the communication protocols over TCP. You can check the Apache Axis for instance. For manual serialization you can use boost::serialization and transform your struct to xml string.

AlexTheo
  • 4,004
  • 1
  • 21
  • 35