在C ++中通过tcp套接字发送struct

时间:2018-01-17 14:07:58

标签: c++ sockets tcp send recv

我正在尝试通过tcp套接字发送struct。我是套接字编程的新手,我确实尝试了这里建议的选项,但那些不符合我的目的。有人可以帮忙吗?

我编写了Server.cpp和Client.cpp,两者都正在编译。但是,当我执行我的服务器以收听客户端时,我不确定服务器是否能够从客户端接收结构。另外,一旦收到这个结构,我该怎么读?

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);
}

3 个答案:

答案 0 :(得分:4)

您的代码中至少存在2个问题:

  1. 您无法以这种方式发送std::string之类的对象和包含它的任何内容(正式非POD数据),您需要对数据进行编组。周围有很多库(比如google proto缓冲区),或者你可以编写自己的库。这个主题太广了,无法在答案中介绍它。

  2. 您不能指望通过您发送的相同块从TCP流接收数据,您必须编写可以处理接收数据的代码(并以此方式发送)。

    < / LI>

答案 1 :(得分:3)

您永远不应该将整个结构写入文件或套接字。 始终分别编写每个字段,并以相同的方式读取它们。 这样做时你需要支付一些内存开销,但出于性能原因它通常是一个很好的设计,因为你不想把每个值写入套接字。

发送二进制数据时,您应始终注意以下事项:

您需要以下功能:

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;
}

如果有这样的函数,可以将结构序列化和反序列化到缓冲区,然后将这个缓冲区发送到套接字上。

需要注意几点:

  • 首先将要发送的结构序列化,逐字段化为缓冲区
  • MESSAGE_BUFFER的类型为UINT8 [1024]
  • 序列化例程返回指向缓冲区中下一个空闲字节的指针,我们用它来计算它序列化的字节数
  • 在我的例程中没有防止缓冲区溢出的保护

答案 2 :(得分:1)

您的代码中存在很少的错误。 在server.cpp中 sockaddr_in - &gt; struct sockaddr_in

server使用accept()调用接受连接请求后,会返回新文件描述符,您应该执行 new fd { {1}}和read操作不是旧操作。

替换以下声明

write

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

<强> client.cpp

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

<强> server.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);
}