“深入浅出”系列之C++:(3)网络编程

news2025/4/14 19:12:37

通过网络编程实现基于网络的应用程序,实现计算机之间的通信和数据交换。

  1. 套接字(Socket):套接字是网络编程中用于通信的一种抽象概念。在C++中使用<sys/socket.h>头文件中定义的套接字函数来创建和操作套接字。目的是开发采用套接字通信的C/S网络应用程序。           

1. 套接字是一个主机本地应用程序所创建的, 为操作系统所控制的接口 (“门”) .

2. 应用进程通过这个接口,使用传输层提供的服务, 跨网络发送(接收)报文到(从)其他应用进程。

client/server模式

通过Socket API,提供传输层的2类传输服务:

不可靠的数据报传输、可靠的字节流传输

1. TCP进行套接字编程

Socket: 应用进程和传输层协议(UCP or TCP)之间的门。

TCP服务: 从1个进程到另1个进程的字节流的可靠传输。   

Socket是从UNIX的I/O命令集发展而来的。Socket为上层实体提供一种透明的访问网络的能力,本质上说是传输层的服务原语。

TCP 中的套接字系统调用

​编辑​       客户必须初始联系服务器   服务器进程必须先运行

服务器进程必须创建套接字 (门) 来迎候客户的初始联系

客户如何初始联系服务器

创建客户本地TCP socket

指定服务器进程的IP地址, 端口号

一旦客户创建套接字, 客户TCP 就发起3次握手并建立与服务器 TCP的连接

一旦客户初始联系(敲门)服务器, 服务器TCP为服务器进程创建1个新的socket 与客户进程通信

允许服务器与多个客户通信

源端口号被用来区分客户

从应用程序的角度来看TCP 为客户和服务器提供了可靠的, 顺序的,字节流的传输 (“管道”) 。

2、IP地址和端口号:每个主机在网络上都有一个唯一的IP地址,用于标识主机。端口号是为了区分一个主机上的不同应用程序而存在的。在C++中,我们可以使用<arpa/inet.h>头文件中的函数来处理IP地址和端口号。

3、客户端和服务器:网络应用程序通常分为客户端和服务器端。客户端向服务器请求服务,而服务器则提供服务。客户端和服务器之间通过套接字进行通信。

使用C++进行网络编程

在C++中,可以使用Socket库来实现网络编程。以下展示了如何使用C++创建一个基于网络的应用程序:

#include <iostream>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <string>

#include <thread>

#include <vector>

// 客户端处理线程函数

void clientHandler(int clientSocket)

{

    // 接收客户端用户名

    char username[1024];

    recv(clientSocket, username, sizeof(username), 0);

    // 向其他客户端广播新用户加入信息

    std::string joinMessage = username;

    joinMessage += "加入了聊天室\n";

    std::cout << joinMessage;

    for (auto socket : connectedClients)

    {

        if (socket != clientSocket)

        {

            send(socket, joinMessage.c_str(), joinMessage.length(), 0);

        }

    }

    // 接收和转发消息

    while (true)

    {

        char buffer[1024] = {0};

        int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);

        if (bytesRead <= 0)

        {

            // 客户端断开连接

            close(clientSocket);

            // 向其他客户端广播用户离开信息

            std::string leaveMessage = username;

            leaveMessage += "离开了聊天室\n";

            std::cout << leaveMessage;

            for (auto socket : connectedClients)

            {

                if (socket != clientSocket)

                {

                    send(socket, leaveMessage.c_str(), leaveMessage.length(), 0);

                }

            }

            // 从已连接客户端列表中移除该客户端

            connectedClients.erase(std::find(connectedClients.begin(), connectedClients.end(), clientSocket));

            break;

        }

        // 向其他客户端广播消息

        std::string message = username;

        message += ": ";

        message += buffer;

        std::cout << message;

        for (auto socket : connectedClients)

        {

            if (socket != clientSocket)

            {

                send(socket, message.c_str(), message.length(), 0);

            }

        }

    }

}

int main()

