简体   繁体   中英

Sending struct over tcp socket in C++

I am trying to send struct over tcp socket. I am a newbie in socket programming, I did try the options suggested here already but those did not serve my purpose. Could someone pls help?

I have written Server.cpp and Client.cpp and both are compiling properly. However, when I am executing my Server to listen to the Client, I am not sure if the Server is able to recieve the structure from Client or not. Also, how can I read this structure once it is received?

Server.cpp

#include<iostream>
#include <cstring>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string>
#include<stdio.h>
using namespace std;
int main()
{
        struct UE
        {
        string Net;
        int imsi;
        } ;
        UE UE2;
        //cout<<UE1.imsi<<"\n"<<UE1.Net<<"\n";
        int sock, cli, receive;
        sockaddr_in server, client;
        unsigned int len;

        if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
                perror("Socket:");
                exit(-1);
        }
        server.sin_family = AF_INET;
        server.sin_port = htons(10000);
        //cout<<htons(10000);
        server.sin_addr.s_addr = INADDR_ANY;
        //cout<<INADDR_ANY;
        memset(&(server.sin_zero), '\0', 8);
        len = sizeof(struct sockaddr_in);
        if((bind(sock, (struct sockaddr *)&server, len)) == -1)
        {
                perror("Bind:");
                exit(-1);

        }
        if((listen(sock, 5)) == -1)
        {
                perror("Listen:");
                exit(-1);
        }
        while(1)
        {
                cli = accept(sock,(struct sockaddr *)&client, &len);
                if(cli == -1)
                {
                        perror("Accept");
                        exit(-1);
                }
                receive = recv(sock, (void*)&UE2, sizeof(UE2), NULL);
                cout<<UE2.imsi;
                //cout<<UE2.imsi<<"\n"<<UE2.Net;
                //int sent = send(cli, (const void*)&mesg, sizeof mesg, 0);
                //cout<<"Sent"<<sent<<" bytes to client :<<inet_ntoa(client.sin_addr)";
                close(cli);
        }
}

Client.cpp

#include <arpa/inet.h>
#include<iostream>
#include <cstring>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string>
#include<stdio.h>
using namespace std;
int main(int argc, char *argv[])
{
        struct UE
        {
        string Net;
        int imsi;
        } ;
        UE UE1;
        UE1.Net = "4G";
        UE1.imsi = htons(8649);
        int sock, receive;
        struct sockaddr_in server;
        char mesg[200];
        if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
                perror("Socket:");
                exit(-1);
        }
        server.sin_family = AF_INET;
        server.sin_port = htons(atoi(argv[2]));
        //cout<<server.sin.port;
        server.sin_addr.s_addr = inet_addr(argv[1]);
        //cout<<server.sin_addr.s_addr;
        memset(&(server.sin_zero), '\0', 8);
        if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == -1)
        {
                perror("Connect:");
                exit(-1);
        }
        int count = 0;
        while(count = 0)
        {
        send(sock, &UE1, sizeof(UE1), 0);
        //receive = recv(sock, (void*)&mesg, sizeof mesg, NULL);
        count++;
        }
        cout<<"Sent "<<UE1.imsi<<" and "<<UE1.Net<<" to Server \n";
        close(sock);
}

There are at least 2 problems in your code:

  1. You cannot send objects like std::string and anything that contains it this way (formally non-POD data), you need to marshall your data. There are plenty of libraries around (like google proto buffers) or you can write your own. This topic is too wide to cover it in the answer here.

  2. You cannot expect that you receive data from TCP stream by the same chunk you sent it, you must write code that can handle receiving data by pieces (and send it that way as well).

You should never ever write a whole struct to a file or a socket. Always write each field separately, and read them the same way. When doing it this way you pay some memory overhead, but it's generally a good design for performance reasons because you don't want to do a write of each value to the socket.

When sending binary data you should always take care for the following things:

You need some functions like the following:

virtual MESSAGE_BUFFER * GetMessageAsBinaryPtr()
{
  MESSAGE_BUFFER * binaryMsg = new MESSAGE_BUFFER;
  UINT8 * ptrBuffer = &(*binaryMsg)[0];
  ptrBuffer = this->serializeUInt16(ptrBuffer, this->m_majorVersion);
  ptrBuffer = this->serializeUInt16(ptrBuffer, this->m_minorVersion);
  ptrBuffer = this->serializeUInt32(ptrBuffer,         (UINT32)this->m_messageType);
  ptrBuffer = this->serializeUInt64(ptrBuffer, this->m_packetID);
  ptrBuffer = this->serializeDouble(ptrBuffer, this->m_timestamp);
  return binaryMsg;
}

