【C++】使用C++实现基于Socket的通信

news2024/9/28 21:26:42

在这里插入图片描述

在本文中,我们将详细讨论如何使用C++实现基于Socket的通信,并设计一个TLV(Type-Length-Value)协议用于数据交互。TLV协议因其灵活性和可扩展性,在多种通信协议中被广泛使用,特别是在需要动态定义数据结构的场景中。我们将分步骤实现Socket通信,设计TLV协议,并通过示例代码展示其应用。
在这里插入图片描述

一、Socket通信基础

1.1 Socket简介

Socket是一种网络通信接口,它提供了端到端的通信服务。Socket分为TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议)两种类型。TCP是面向连接的、可靠的、基于字节流的传输层通信协议,而UDP则是无连接的、不可靠的、基于数据报的传输层通信协议。

1.2 TCP Socket编程基本步骤

创建Socket:使用socket()函数创建一个新的socket描述符。
绑定Socket:使用bind()函数将socket与特定的IP地址和端口号绑定。
监听连接(服务器端):使用listen()函数使socket进入监听状态,准备接收客户端的连接请求。
接受连接(服务器端):使用accept()函数接受客户端的连接请求,建立连接。
连接服务器(客户端):使用connect()函数与服务器建立连接。
数据读写:使用send()、recv()等函数进行数据的发送和接收。
关闭连接:使用close()函数关闭socket连接。

二、TLV协议设计

TLV(Type-Length-Value)协议是一种简单但强大的数据编码方式,它通过三个主要部分来组织数据:

Type(类型):用于标识Value的类型或用途,通常是一个整数。
Length(长度):表示Value部分的长度,也是一个整数。
Value(值):实际的数据内容,其类型和长度由Type和Length决定。

2.1 TLV数据结构定义

#include <cstdint>  
#include <vector>  
#include <memory>  
  
struct TLVElement {  
    std::uint16_t type;    // Type部分,通常使用16位整型  
    std::uint16_t length;  // Length部分,也是16位整型  
    std::vector<std::uint8_t> value;  // Value部分,使用字节向量存储  
  
    // 构造函数、序列化、反序列化等成员函数可以在这里添加  
};  
  
// TLV消息可以看作是一个TLVElement的数组  
using TLVMessage = std::vector<TLVElement>;

2.2 TLV协议的序列化与反序列化

序列化是将TLV消息转换为字节流以便在网络中传输的过程,反序列化则是将接收到的字节流转换回TLV消息的过程。

// 序列化函数示例  
std::vector<std::uint8_t> SerializeTLVMessage(const TLVMessage& message) {  
    std::vector<std::uint8_t> result;  
    for (const auto& elem : message) {  
        // 写入Type  
        result.push_back(elem.type & 0xFF);  
        result.push_back((elem.type >> 8) & 0xFF);  
  
        // 写入Length  
        result.push_back(elem.length & 0xFF);  
        result.push_back((elem.length >> 8) & 0xFF);  
  
        // 写入Value  
        result.insert(result.end(), elem.value.begin(), elem.value.end());  
    }  
    return result;  
}  
  
// 反序列化函数需要根据实际情况设计,这里不详细展开

三、C++ Socket编程实现

3.1 服务器端代码实现

#include <iostream>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <unistd.h>  
#include <cstring>  
  
// 假设Socket和TLV的序列化/反序列化已经实现  
  