{

    // 创建服务器套接字

    int serverSocket = socket(AF_INET, SOCK_STREAM, 0);

    if (serverSocket == -1)

    {

        std::cerr << "无法创建套接字" << std::endl;

        return -1;

    }

    // 定义服务器地址

    struct sockaddr_in serverAddress;

    serverAddress.sin_family = AF_INET;

    serverAddress.sin_port = htons(8080);

    serverAddress.sin_addr.s_addr = INADDR_ANY;

    // 绑定套接字到服务器地址

    if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) == -1)

    {

        std::cerr << "绑定套接字失败" << std::endl;

        return -1;

    }

    // 监听连接请求

    if (listen(serverSocket, SOMAXCONN) == -1)

    {

        std::cerr << "监听连接请求失败" << std::endl;

        return -1;

    }

    std::cout << "聊天室服务器已启动,等待客户端连接..." << std::endl;

    // 接受客户端连接并创建处理线程

    std::vector<std::thread> clientHandlers;

    while (true)

    {

        struct sockaddr_in clientAddress;

        socklen_t clientAddressSize = sizeof(clientAddress);

        int clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, &clientAddressSize);

        if (clientSocket == -1)

        {

            std::cerr << "无法接受客户端连接" << std::endl;

            return -1;

        }

        // 将客户端套接字添加到已连接客户端列表中

        connectedClients.push_back(clientSocket);

        // 创建客户端处理线程

        std::thread handlerThread(clientHandler, clientSocket);

        handlerThread.detach();

    }

    // 关闭服务器套接字

    close(serverSocket);

    return 0;

}

Boost:Beast库是一个作为 Boost 库一部分的 C++ 库,它提供了低级网络编程接口,专为HTTP和WebSocket设计。此库旨在让开发者能够高效地构建高性能的网络服务而无需过多关注底层细节。通过利用C++的现代特性,Beast使得在TCP上实现标准Web协议变得更加直观和强大。

1. 项目目录结构及介绍

Boost.Beast 的GitHub仓库大致结构如下:

目录解析:

docs : 提供详细的API文档和教程。

example : 实际可用的小型应用程序示例,帮助理解如何使用Beast进行编程。

include : 包含所有Beast库的头文件,是开发者日常工作中频繁访问的区域。

test : 包含单元测试,对于了解库的内部逻辑和功能覆盖度非常有用。

2. 项目的启动文件介绍

Boost.Beast本身不直接提供一个“启动文件”,因为它的使用依赖于嵌入到用户自己的应用程序中。然而,在example目录下,有多个入门级别的示例,如http.server.basic, http.client.sync等,这些可以视作“启动点”去学习如何开始编写基于Beast的应用。

例如,从http.server.basic开始,主要启动逻辑通常位于一个main函数内,该函数设置服务器监听端口,接收请求并响应。这通常涉及到创建Beast的核心对象,比如http::server类实例(虽然实际的类名可能有所不同,具体取决于你的实现细节)。

3. 项目的配置文件介绍

Boost.Beast作为一个库,并没有要求特定的配置文件。其配置主要通过编码方式实现,即在代码中设定参数、端口号、日志级别等。对于应用程序来说,如果需要配置Beast的行为,通常是自定义的配置逻辑,通过环境变量、命令行参数或外部配置文件(XML、JSON、YAML等)来读取,并在应用初始化阶段处理这些配置信息。

例如,如果你的应用需要动态指定服务器地址和端口,你会在代码里添加解析这类信息的逻辑,而不是Beast库直接支持的配置文件读取机制。

Boost.Asio是一个跨平台的C++库,用于网络和底层I/O编程,可以在I/O对象(如socket)上执行同步和异步操作。

该库可以让C++异步地处理数据,且平台独立。异步数据处理就是指,任务触发后不需要等待它们完成。 相反,Boost.Asio 会在任务完成时触发一个应用。 异步任务的主要优点在于,在等待任务完成时不需要阻塞应用程序,可以去执行其它任务。

异步任务的典型例子是网络应用。如果数据被发送出去了,比如发送至 Internet,通常需要知道数据是否发送成功。如果没有一个象 Boost.Asio这样的库,就必须对函数的返回值进行求值。但是,这样就要求待至所有数据发送完毕,并得到一个确认或是错误代码。而使用 Boost.Asio,这个过程被分为两个单独的步骤:第一步是作为一个异步任务开始数据传输。一旦传输完成,不论成功或是错误,应用程序都会在第二步中得到关于相应的结果通知。主要的区别在于,应用程序无需阻塞至传输完成,而可以在这段时间里执行其它操作。

I/O 服务与 I/O 对象

使用Boost.Asio进行异步数据处理的应用程序基于两个概念:I/O 服务和 I/O 对象。I/O 服务抽象了操作系统的接口,允许第一时间进行异步数据处理,而 I/O 对象则用于初始化特定的操作。鉴于Boost.Asio只提供了一个名为

boost::asio::io_service 的类作为 I/O 服务,它针对所支持的每一个操作系统都分别实现了优化的类,另外库中还包含了针对不同 I/O 对象的几个类。 其中,类 boost::asio::ip::tcp::socket 用于通过网络发送和接收数据,而类 boost::asio::deadline_timer 则提供了一个计时器,用于测量某个固定时间点到来或是一段指定的时长过去了。

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

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

相关文章

高等数学学习笔记 ☞ 一元函数微分的基础知识

1. 微分的定义 &#xff08;1&#xff09;定义&#xff1a;设函数在点的某领域内有定义&#xff0c;取附近的点&#xff0c;对应的函数值分别为和&#xff0c; 令&#xff0c;若可以表示成&#xff0c;则称函数在点是可微的。 【 若函数在点是可微的&#xff0c;则可以表达为】…

家用万兆网络实践:紧凑型家用服务器静音化改造(二)

大家好&#xff0c;这篇文章我们继续分享家里网络设备的万兆升级和静音改造经验&#xff0c;希望对有类似需求的朋友有所帮助。 写在前面 在上一篇《家用网络升级实践&#xff1a;低成本实现局部万兆&#xff08;一&#xff09;》中&#xff0c;我们留下了一些待解决的问题。…

[控制理论]—带死区的PID控制算法及仿真

带死区的PID控制算法及仿真 1.基本概念 在计算机控制系统重&#xff0c;某些系统为了避免控制作用过于频繁&#xff0c;消除由于频繁动作所引起的振荡&#xff0c;可采用带死区的PID控制算法&#xff1a; err(k) 为位置跟踪偏差&#xff0c;err0为可调参数&#xff0c;其具体…

(概率论)无偏估计

参考文章&#xff1a;(15 封私信 / 51 条消息) 什么是无偏估计&#xff1f; - 知乎 (zhihu.com) 首先&#xff0c;第一个回答中&#xff0c;马同学图解数学讲解得很形象&#xff0c; 我的概括是&#xff1a;“注意&#xff0c;有一个总体的均值u。然后&#xff0c;如果抽样n个&…

Visio 画阀门 符号 : 电动阀的画法

本篇文章介绍阀门&#xff0c;很多朋友在利用Visio绘画管道流程简图时&#xff0c;需要进行阀门符号的绘画&#xff0c;而Visio提供的阀门符号种类并不是很齐全。 本篇文章给出电动阀的画法&#xff1a; 下图是液动阀的符号&#xff1a; 首先&#xff0c;找到“更多形状”中的…

css出现边框

前言 正常情况下&#xff0c;开启 contenteditable 属性后会出现 “黑色边框”。 如下图所示&#xff0c;很影响美观&#xff1a; 您可能想去掉它&#xff0c;就像下面这样&#xff1a; 解决方案 通过选择器&#xff0c;将 focus 聚焦时移除 outline 属性即可。 如下代码所示&a…

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务&#xff0c;解决屏幕录制音乐播放等操作不被挂起 一、前言 1.后台是什么&#xff1f; 了解后台任务和长时任务前&#xff0c;我们需要先明白鸿蒙的后台特性&#xff1a;所谓的后台&#xff0c;指的是设备返回主界面、锁屏、…

【JVM】总结篇之垃圾回收★

文章目录 为什么需要GC垃圾回收算法★垃圾判断阶段引用计数算法可达性分析GC roots为什么会stop world?垃圾清除阶段标记-清除(Mark - Sweep)算法复制(Copying)算法标记-压缩(或标记-整理、Mark - Compact)算法分代收集算法增量收集(Incremental Collecting)算法分区算…

【Redis源码】 RedisObject结构体