virtual void CreateFromBinary(MESSAGE_BUFFER buffer)
{
  UINT8 * ptrBuffer = &buffer[0];
  ptrBuffer = this->deserializeUInt16FromBuffer(ptrBuffer, &this->m_majorVersion);
  ptrBuffer = this->deserializeUInt16FromBuffer(ptrBuffer, &this->m_minorVersion);

  UINT32 messageType = 0;
  ptrBuffer = this->deserializeUInt32FromBuffer(ptrBuffer, &messageType);
  this->SetMessageType((MessageTypes)messageType);

  ptrBuffer = this->deserializeUInt64FromBuffer(ptrBuffer, &this->m_packetID);
  ptrBuffer = this->deserializeDoubleFromBuffer(ptrBuffer, &this->m_timestamp);
}

inline UINT8 * serializeUInt16(UINT8 * buffer, UINT16 value)
{
  buffer[1] = value;
  buffer[0] = value >> 8;
  return buffer + 2;
}

inline UINT8 * deserializeUInt16FromBuffer(UINT8 * buffer, UINT16 *     pOutput)
{
  *pOutput = (*pOutput << 8) + buffer[0];
  *pOutput = (*pOutput << 8) + buffer[1];
  return buffer + 2;
}

When you have such functions you can serialize and deserialize your structs to a buffer and then send this buffer over your socket.

A few points to note:

  • The struct to send is first serialized, field by field into a buffer
  • MESSAGE_BUFFER is of type UINT8[1024]
  • The serialization routine returns a pointer to the next free byte in the buffer, which we use to compute how many bytes it serialized to
  • Theres no protection against buffer overflows in my routines

There are few bugs in your code. In server.cpp sockaddr_in --> struct sockaddr_in

Once connection request is accepted by server using accept() call, it returns new file descriptor , with that new fd you should do read and write operation not with old one.

Replace below statement

receive = recv(sock, (void*)&UE2, sizeof(UE2), NULL); /** you are receiving with old fd called sock **/

with

receive = recv(cli, (void*)&UE2, sizeof(UE2), NULL);

client.cpp

using namespace std;
int main(int argc, char *argv[])
{
        struct UE
        {
                string Net;
                int imsi;
        } ;
        UE UE1;
        UE1.Net = "4G";
        UE1.imsi = htons(8649);
        int sock, receive;
        struct sockaddr_in server;
        char mesg[200];
        sock = socket(PF_INET, SOCK_STREAM, 0);
        perror("Socket:");

        server.sin_family = AF_INET;
        server.sin_port = htons(atoi(argv[2]));
        server.sin_addr.s_addr = inet_addr(argv[1]);
        memset(&(server.sin_zero), '\0', 8);

        connect(sock, (struct sockaddr*)&server, sizeof(server));
        perror("Connect:");

        int count = 0;
        send(sock, &UE1, sizeof(UE1), 0);
        perror("send");
        cout<<"Sent "<<UE1.imsi<<" and "<<UE1.Net<<" to Server \n";
        close(sock);
}

server.cpp

using namespace std;
int main()
{
        struct UE
        {
                string Net;
                int imsi;
        } ;
        UE UE2;

        int sock, cli, receive;
        struct sockaddr_in server, client;
        unsigned int len;
        sock = socket(AF_INET, SOCK_STREAM, 0);
        perror("Socket:");

        server.sin_family = AF_INET;
        server.sin_port = htons(10001);
        server.sin_addr.s_addr = INADDR_ANY;
        memset(&(server.sin_zero), '\0', 8);

        len = sizeof(server);
        bind(sock, (struct sockaddr *)&server, len);
        perror("Bind:");

        listen(sock, 1);
        perror("Listen:");

        cli = accept(sock,(struct sockaddr *)&client, &len);
        perror("accept");
        receive = recv(cli, ( void*)&UE2, sizeof(UE2), 0);
        perror("recv");

        cout << "rec = "<<receive<<endl;
        cout<<UE2.imsi<<"\n";
        close(sock);
        perror("close");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM