零基础入门:用C++从零实现TCP Socket网络小工具

零基础入门:用C++从零实现TCP Socket网络小工具

个人主页:chian-ocean

文章专栏-Linux

前言:

网络编程中的套接字(Socket)是通信的基本接口,允许不同计算机之间通过网络交换数据。套接字是计算机网络中通信的“端点”,通过它,应用程序可以与网络中的其他计算机进行数据通信。网络套接字接口提供了一种抽象的、平台无关的方式来进行进程间通信(IPC)或网络通信。
在这里插入图片描述

网络套接字接口

头文件

  • 编写网络的常用的4个头文件,基本常用的函数都在这4个头文件里面。
#include<sys/types.h>// 包含各种系统数据类型#include<sys/socket.h>// 包含套接字操作相关函数和常量#include<arpa/inet.h>// 包含与Internet地址转换相关的函数#include<netinet/in.h>// 定义与网络字节序及IPv4/IPv6地址相关的结构体和常量

接口

socket

socket() 函数是创建网络通信套接字的基础。它用于创建一个套接字(socket)并返回一个套接字描述符(socket descriptor),这个描述符将被用来进行后续的网络通信(例如发送和接收数据)。

image-20250718221207038
intsocket(int domain,int type,int protocol);

参数说明

  1. domain(地址族):指定通信使用的协议族。
    • 常用值:
      • AF_INET:IPv4 地址族(用于 TCP/IP 通信)。
      • AF_INET6:IPv6 地址族。
      • AF_UNIX:本地通信,适用于 Unix 域套接字。
  2. type(套接字类型):指定套接字的类型,决定数据传输的方式。
    • 常用值:
      • SOCK_STREAM:流套接字(用于 TCP)。
      • SOCK_DGRAM:数据报套接字(用于 UDP)。
      • SOCK_RAW:原始套接字,用于底层协议。
  3. protocol(协议):指定使用的具体协议,通常设置为 0 让系统自动选择协议。
    • 常用值:
      • 0:自动选择合适的协议。
      • IPPROTO_TCP:用于 TCP。
      • IPPROTO_UDP:用于 UDP。

返回值

  • 成功时,socket() 返回一个 非负整数,这是一个套接字描述符,代表这个套接字。该描述符将用于后续的套接字操作(如绑定、连接、发送数据等)。
  • 失败时,返回 -1,并且设置全局变量 errno 来指示错误类型。

bind()

image-20250718221211415
intbind(int sockfd,conststructsockaddr*addr, socklen_t addrlen);

参数说明

  1. sockfd(套接字描述符)
    • 类型int
    • 描述:这是通过 socket() 创建的套接字描述符。客户端使用该套接字发起连接请求。
    • 说明:该套接字应该是已经创建并且可以进行连接的有效套接字。
  2. addr(目标地址)
    • 类型struct sockaddr *
    • 描述:指向一个 struct sockaddr 结构体的指针,包含了服务器的地址(IP 地址和端口号)。
    • 说明:具体的结构类型通常为 struct sockaddr_in(用于 IPv4 地址)或者 struct sockaddr_in6(用于 IPv6 地址)。这个结构体包含了目标服务器的 IP 地址和端口号。
  3. addrlen(地址长度)
    • 类型socklen_t
    • 描述:指定目标地址结构体的大小(字节数)。
    • 说明:通常设置为 sizeof(struct sockaddr_in)sizeof(struct sockaddr_in6),用于告诉 connect() 函数地址结构的实际长度。

返回值

  • 成功时:返回 0,表示成功将套接字与指定的本地地址绑定。
  • 失败时:返回 -1,并将 errno 设置为具体的错误码。

listen()

image-20250718221214485
intlisten(int sockfd,int backlog);

参数说明

  1. sockfd
    • 类型int
    • 描述:表示要进入监听状态的套接字描述符。这个套接字通常是通过 socket() 创建的,并且应该已经通过 bind() 绑定了本地地址(如 IP 地址和端口)。
    • 说明:套接字需要是一个有效的连接套接字,用于接受客户端连接。
  2. backlog
    • 类型int
    • 描述:表示 监听队列的最大长度,也就是可以等待的连接请求数量。如果有多个客户端同时请求连接,系统会将这些请求放入队列中,backlog 参数设置了队列的最大长度。
    • 说明:如果有超过 backlog 数量的连接请求,新的连接请求会被拒绝,或者它们会根据操作系统的实现策略被丢弃。
    • 推荐值:常见的 backlog 值一般设置为 5 到 128,根据服务器的需求而定。对于高并发系统,可能需要更大的 backlog 值。