【Redis源码】 RedisObject结构体 文章目录 【Redis源码】 RedisObject结构体概要1. redis object 由来2. 通过汇编代码分析3. 总结 概要 博主这里从redis object由来&#xff0c;和从底层内存分配角度进行讲解哦&#xff0c;小伙伴们自行选择读取 1. redis object 由来 ​ …

Ungoogled Chromium127 编译指南 MacOS 篇(二)- 项目要求

1. 引言 在开始编译 Ungoogled Chromium 之前&#xff0c;我们需要确保系统满足所有必要的硬件和软件要求。由于浏览器编译是一个资源密集型的任务&#xff0c;合适的硬件配置和完整的软件环境至关重要。本文将详细介绍编译 Ungoogled Chromium 所需的各项要求。 2. 硬件要求…

专家混合(MoE)大语言模型:免费的嵌入模型新宠

专家混合&#xff08;MoE&#xff09;大语言模型&#xff1a;免费的嵌入模型新宠 今天&#xff0c;我们深入探讨一种备受瞩目的架构——专家混合&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;大语言模型&#xff0c;它在嵌入模型领域展现出了独特的魅力。 一、M…

【Golang 面试题】每日 3 题(二十四)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

TCP Analysis Flags 之 TCP Retransmission

前言 默认情况下&#xff0c;Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态&#xff0c;并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时&#xff0c;会对每个 TCP 数据包进行一次分析&#xff0c;数据包按照它们在数据包列表中出现的顺序进行处理。可…

Docker 使用Dockerfile创建镜像

创建并且生成镜像 在当前目录下创建一个名为Dockerfile文件 vi Dockerfile填入下面配置 # 使用 CentOS 作为基础镜像 FROM centos:7# 设置工作目录 WORKDIR /app# 复制项目文件到容器中 COPY bin/ /app/bin/ COPY config/ /app/config/ COPY lib/ /app/lib/ COPY plugin/ /a…

Matlab 数据处理与可视化的多元拓展应用(具体代码分析)

一、代码整体功能概述&#xff1a; 该代码主要实现了以下几个功能&#xff1a; 从文件&#xff08;part1.txt&#xff09;中读取数据&#xff0c;并提取第二列数据&#xff0c;将其存储在 originalColumnData 中。对原始数据进行可视化&#xff0c;包括绘制置零前数据的折线图…

Oracle OCP考试常见问题之线上考试流程

首先要注意的是&#xff1a;虽然Oracle官方在国际上取消了获得OCP认证需要培训记录的要求&#xff0c;但在中国区&#xff0c;考生仍然需要参加Oracle的官方或者其合作伙伴组织的培训&#xff0c;并且由Oracle授权培训中心向Oracle提交学员培训记录。考生只有在完成培训并通过考…

第2章 市场走势的分类与组合

本章主要讨论市场中走势的分类与组合&#xff0c;从基本假设出发&#xff0c;对市场的走势状态进行分类&#xff0c;探讨不同的走势组合和走势组合分类&#xff0c;以深刻理解走势。 2.1 走势分类 根据第一章市场的基本假设三&#xff0c;走势包含无序运动状态&#xff08;混…

【Excel/WPS】根据平均值,生成两列/多列指定范围的随机数/随机凑出两列数据

原理就是通过随机生成函数和平均值函数。 适用场景&#xff1a;在总体打分后&#xff0c;需要在小项中随机生成小分数 第一列&#xff1a;固定的平均值A2第二列&#xff1a; RANDBETWEEN(A2-10,A210)第三列&#xff1a;根据第二列用平均值函数算除 A2*2-B2这是随机值1的公式&am…

STM32裸机开发转FreeRTOS教程

目录 1. 简介2. RTOS设置&#xff08;1&#xff09;分配内存&#xff08;2&#xff09;查看任务剩余空间&#xff08;3&#xff09;使用osDelay 3. 队列的使用&#xff08;1&#xff09;创建队列&#xff08;1&#xff09;直接传值和指针传值&#xff08;2&#xff09;发送/接收…

Golang的网络安全漏洞检测

Golang的网络安全漏洞检测 一、网络安全意识的重要性 在当今网络高度发达的环境下&#xff0c;网络安全问题变得异常突出。黑客利用各种手段对网络系统进行攻击&#xff0c;企图窃取数据、篡改信息、瘫痪服务等&#xff0c;因此网络安全成为全球关注的焦点。在这种环境下&#…