C++ - tcp網路傳輸如何傳送結構體型別

[BORUTO]發表於2024-05-29

1、tcp網路傳輸如何傳送結構體型別

在C++中,要透過TCP網路傳輸結構體型別,你需要將結構體序列化為位元組流,然後在另一端反序列化。這裡有一個簡單的例子:

#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
 
// 假設的結構體
struct MyStruct {
    int id;
    float value;
    // 序列化函式
    char* serialize() const {
        char* buffer = new char[sizeof(MyStruct)];
        memcpy(buffer, &this->id, sizeof(int));
        memcpy(buffer + sizeof(int), &this->value, sizeof(float));
        return buffer;
    }
    // 反序列化函式
    void deserialize(char* buffer) {
        memcpy(&this->id, buffer, sizeof(int));
        memcpy(&this->value, buffer + sizeof(int), sizeof(float));
    }
};
 
int main() {
    int sockfd;
    struct sockaddr_in servaddr;
 
    // 建立socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
    // 伺服器地址
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(1234);
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
 
    // 連線伺服器
    connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
 
    // 建立結構體例項
    MyStruct obj = {123, 3.14f};
 
    // 序列化併傳送
    char* buffer = obj.serialize();
    send(sockfd, buffer, sizeof(MyStruct), 0);
    delete[] buffer;
 
    // 接收並反序列化
    char recvBuffer[sizeof(MyStruct)];
    recv(sockfd, recvBuffer, sizeof(recvBuffer), 0);
    MyStruct receivedObj;
    receivedObj.deserialize(recvBuffer);
 
    // 輸出接收到的結構體成員
    std::cout << "Received ID: " << receivedObj.id << ", Value: " << receivedObj.value << std::endl;
 
    // 關閉socket連線
    close(sockfd);
    return 0;
}

在這個例子中,MyStruct 結構體包含了兩個成員:idvalueserialize 函式將結構體轉換為位元組流,而 deserialize 函式從位元組流中恢復結構體。

在實際的TCP通訊過程中,你需要確保傳送和接收方的序列化和反序列化方式完全一致,以正確地重建結構體資料。

2、TCP-socket傳送結構體型別資料

UDP傳輸模式是資料包,TCP傳輸模式為位元組流,位元組流與資料包區別在於有邊界與無邊界。例如:TCP客戶端傳送了三個資料包,開的快取足夠大服務端一次可接收三個資料包的資料,這就是無邊界。UDP客戶端傳送了三個資料包,就算開的快取足夠大服務端一次也只能接收一個資料包,這就是有邊界。

還有就是協議會維護源地址和目的地址直到協議要求斷開連線,這就決定了TCP不能進行廣播和多播。

如何使用TCP傳送結構體型別資料:

· //使用結構體轉換成字串傳送,在伺服器端直接轉為結構體

· /*

· char send_buf[1024] = "tony 2000 ";

· memset(send_buf,0,1024);

· struct msg

· {

· int cmd;

· int sendID;

· int recvID;

· string name;

· int number;

· };

· msg msg1;

· msg1.cmd = COMMAND;

· msg1.sendID = 2120100324;

· msg1.recvID = 2120100325;

· msg1.name = "Tony";

· msg1.number = 2000;

·

·

· //以字串形式傳送,因為TCP/IP是位元組流通訊

· //memcpy(send_buf,&msg1,sizeof(msg));

· //int len_send = send(Socket,send_buf,sizeof(send_buf),0);

· int len_send = send(Socket,(char *)&msg1,sizeof(msg),0);

如上所示,

TCP是無邊界的位元組流傳輸,所以需要將結構體轉換為字串後在傳送,最後三行用了兩種方法傳送屬於結構體型別的資料,透過TCP傳輸。最後在接收方需要轉換為結構體。

紅色: 陣列屬於字串,該方法是將要傳送結構體所佔位元組大小考到陣列中, 再透過陣列傳送。

藍色: 將該結構體地址轉化為char* 型別的地址,目的是使該指標加1移動時 是按一個位元組移動,而不是加1按該結構體大小移動,然後傳送該結構 體所佔位元組大小。

3、C++ socket 傳輸不同型別的資料

1.1 使用結構體

I.假設需要傳輸的結構體資料如下:

注意:(傳送方和接收方都需要定義相同的結構體)

struct Student
{
	int iId;
	string strName;
	bool bSex;    //為了節省記憶體空間,性別採用一個位元組的BOOL型別表示
};

  

II.傳送方程式碼(客戶端)

struct Student stu;    //宣告一個Student結構體變數
stu.iId = 1001;
stu.bSex = true;       //true表示男性,false表示女性,你反過來也行,別打拳
stu.strName = "abcdefzzzzz";
 
//下面的m_sclient是客戶端(傳送方)的Socket套接字
 
//方法一:推薦如下
send(m_sclient, (char*)&stu, sizeof(Student), 0);//&stu取stu地址,(char*)轉化為char型別的指標
 
//方法二:或者增加一箇中間變數sendBuff[]來傳送,如下
//char sendBuff[1024];
//memset(sendBuff,0,sizeof(sendBuff));
//memcpy(sendBuff, &stu, sizeof(Student));
//send(m_sclient, sendBuff, sizeof(sendBuff), 0);

III.接收方程式碼(服務端)

struct Student stu;    //宣告一個結構體變數,用於接收客戶端(傳送方)發來的資料
char buffFromClient[1024];    //用於臨時接收傳送方的資料
//方法一:(推薦)
recv(clientSocket, (char*)&stu, sizeof(Student), 0);
 
//方法二:
//memset(buffFromClient,0,sizeof(buffFromClient));
//recv(clientSocket, buffFromClient, sizeof(Student), 0);
//memset(&stu,buffFromClent,sizeof(Student));

1.2 使用類物件

I.新增StudentInfo類

注意:(傳送方和接收方都需要定義相同結構的類物件)

//StudentInfo.h檔案如下,.cpp檔案自行實現
 
#pragma once
#include <iostream>
using namespace std;
class StudentInfo
{
private:
	int m_iId;
	string m_strName;
	bool m_bSex;
 
public:
	StudentInfo();
	~StudentInfo();
 
	int GetId();
	string GetName();
	bool GetSex();
 
	void SetId(int iId);
	void SetName(string strName);
	void SetSex(bool bSex);
};

II.傳送方程式碼(客戶端)

初始化類物件併傳送資料

//初始化stuInfo類物件
StudentInfo stuInfo;
stuInfo.SetId(111);
stuInfo.SetName("abcdefzzzzz");
stuInfo.SetSex(true);
send(m_clientSocket, (char*)&stuInfo, sizeof(StudentInfo), 0);

II.接收方程式碼(服務端)

定義類物件並接收資料

StudentInfo stuInfo;
int iLenOfRecvData = -1;
//傳輸類物件資料
iLenOfRecvData = recv(clientSocket, (char*)&stuInfo, sizeof(StudentInfo), 0);
if (iLenOfRecvData > 0)		//如果接收的資料不為空
{
	cout << stuInfo.GetId() << endl;
	cout << stuInfo.GetName() << endl;
	cout << stuInfo.GetSex() << endl;
}

相關文章