返回值

  • 成功时:返回 0,表示成功将套接字转换为监听状态。
  • 失败时:返回 -1,并设置 errno 以指示错误原因。

accept()

image-20250718221217649
intaccept(int sockfd,structsockaddr*addr, socklen_t *addrlen);

参数说明

  1. sockfd(套接字描述符)
    • 类型int
    • 描述:这是通过 socket() 创建的套接字描述符。客户端使用该套接字发起连接请求。
    • 说明:该套接字应该是已经创建并且可以进行连接的有效套接字。
  2. addr(目标地址)
    • 类型struct sockaddr *
    • 描述:指向一个 struct sockaddr 结构体的指针,包含了服务器的地址(IP 地址和端口号)。
    • 说明:具体的结构类型通常为 struct sockaddr_in(用于 IPv4 地址)或者 struct sockaddr_in6(用于 IPv6 地址)。这个结构体包含了目标服务器的 IP 地址和端口号。
  3. addrlen(地址长度)
    • 类型socklen_t
    • 描述:指定目标地址结构体的大小(字节数)。
    • 说明:通常设置为 sizeof(struct sockaddr_in)sizeof(struct sockaddr_in6),用于告诉 connect() 函数地址结构的实际长度。

返回值

  • 成功时:返回 新的套接字描述符,用于与客户端进行通信。这个新的套接字是通过 accept() 函数创建的,它与原始的监听套接字不同,可以用于数据发送和接收。
  • 失败时:返回 -1,并设置 errno 以指示错误原因。

connect()

image-20250718221220747

参数说明

  1. sockfd(套接字描述符)
    • 类型int
    • 描述:这是通过 socket() 创建的套接字描述符。客户端使用该套接字发起连接请求。
    • 说明:该套接字应该是已经创建并且可以进行连接的有效套接字。
  2. addr(目标地址)
    • 类型struct sockaddr *
    • 描述:指向一个 struct sockaddr 结构体的指针,包含了服务器的地址(IP 地址和端口号)。
    • 说明:具体的结构类型通常为 struct sockaddr_in(用于 IPv4 地址)或者 struct sockaddr_in6(用于 IPv6 地址)。这个结构体包含了目标服务器的 IP 地址和端口号。
  3. addrlen(地址长度)
    • 类型socklen_t
    • 描述:指定目标地址结构体的大小(字节数)。
    • 说明:通常设置为 sizeof(struct sockaddr_in)sizeof(struct sockaddr_in6),用于告诉 connect() 函数地址结构的实际长度。

返回值

  • 成功时:返回 0,表示连接成功。
  • 失败时:返回 -1,并且会设置 errno 来指示错误的具体原因。

close()

image-20250718221223631
intclose(int fd);

参数说明

  1. fd(文件描述符):
    • 类型int
    • 描述:这是要关闭的文件描述符。对于套接字编程而言,这通常是由 socket() 函数返回的套接字描述符(sockfd)。
    • 说明:在网络编程中,fd 是表示套接字的描述符,它可以是通过 socket() 创建的套接字描述符。关闭该描述符会释放套接字占用的资源。

返回值

  • 成功时:返回 0,表示成功关闭套接字或文件描述符。
  • 失败时:返回 -1,并设置 errno 为具体的错误代码

网络套接字封装(TCP)

1. 头文件引用

#include<iostream>#include<string>#include<cstring>#include<strings.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include"log.hpp"
  • <iostream>:提供输入输出流的功能,常用于打印日志或错误信息。
  • <string>:提供 C++ 标准库的字符串类 std::string,用于字符串处理。
  • <cstring><strings.h>:用于处理字符串相关的操作,如 bzero()strerror()
  • <sys/types.h><sys/socket.h>:提供套接字编程所需的类型定义和系统调用。
  • <arpa/inet.h>:提供与 IP 地址转换相关的函数(如 inet_ntop()inet_addr())。
  • <netinet/in.h>:定义了用于 IPv4 地址和端口的结构体和常量(如 sockaddr_inhtons())。
  • "log.hpp":这是一个自定义的日志头文件,包含了日志记录相关的内容。lg() 函数用于记录日志,lg() 宏应该在 log.hpp 中定义。

2. 全局变量和枚举类型

int backlog =10;enumerr{ Socketerr =1, Bindeterr, Listeneterr, Accepteterr,};
  • backlog:这是传递给 listen() 函数的参数,定义了监听队列的最大长度(即最大客户端连接数)。设置为 10
  • enum err:定义了与套接字相关的错误类型。
    • Socketerr = 1:表示套接字创建失败。
    • Bindeterr:表示套接字绑定失败。
    • Listeneterr:表示监听失败。
    • Accepteterr:表示接受客户端连接失败。