int main() {  
    int server_fd, new_socket;  
    struct sockaddr_in address;  
    int opt = 1;  
    int addrlen = sizeof(address);  
  
    // 创建socket文件描述符  
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {  
        perror("socket failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 强制绑定socket到端口8080  
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSE
ADDRPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {  
    perror("bind failed");  
    exit(EXIT_FAILURE);  
}  
if (listen(server_fd, 3) < 0) {  
    perror("listen");  
    exit(EXIT_FAILURE);  
}  
 
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {  
    perror("accept");  
    exit(EXIT_FAILURE);  
}  
 
// 假设我们有一个TLVMessage需要发送给客户端  
TLVMessage messageToSend;  
// 填充messageToSend...  
 
// 序列化TLVMessage为字节流  
auto serializedData = SerializeTLVMessage(messageToSend);  
 
// 发送数据  
send(new_socket, serializedData.data(), serializedData.size(), 0);  
 
// 关闭socket  
close(new_socket);  
close(server_fd);  
 
return 0;
}

3.2 客户端代码实现

#include <iostream>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <cstring>  
  
// 假设Socket和TLV的反序列化函数已经实现  
  
int main() {  
    struct sockaddr_in serv_addr;  
    int sock = 0;  
  
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        std::cerr << "Socket creation error" << std::endl;  
        return -1;  
    }  
  
    serv_addr.sin_family = AF_INET;  
    serv_addr.sin_port = htons(8080);  
  
    // 将IPv4地址从文本转换为二进制形式  
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {  
        std::cerr << "Invalid address/ Address not supported" << std::endl;  
        return -1;  
    }  
  
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {  
        std::cerr << "Connection Failed" << std::endl;  
        return -1;  
    }  
  
    // 接收数据  
    char buffer[1024] = {0};  
    int valread = read(sock, buffer, 1024);  
    std::vector<std::uint8_t> receivedData(buffer, buffer + valread);  
  
    // 反序列化数据为TLVMessage  
    TLVMessage receivedMessage = DeserializeTLVMessage(receivedData);  
  
    // 处理receivedMessage...  
  
    // 关闭socket  
    close(sock);  
    return 0;  
}

四、TLV协议在实际应用中的优势与注意事项

4.1 优势

灵活性:TLV协议允许在单个消息中灵活地包含多种类型的数据,每个TLV元素都是独立的,易于扩展和维护。
可扩展性:通过增加新的Type值,可以很容易地添加新的数据类型或功能,而无需修改现有数据的结构。
清晰性:每个TLV元素都明确指出了其类型和长度,这使得数据的解析变得简单明了。

4.2 注意事项

性能:由于每个TLV元素都包含Type和Length字段,这可能会增加消息的开销,特别是在包含大量小元素时。
对齐与填充:在序列化到某些类型的网络或存储设备时,可能需要考虑字节对齐和填充问题,以确保数据的正确性和效率。
错误处理:在反序列化过程中,必须严格检查Type和Length的有效性,以避免数据损坏或安全问题。

五、总结

本文详细讨论了如何使用C++实现基于Socket的通信,并设计了一个TLV协议用于数据交互。我们介绍了Socket编程的基本步骤,包括创建Socket、绑定、监听、接受连接、数据读写和关闭连接等。同时,我们定义了TLV协议的数据结构,并展示了如何将其序列化为字节流以便在网络中传输,以及如何在接收端反序列化这些数据。

六、TLV协议的实现细节

6.1 TLV数据结构定义

在C++中,我们可以定义一个简单的结构体或类来表示TLV(Type-Length-Value)元素:

#include <cstdint>  
#include <vector>  
  
struct TLVElement {  
    std::uint8_t type;  
    std::uint16_t length;  
    std::vector<std::uint8_t> value;  
  
    // 构造函数、赋值函数、比较函数等可以根据需要添加  
};  
  
// 假设我们有一个复合消息,由多个TLV元素组成  
struct TLVMessage {  
    std::vector<TLVElement> elements;  
  
    // 可以添加序列化和反序列化函数  
    std::vector<std::uint8_t> Serialize() const;  
    static TLVMessage Deserialize(const std::vector<std::uint8_t>& data);  
};

6.2 序列化函数

序列化函数负责将TLVMessage转换为字节流,以便通过网络发送:

std::vector<std::uint8_t> TLVMessage::Serialize() const {  
    std::vector<std::uint8_t> result;  
    for (const auto& element : elements) {  
        result.push_back(element.type);  
        result.push_back(static_cast<std::uint8_t>(element.length & 0xFF));  
        result.push_back(static_cast<std::uint8_t>(element.length >> 8));  
        result.insert(result.end(), element.value.begin(), element.value.end());  
    }  
    return result;  
}

注意:上面的length字段序列化方式假设了length是一个16位无符号整数,并使用了大端序(Big-Endian)进行传输。在实际应用中,你可能需要根据协议或平台的需求调整字节序。

6.3 反序列化函数

反序列化函数负责将接收到的字节流转换回TLVMessage对象:

TLVMessage TLVMessage::Deserialize(const std::vector<std::uint8_t>& data) {  
    TLVMessage message;  
    size_t index = 0;  
    while (index < data.size()) {  
        TLVElement element;  
        element.type = data[index++];  
  
        // 假设我们总是使用16位长度,并忽略字节序问题(这里应根据实际情况处理)  
        element.length = (static_cast<std::uint16_t>(data[index++]) << 8) | static_cast<std::uint16_t>(data[index++]);  
  
        if (index + element.length > data.size()) {  
            // 处理错误,例如数据截断  
            throw std::runtime_error("Invalid TLV data");  
        }  
  
        element.value.assign(data.begin() + index, data.begin() + index + element.length);  
        index += element.length;  
  
        message.elements.push_back(element);  
    }  
    return message;  
}

七、安全性与错误处理

错误处理:在序列化和反序列化过程中,必须添加适当的错误处理逻辑,以处理无效数据、数据截断等问题。
安全性:确保对接收到的数据进行充分的验证,防止潜在的缓冲区溢出、类型混淆等安全漏洞。
完整性校验:如果可能,可以添加消息完整性校验(如CRC、MD5、SHA等),以确保数据在传输过程中未被篡改。

八、结论

通过定义TLV协议并在C++中实现其序列化和反序列化,我们可以灵活地构建基于Socket的通信系统,该系统能够处理多种类型的数据并易于扩展。然而,在实际应用中,还需要注意性能优化、错误处理、安全性等方面的问题,以确保系统的稳定性和可靠性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1919587.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件架构之嵌入式系统设计

软件架构之嵌入式系统设计 第 12 章&#xff1a;嵌入式系统设计12.1 嵌入式系统概论12.2 嵌入式系统的组成12.2.1 硬件架构12.2.2 软件架构 12.3 嵌入式开发平台与调试环境12.3.1 嵌入式系统软件开发平台12.3.2 嵌入式开发调试 第 12 章&#xff1a;嵌入式系统设计 随着计算机…

# Redis 入门到精通(一)数据类型(3)

Redis 入门到精通&#xff08;一&#xff09;数据类型&#xff08;3&#xff09; 一、redis 数据类型–set 类型介绍与基本操作 1、set 类型 新的存储需求: 存储大量的数据&#xff0c;在查询方面提供更高的效率。需要的存储结构: 能够保存大量的数据&#xff0c;高效的内部…

MybatisPlus 核心功能

MybatisPlus 核心功能 文章目录 MybatisPlus 核心功能1. 条件构造器1.1 QueryWrapper1.2 LambdaQueryWrapper&#xff08;推荐&#xff09;1.3 UpdateWrapper1.4 LambdaUpdateWrapper 2. 自定义SQL3. Service接口 1. 条件构造器 当涉及到查询或修改语句时&#xff0c;MybatisP…

【C++】C++商店销售管理系统(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【C++】C++-机房收费管理系统(源码+注释)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【深度学习入门篇 ④ 】Pytorch实现手写数字识别

【&#x1f34a;易编橙&#xff1a;一个帮助编程小伙伴少走弯路的终身成长社群&#x1f34a;】 大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; ) &#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…

项目中日志采集实践:技术、工具与最佳实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一. 选择合适的日志框架 二. 配置日志框架 三. 使用…

既要“性价比”,又要“品价比”,零跑何以成为新能源“卷王”?

从前&#xff0c;“汽车界小米”“品价比”是围绕在零跑汽车周围的关键词。而在零跑C16上市发布会上&#xff0c;零跑汽车创始人朱江明又提出了“车圈优衣库”的概念&#xff0c;“我希望零跑在汽车行业的定位&#xff0c;就像优衣库在服装行业一样&#xff0c;追求品价比&…

看番工具 -- oneAnime v1.2.5绿色版

软件简介 OneAnime是一款专为动漫爱好者设计的应用程序&#xff0c;它提供了一个庞大的动漫资源库&#xff0c;用户可以在这里找到各种类型的动漫&#xff0c;包括热门的、经典的、新番的等等。OneAnime的界面设计简洁明了&#xff0c;操作方便&#xff0c;用户可以轻松地搜索…

智能小车——初步想法

需要参考轮趣的智能小车自己搭建一台智能机器人&#xff0c;这里从底层控制开始逐步搭建。 控制模式 之后要自行搭建智能小车&#xff0c;所以将轮趣的底盘代码进行学习&#xff0c;根据开发手册先大致过一遍需要的内容。 有做很多个控制方法&#xff0c;包括了手柄、串口、…

选择TPM管理咨询公司时需要考虑哪些因素?

在选择TPM&#xff08;全面生产维护&#xff09;管理咨询公司时&#xff0c;企业通常需要经过深思熟虑的过程&#xff0c;以确保所选择的咨询公司能够真正帮助企业实现生产效率和设备可靠性的提升。以下是在选择TPM管理咨询公司时需要考虑的关键因素&#xff1a; 一、行业经验和…

【Linux】Windows平台使用gdb调试FFmpeg源码

FFmpeg是一个跨平台的多媒体库&#xff0c;有时需要在别的平台上进行开发和调试&#xff0c;记录一下在linux环境下使用gdb来调试FFmpeg源码的基本方式 1.可执行文件 在windows平台使用linux环境来调试FFmpeg源码&#xff0c;需要编译生成一个后缀有_g的exe文件&#xff0c;参…

国漫推荐10

玄幻、恋爱 1.《两不疑》古风、恋爱 2.《中国古诗词动漫》 3.《武神主宰》 4.《百妖谱》 5.《灵剑尊》 6.《万界仙踪》 7.《万界神主》 8.《武庚纪》 9.《无上神帝》

Python实战:拥有设闹钟功能的可视化动态闹钟的实现

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

14 - matlab m_map地学绘图工具基础函数 - 一些数据转换函数(一)

14 - matlab m_map地学绘图工具基础函数 - 一些数据转换函数&#xff08;一&#xff09; 0. 引言1. 关于m_ll2xy和m_xy2ll2. 关于m_lldist3. 关于m_xydist4 关于m_fdist5 关于m_idist6. 总结 0. 引言 通过前面篇节已经将m_map绘图工具中大多绘图有关的函数进行过介绍&#xff0…

后仿真中《SDF反标必懂连载篇》之 反向提取SDF反标延迟

今天&#xff0c;整理一下最近工作中遇到的一个问题&#xff0c;及解决问题的办法&#xff0c;仅分享给大家。 我们知道&#xff0c;我们在完成SDF时序反标之后&#xff0c;首先要做的事情&#xff1a;检查sdfannotation 文件。文件中记录了每个sdf 文件&#xff0c;每个实例的…

【C语言】C语言-宾馆客房管理系统(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

20240711编译友善之臂的NanoPC-T6开发板的Buildroot

20240711编译友善之臂的NanoPC-T6开发板的Buildroot 2024/7/11 21:02 百度&#xff1a;nanopc t6 wiki https://wiki.friendlyelec.com/wiki/index.php/NanoPC-T6/zh NanoPC-T6/zh 4.4 安装系统 4.4.1 下载固件 4.4.1.1 官方固件 访问此处的下载地址下载固件文件 (位于网盘的&q…

可道云teamOS,用个人标签和公共标签,文件分类更多样

在信息爆炸的时代&#xff0c;我们每天都在与海量的数据和信息打交道。如何在这些纷繁复杂的信息中快速找到我们需要的&#xff0c;成为了摆在我们面前的一大难题。 为大家介绍一下可道云teamOS个人标签和公共标签功能&#xff0c;让信息的整理与搜索变得简单高效。 一、个人…

YOLOv10训练自己的数据集(交通标志检测)

YOLOv10训练自己的数据集&#xff08;交通标志检测&#xff09; 前言相关介绍前提条件实验环境安装环境项目地址LinuxWindows 使用YOLOv10训练自己的数据集进行交通标志检测准备数据进行训练进行预测进行验证 参考文献 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff…