零基础入门:用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

int socket(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

int bind(int sockfd, const struct sockaddr *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

int listen(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

int accept(int sockfd, struct sockaddr *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

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

3. Sock

3.1 构造函数和析构函数

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

3.2 Socket() - 创建套接字

void Socket()
{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) - 绑定套接字

void Bind(uint16_t port)
{struct sockaddr_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_,(struct sockaddr *)&(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() - 开始监听

void Listen()
{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) - 接受连接

int Accept(std::string * clientip, uint16_t* clientport)
{struct sockaddr_in peer;socklen_t len  = sizeof(peer);bzero(&peer,len);int newfd = accept(sockfd_,(struct sockaddr*)&(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) - 连接服务器

bool Connect(const std::string& ip,const uint16_t& port)
{struct sockaddr_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_,(struct sockaddr*)&(peer),len);if(n < 0){lg(WARNING,"Connect error: %d,%s",errno,strerror(errno));return false;}return true;
}
  • 目的:客户端通过此函数连接到远程服务器,指定服务器的 IP 地址和端口。
    • inet_addr():将 IP 地址从字符串转换为网络字节序的二进制格式。
    • htons():将端口号转换为网络字节序。
  • 错误处理:如果 connect() 失败,记录警告日志并返回 false,否则返回 true 表示连接成功。

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

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

网络小组件链接

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

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

相关文章

SOES:软实现EtherCAT从站协议栈项目介绍及从站开发案例

在现代工业自动化领域&#xff0c;EtherCAT&#xff08;Ethernet for Control Automation Technology&#xff09;以其高速、实时和开放的特性&#xff0c;成为现场总线通信的主流协议之一。EtherCAT网络中&#xff0c;主站&#xff08;Master&#xff09;负责调度和管理&#…

[simdjson] 填充字符串 | `document` 对象 | on-demand 模式

第二章&#xff1a;填充字符串 在第一章解析器中&#xff0c;我们学习了simdjson::dom::parser和simdjson::ondemand::parser作为可复用内存的JSON解析工具。 本章将深入解析JSON数据输入的核心要求——“填充字符串”。 为何需要填充&#xff1f; simdjson通过SIMD&#x…

扭蛋机小程序开发:开启线上娱乐新风尚

在当今数字化浪潮席卷的时代&#xff0c;娱乐方式正经历着前所未有的变革。传统的扭蛋机&#xff0c;那充满惊喜与期待的实体装置&#xff0c;曾是无数人童年回忆中的欢乐源泉。如今&#xff0c;随着科技的飞速发展&#xff0c;扭蛋机小程序开发应运而生&#xff0c;将这份经典…

【React Native】布局和 Stack 、Slot

布局和Stack 点击链接后&#xff0c;页面切换时最好是有动画效果。页面一般都有头部&#xff0c;里面有页面的标题之类的东西。 在app目录里&#xff0c;新建一个_layout.js文件&#xff0c;这是项目的布局文件。 这个名字是固定的&#xff0c;前面必须有一个_ 。 布局的意…

3C电子产品蓝光三维扫描检测方案-中科米堆CASAIM

随着3C电子产品向轻薄化、精密化方向发展&#xff0c;传统的二维检测技术已难以满足现代制造业对产品精度的高标准要求。特别是在智能手机、平板电脑等消费电子领域&#xff0c;微小的结构偏差都可能导致产品组装困难或性能下降。当前行业内普遍面临检测效率低、数据采集不完整…

Docker 镜像原理

Union FS(联合文件系统) Union File System 是一种分层、轻量级并且高性能的文件系统&#xff0c;它支持对文件系统的修改作为一次提交来一层层的叠加&#xff0c;同时可以将不同目录挂载到同一个虚拟文件系统下。UnionFS 是一种为 Linux&#xff0c;FreeBSD 和 NetBSD 操作系统…

为什么IoTDB成为物联网场景的技术优选?

在物联网、工业监控等领域&#xff0c;时序数据的高效管理成为技术架构设计的关键环节。时序数据库作为专门处理带时间戳数据的系统&#xff0c;其选型需兼顾性能、兼容性与场景适配性。本文将从技术角度解析 IoTDB 的设计理念与实践方法&#xff0c;为时序数据库选型提供参考。…

js中的微任务和宏任务的理解

在JavaScript中&#xff0c;微任务&#xff08;Microtask&#xff09;和宏任务&#xff08;Macrotask&#xff09;是异步任务执行机制的重要组成部分&#xff0c;它们共同构成了JavaScript事件循环&#xff08;Event Loop&#xff09;的核心逻辑。理解这两个概念对于编写高性能…

Spring-AI系列-AI模型-Model

原文-知识库&#xff0c;欢迎大家评论互动 AI Model API Portable ModelAPI across AI providers for Chat, Text to Image, Audio Transcription, Text to Speech, and Embedding models. Both synchronous and stream API options are supported. Dropping down to access mo…

MySQL查询今天、昨天、上周、近30天、去年等的数据的方法

目录 常用的MySQL查询今天、昨天、上周、近30天、去年等数据的方法 0、Sql server中DateDiff()用法 1、MySQL的DATE_SUB()函数 定义和用法 语法 实例 2、MySQL的TO_DAYS(date) 3、MySQL的DATE() 函数 定义和用法 4、MySQL NOW() 函数 定义和用法 语法 实例 例子 …

Linux —— B / 基础开发工具

一、软件包管理器1.1什么是软件包1.2 Linux软件生态1.3 yum具体操作1.3.1 查看软件包1.3.2 安装软件1.3.3 卸载软件1.3.4 注意事项1.4 安装源二、编辑器Vim2-1 Linux编辑器-vim使用2-2 vim的基本概念2-3 vim的基本操作2-4 vim正常模式命令集2-5 vim末行模式命令集2-6 vim操作总…

SQL,在join中,on和where的区别

0.结论 两个表在&#xff0c;join时&#xff0c;首先做一个笛卡尔积&#xff0c;on后面的条件是对这个笛卡尔积做一个过滤形成一张临时表&#xff0c;如果没有where就直接返回结果&#xff0c;如果有where就对上一步的临时表再进行过滤。 先on&#xff0c;再join&#xff0c;再…

SD-WAN在储能网络中的应用,传统方案如何借力智能化升级?(附网络架构图)

一、储能网络的建设挑战在储能项目中&#xff0c;网络系统通常需要实现以下目标&#xff1a;高可靠性&#xff1a;实时采集和传输储能设备状态数据&#xff0c;链路中断可能导致系统故障。灵活扩展&#xff1a;分布式站点部署广泛&#xff0c;传统网络扩展需重新铺设线路&#…

Oracle11.2.0.4 RAC迁移升级Oracle19.3 RAC

问题描述 填写问题的基础信息。 系统名称 Oracle11.2.0.4迁移升级Oracle19.3 IP地址 操作系统 Centos7.5 数据库 Oracle11.2.0.4迁移升级Oracle19.3 症状表现 问题的症状表现如下 需要将单机的Oracle11.2.0.4环境升级到Oracle19.3.0RAC环境&#xff0c;采用迁移升级的…

SAP-ABAP:SAP的‘cl_http_utility=>escape_url‘对URL进行安全编码方法详解

SAP的’cl_http_utility>escape_url’对URL进行安全编码方法详解 核心作用&#xff1a;对 URL 进行安全编码&#xff0c;将特殊字符转换为 %XX 格式&#xff0c;确保符合 HTTP 传输规范。1. 功能与作用 ✅ URL 安全编码 将非安全字符转换为十六进制 ASCII 码&#xff08;%XX…

基于HarmonyOS的智能灯光控制系统设计:从定时触发到动作联动全流程实战

摘要 随着智能家居的快速普及&#xff0c;人们对居住环境的智能化需求越来越高&#xff0c;其中智能灯光控制是最基础、也是最常用的功能之一。从最初的远程控制发展到如今能“感知环境、自动响应”的智能灯光系统&#xff0c;背后依赖的是强大的系统联动能力。鸿蒙系统作为面向…

ROS1/Linux——linux虚拟机主ip地址:网络信息不可用

ROS1/Linux——linux虚拟机主ip地址&#xff1a;网络信息不可用 文章目录ROS1/Linux——linux虚拟机主ip地址&#xff1a;网络信息不可用参考亿点链接问题描述最终解决方案参考亿点链接 Unable to fetch some archives, maybe run apt-get update or try with –fix-missingli…

ssl相关命令生成证书

当前环境 OpenSSL 3.5.1 1 Jul 2025 (Library: OpenSSL 3.5.1 1 Jul 2025) GmSSL 3.1.2 Dev 本地gmssl命令 #生成证书公私钥对 gmssl sm2keygen -pass 1234 -out sm2.key -pubout sm2pub.pem #使用certgen命令生成自签名证书cert.crt gmssl certgen -C CN -ST Beijing -L Ha…

TensorFlow深度学习实战——DCGAN详解与实现

TensorFlow深度学习实战——DCGAN详解与实现0. 前言1. DCGAN 架构2. 构建 DCGAN 生成手写数字图像2.1 生成器与判别器架构2.2 构建 DCGAN相关链接0. 前言 深度卷积生成对抗网络 (Deep Convolutional Generative Adversarial Network, DCGAN) 是一种基于生成对抗网络 (Generati…

SpringBoot 使用MyBatisPlus

引入依赖<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.3.0</version> </dependency>写一个interface 继承basemapMapper public in…