3. Sock

3.1 构造函数和析构函数

Sock(){}~Sock(){}
  • 构造函数:默认构造函数,没有进行任何初始化操作。
  • 析构函数:默认析构函数,没有执行任何资源清理操作。

3.2 Socket() - 创建套接字

voidSocket(){ sockfd_ =socket(AF_INET, SOCK_STREAM,0);if(sockfd_ <0){lg(FATAL,"Socket error: %d,%s",errno,strerror(errno));exit(Socketerr);}}
  • 目的:创建一个 TCP 套接字。
    • AF_INET:表示 IPv4 地址族。
    • SOCK_STREAM:表示 TCP 流套接字(面向连接的套接字)。
    • 0:表示默认协议,通常是 TCP 协议。
  • 错误处理:如果 socket() 返回值小于 0,表示套接字创建失败,记录日志并退出程序,退出代码为 Socketerr

3.3 Bind(uint16_t port) - 绑定套接字

voidBind(uint16_t port){structsockaddr_in peer; socklen_t len =sizeof(peer);bzero(&peer,len); peer.sin_port =htons(port); peer.sin_family = AF_INET; peer.sin_addr.s_addr = INADDR_ANY;if(bind(sockfd_,(structsockaddr*)&(peer),len)<0){lg(FATAL,"Bind error: %d,%s",errno,strerror(errno));exit(Bindeterr);}}
  • 目的:将套接字与本地 IP 地址和端口号绑定。通过 INADDR_ANY 将套接字绑定到所有可用的网络接口上,接受来自任何 IP 地址的连接。
    • htons():将端口号从主机字节序转换为网络字节序。
  • 错误处理:如果 bind() 返回值小于 0,表示绑定失败,记录日志并退出程序,退出代码为 Bindeterr

3.4 Listen() - 开始监听

voidListen(){if(listen(sockfd_, backlog)<0){lg(FATAL,"Listen error: %d,%s",errno,strerror(errno));exit(Listeneterr);}}
  • 目的:将套接字设置为监听状态,准备接受客户端的连接。
    • backlog:监听队列的最大长度,定义最多能排队等待的连接数。
  • 错误处理:如果 listen() 返回值小于 0,表示监听失败,记录日志并退出程序,退出代码为 Listeneterr

3.5 Accept(std::string \* clientip, uint16_t\* clientport) - 接受连接

intAccept(std::string * clientip,uint16_t* clientport){structsockaddr_in peer; socklen_t len =sizeof(peer);bzero(&peer,len);int newfd =accept(sockfd_,(structsockaddr*)&(peer),&len);if(newfd <0){lg(FATAL,"Accept error: %d,%s",errno,strerror(errno));exit(Accepteterr);}char ip[64];inet_ntop(AF_INET,&peer.sin_addr.s_addr,ip,sizeof(ip));*clientip = ip;*clientport =ntohs(peer.sin_port);return newfd;}
  • 目的:接受来自客户端的连接请求,并返回一个新的套接字用于与客户端的通信。
    • accept() 函数返回一个新的套接字 newfd,用于与客户端交换数据。
    • 通过 inet_ntop() 将客户端的 IP 地址从二进制转换为字符串格式,ntohs() 将客户端的端口号转换为主机字节序。
  • 错误处理:如果 accept() 返回值小于 0,表示接受连接失败,记录日志并退出程序,退出代码为 Accepteterr

3.6 Connect(const std::string& ip, const uint16_t& port) - 连接服务器

boolConnect(const std::string& ip,constuint16_t& port){structsockaddr_in peer; socklen_t len =sizeof(peer);bzero(&peer,len); peer.sin_addr.s_addr =inet_addr(ip.c_str()); peer.sin_port =htons(port); peer.sin_family = AF_INET;int n =connect(sockfd_,(structsockaddr*)&(peer),len);if(n <0){lg(WARNING,"Connect error: %d,%s",errno,strerror(errno));returnfalse;}returntrue;}
  • 目的:客户端通过此函数连接到远程服务器,指定服务器的 IP 地址和端口。
    • inet_addr():将 IP 地址从字符串转换为网络字节序的二进制格式。
    • htons():将端口号转换为网络字节序。
  • 错误处理:如果 connect() 失败,记录警告日志并返回 false,否则返回 true 表示连接成功。

3.7 GetFd() - 获取套接字描述符

intGetFd(){return sockfd_;}
  • 目的:返回套接字描述符,便于外部访问该套接字,用于进一步的操作(如 send()recv() 等)。

网络小组件链接

Read more

【数学建模】用代码搞定无人机烟幕:怎么挡导弹最久?

【数学建模】用代码搞定无人机烟幕:怎么挡导弹最久?

前言:欢迎各位光临本博客,这里小编带你直接手撕**,文章并不复杂,愿诸君耐其心性,忘却杂尘,道有所长!!!! **🔥个人主页:IF’Maxue-ZEEKLOG博客 🎬作者简介:C++研发方向学习者 📖**个人专栏: 《C语言》 《C++深度学习》 《Linux》 《数据结构》 《数学建模》** ⭐️人生格言:生活是默默的坚持,毅力是永久的享受。不破不立,远方请直行! 文章目录 * 一、先搞懂:我们要解决啥问题? * 二、核心计算:代码怎么判断“烟幕有没有用”? * 1. 先算单个烟幕的“有效时间段” * 2. 合并重叠的时间段(避免重复计算) * 3. 只算“导弹到达前”的有效时间 * 三、代码优化:加了2个实用功能,结果直接看 * 1. 跑完直接显示“最优遮蔽时长”

By Ne0inhk
1200PLC与爱普生机器人modbus_TCP通讯

1200PLC与爱普生机器人modbus_TCP通讯

1.前言 首先申明一下我的硬件信息 机器人:C4-A601S 控制器:RC700 PLC:西门子S7-1200(CPU:1217C/DC/DC/DC) 2.控制器IP地址查看及修改 在配置控制器相关信息时需要先用网线连接PC与机器人控制器连接,爱普生机器人出厂设定网址为192.168.0.1(我这里是之前修改过了) 若默认没有显示以太网连接,点击右侧的增加,选择“通过以太网连接到控制器”后点击确定 如果控制器网址被修改过了,不知道是多少,可以用一根PC线,一头接在控制器的“开发用PC连接专用USB端口”另一头接在电脑USB口 这时候再在通讯处选择USB连接就可以通上了 现在就可以在“系统配置”处看到控制器的IP地址以及相关信息了,如果有需要也可以直接在这修改IP地址。 3.机器人控制器配置 网线连接好后开始配置通讯相关信息 1.控制设备 控制设备修改为远程I/O 2.现场总线 现场总线类型修改为“Modbus TCP”

By Ne0inhk

养龙虾-------【多openclaw 对接飞书多应用】---多个大龙虾机器人群聊

🚀 MiniMax Token Plan 惊喜上线!新增语音、音乐、视频和图片生成权益。邀请好友享双重好礼,助力开发体验! 好友立享 9折 专属优惠 + Builder 权益,你赢返利 + 社区特权! 👉 立即参与:https://platform.minimaxi.com/subscribe/token-plan?code=2NMAwoNLlZ&source=link 最近玩了下大龙虾,对接飞书后玩的不亦乐乎,妥妥滴私人助理。但是也萌发一个想法,多个机器人可以自己聊天吗?那会不会把世界给聊翻了。于是我马上搜寻各个配置方式,却是找到了可以配置多个机器人得群聊方式。 1.首先创建多个应用添加机器人,分别和部署得多个openclaw系统对接具体对接参考我写的【 养龙虾-------【openclaw 对接飞书、钉钉、微信 】—移动AI助理】 2.手工拉群并添加机器人: 3.把群id配置进各个龙虾配置文件里面 接下来就可以群聊了

By Ne0inhk
WorkBuddy 使用指南:从零开始配置 QQ 机器人,解锁桌面智能体新玩法

WorkBuddy 使用指南:从零开始配置 QQ 机器人,解锁桌面智能体新玩法

文章目录 * 前言 * 下载 WorkBuddy * 认识 WorkBuddy * 插件类型 * 配置 QQ 机器人 * 登录 QQ 开放平台并注册激活账号 * 配置超级管理员、主体及认证信息 * 创建 QQ 机器人 * 获取 AppID 和 AppSecret * 从 Claw 中获取 Webhook * 在 QQ 开发平台配置回调地址 * 开始使用 WorkBuddy Claw * 总结 前言 在大家还在沉迷于如何搭建 OpenClaw 的时候,腾讯竟然悄悄公测了 WorkBuddy。这是一款面向全角色的桌面智能体,下达指令即可自动生成文档、表格、图表及 PPT 等可视化成果,能够自主规划并交付多模态复杂任务结果,支持多 Agents 并行工作,极致提效,

By Ne0inhk