C++ Webserver从零开始:基础知识(一)——Linux网络编程基础API

news2024/11/17 11:36:07

前言     

        本专栏将从零开始制作一个C++ Webserver,用以记录笔者学习的过程

        如果你想要跟着我这个专栏制作一个C++ Webserver,你需要掌握以下前置基础课程知识:

                1.C/C++的语法(在Leetcode刷100~200题的程度即可)

                2.计算机网络基础知识

                3.操作系统基础知识

        掌握以上前置课程知识后,即可开始本专栏的内容


一.socket地址API

       1.主机字节序和网络字节序

        我们知道,一个32位计算机的CPU累加器一次能累加4字节的数据,而这4字节的数据在内存中排列的顺序是可以有2种方式的,即大端字节序和小端字节序

        大端字节序:一个整数的高位字节(23~31bit)存储在内存的低地址处

        小端字节序:一个整数的低位字节(0 ~ 7 bit)存储在内存的低地址处

           

        由于不同的主机使用的字节序可能不同,因此两个主机之间发送数据可能会发生错误。解决方法是:

        人们制定一个规范:发送端统一使用大端字节序(因此大端字节序称为网络字节序

        而因为现代PC大多采用小端字节序,因此小端字节序称为主机字节序

        

Linux中实现主机字节序和网络字节序的函数:

#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

        他们的作用可以通过名字理解,比如 htonl :" host to network long" ,即长整型的主机字节序转换成网络字节序。这四个函数中long类型的函数一般用来转换IP地址,short类型的一般用来转换端口号(操作系统基础知识)。


        2.通用socket地址

        主机之间的通信需要知晓对方的地址,而网络中主机的地址是TCP/IP协议族来定义的(计算机网络基础知识),在Linux网络编程中,我们通过使用socket的这个套接字来进行网络通信。socket定义了一系列的API实现网络通信,是非常方便好用的工具。

        在socket网络编程中表示地址的是结构体sockaddr,但由于这个结构体的设计问题,无法容纳多数协议族的地址值,因此Linux定义了一个新的通用socket地址结构体

#include<bits/socket.h>
struct sockaddr_storge
{
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char__ss_padding[128-sizeof(__ss_align)];
}

     


        3.专用socket地址

        以上两种通用socket地址其实并不好用,所以Linux为各个协议族提供了专门的socket地址结构体

        其中,UNIX本地协议族使用sockaddr_un,本文不予说明

        而TCP/IP协议族使用sockaddr_in和sockaddr_in6两个分别对应IPv4和IPv6

        

         但需要注意的是,使用sockaddr_in或其他专用socket地址(包括socket_storge),最后都要强制转换成通用socket地址类型sockaddr,这是因为所有socket编程接口使用的地址参数的类型都是sockaddr


二.创建socket

        我们了解socket地址API后,要如何创建一个socket呢?下面是创建socket的代码

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type, int protocol);

int socket(int domain,int type,int protocol);

  • 功能:创建一个套接字
  • 参数:
    • domain:协议族
      • AF_INET:IPv4
      • AF_INET6:IPv6
      • AF_UNIX,AF_LOCAL:本地套接字通信
    • type:通信过程中使用的协议类型
      • SOCK_STEAM:流式协议(传输层使用TCP协议)
      • SOCK_DGRAM:报式协议(传输层使用UDP协议)
    • protoco:具体的一个协议,写 0 就行
  • 返回值:返回文件描述符,操作内核缓冲区
    • 失败:-1

        以上块引用中,在写Webserver时都是用笔者加粗部分的参数

另外:

        type参数中还可以与以下两个参数相与计算:

                SOCK_NONBLOCK:创建的socket为非阻塞

                SOCK_CLOEXEC:用fork调用创建子进程时,在子进程中关闭该socket


三.绑定socket(命名socket)

        在创建socket时,我们在第一个参数时给它指定了协议族,但是并未指定使用协议族中哪个具体的socket地址。所以我们用系统调用bind来给socket绑定地址。

        PS:服务端需要绑定,客户端不需要绑定,客户端采用匿名绑定,操作系统会代劳。

        

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);
  • 功能:将my_addr所指的socket地址分配给未命名的socket文件描述符
  • 参数
    • sockfd:通过socket函数得到文件描述符
    • addr:需要绑定的socket地址,这个地址封装了ip和端口号的信息
    • addrlen:第二个参数结构体占的内存大小
  • 返回值
    • 0:成功
    • -1:失败


四.监听socket

        现在我们创建好了socket,也为其分配好了socket地址,接下它就可以工作进行主机间的通信了吗?其实并不行,我们还需要为他创建一个监听队列,用以存储待处理的客户连接。

        以下是创建监听队列的系统调用

        

#include<sys/socket.h>
int lsiten(int sockfd,int backlog);

int listen(int sockfd,int backlog);

  • 功能:监听socket上的连接
  • 参数
    • sockfd:通过socket()函数得到文件描述符
    • backlog:未连接的和已经连接的和的最大值
  • 返回值:
    • 成功:0
    • 失败:-1


五.接受连接(服务端)

        现在我们已经有了一个监听socket(执行过listen调用,处于LISTEN状态的socket),我们终于可以进行通信啦!而最后一步,就是将监听队列中的一个socket取出来,即可与远端的主机进行读写交互了

        下面是从监听队列取出socket的系统调用

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* addr,socklen_t* addrlen);
  • 功能:接受客户端连接,默认时一个阻塞的函数,阻塞等待客户端连接
  • 参数
    • sockfd:用于监听的文件描述符
    • addr:传出参数,记录连接成功后客户端的地址信息
    • addrlen:指定第二个参数的对应的内存大小
  • 返回值:
    • 成功:返回用于通信的文件描述符
    • 失败:-1


六.发起连接(客户端)

        注意,上文的绑定socket,监听socket和接受连接都是服务端要干的事,接下来讲客户端的任务。在服务端创建好了监听队列后,就可以接受来自客户端的连接请求,那么客户端是怎么发起连接请求的呢?

        

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr* serv_addr,socklen_t addr_len);
  • 功能:客户端连接服务器
  • 参数
    • sockfd:用于通信的文件描述符
    • serv_addr:客户端要连接的服务器地址信息
    • addrlen:指定第二个参数的对应的内存大小
  • 返回值:
    • 成功:0
    • 失败:-1


七.关闭连接

        通信完成后,如要关闭连接,可以通过下面的系统调用

#include<unistd.h>
int close(int fd);

        fd参数是待关闭的socket,close系统调用不会立刻关闭一个连接,而是将fd的引用计数-1,只有当其引用计数为0时才会真正关闭连接(类似C++的智能指针)。

        在多进程程序中,一次fork系统调用默认将引用计数+1.

        如果你非要立即终止连接,也有办法,即shutdown系统调用,读者可以自行搜索。


八.数据读写

        我们在二到七的过程中完整经历了socket通信的创建,命名,监听,接受(发起),关闭的过程,在连接建立成功到关闭连接的这个时间段中我们就可以进行两个主机之间的通信。

        通信的方式即:

                1.发送信息

                2.接受信息

        专业的说法也就是数据读写。Linux本身对文件的读写也可以用于socket,因为socket本身也是文件(Linux中万物皆文件)。但socket还是提供了几种好用的数据读写系统调用。

          分别有

                1.TCP数据读写

                2.UDP数据读写

                3.通用数据读写

           本文只介绍TCP数据读写,UDP和通用数据读写请读者自行学习

        

#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd,void* buf,size_t len,int flags);
ssize_t send(int sockfd,const void* buf,size_t len,int flags);
  • 功能:数据读写
  • 参数:
    • sockfd:用于通信的文件描述符
    • buf: 缓冲区的位置
    • len:缓冲区的大小
    • flags:通常取0,其他含义自行搜素,可以进行具体的控制
  • 返回值:
    • 成功:
      • recv:返回实际读到的数据的长度,如果小于期望长度len就多调用几次recv
      • send:实际写入的数据长度
    • 失败:-1


九.一些废话

        前面一到八即Linux网络编程基础API的常用内容了,其他的一些不常用的API如地址信息函数,socket选项等没有写入文中,一是因为其使用场景较少,二是我对这个专栏的定位是:

        简洁且重要

        我写的内容基本上是完成C++ Webserver所必须掌握的前置知识,所以会极可能的少,这样的话对一些没有基础却又无从下手的后辈能起到一个引入门的作用。

        等真正地了解了这些内容后,再去提升应该会容易的多。之所以这也想是因为我在学习完C/C++,操作系统,计算机网络之后进行项目制作时发现根本无从下手,我在网上找到的内容很少有建立在你完全不了解任何Webserver的知识,但又学完了基础课程的情况下。所以我希望能写一些内容,对和我一样的同学有些帮助。

        另外就是我也是在学习过程中,所以想记录一下学习过程帮自己更好地完成这个目标。如果本文有什么问题,或者你有什么建议的话欢迎私信笔者,我看到了应该都会回复。
                

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

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

相关文章

WSL2-Ubuntu20.04-配置

WSL2-Ubuntu20.04-配置 安装wsl2安装Ubuntu20.04安装anacondaWSL2可视化&#xff08;VcXsrv&#xff09; 安装wsl2 wsl --install wsl -l -v # 版本查看 默认的都是 wsl2 &#xff08;如果是wsl1 就自行升级 wsl --update&#xff09; 官方教程 安装Ubuntu20.04 安装wsl2之后…

CRLF漏洞靶场记录

搭建 利用 docker 搭建 vulhub 靶场 git clone https://github.com/vulhub/vulhub.git 进入 /vulhub/nginx/insecure-configuration 目录 启动前关闭现有的 8080、8081、8082 端口服务&#xff0c;避免端口占用 docker-compose up -d 进入容器 docker exec -it insecure-…

char常见问题之一【C语言】

引出 在所写的代码中&#xff1a; char ch0 "asd";报错&#xff1a;因为char类型的变量只能存储一个字符&#xff0c;不能存储字符串 char ch1a;正确 char ch2"a";报错&#xff1a;因为&#xff0c;虽然a是一个字符&#xff0c;但是用了双引号&#xf…

视频号下载小助手:教你微信视频号怎么提取视频出来

作为一名剪辑师或自由职业者,我们作为短视频创作者有时候需要下载多个视频用于制作多个解说系列的视频或者连续剧。然而,下载这些视频通常需要花费大量时间和精力,尤其是在没有合适的工具的情况下&#xff0c;让我们制作视频也确实困难&#xff0c;那么我们该如何解决呢&#x…

《产业结构调整指导目录(2024年本)》发布,模糊测试首次纳入

近日&#xff0c;第6次委务会议通过了新版的《产业结构调整指导目录&#xff08;2024年本&#xff09;》&#xff0c;该目录自2024年2月1日起正式实施。 与之前的版本相比&#xff0c;本次目录在行业设置上进行了全面升级&#xff0c;新增了“网络安全”这一重要行业大类&#…

Pytest插件pytest-cov:优雅管理测试覆盖率

在软件开发中&#xff0c;测试覆盖率是评估测试质量的关键指标之一。为了更方便地统计和管理测试覆盖率&#xff0c;Pytest插件"pytest-cov"应运而生。本文将介绍"pytest-cov"的基本用法和优雅管理测试覆盖率的方法。 什么是pytest-cov? pytest-cov 是Pyt…

Css样式制作图形倒影

该CSS样式是WebKit&#xff08;主要应用于Safari和其他基于WebKit的浏览器&#xff09;的特定前缀属性&#xff0c;用于实现元素内容的反射效果。具体解释如下&#xff1a; -webkit-box-reflect: 定义了一个盒反射效果&#xff0c;仅在支持WebKit的浏览器中生效。 below 15px&a…

JS-DOM树和DOM对象

作用和分类 作用&#xff1a;就是使用JS去操作html和浏览器 分类&#xff1a;DOM&#xff08;文档对象模型&#xff09;、BOM&#xff08;浏览器对象模型&#xff09; 什么是DOM DOM&#xff08;Document Object Model--文档对象模型&#xff09;是用来呈现以及与任意HTML或…

Laravel 框架中队列的使用

概述 Laravel 框架内置了强大的队列系统&#xff0c;用于处理异步任务、提高系统性能等。队列可以让任务异步执行&#xff0c;而不会阻塞当前进程&#xff0c;可以提高系统的处理能力。 Laravel 的队列系统支持多种驱动&#xff0c;如 Redis、Beanstalkd、SQS 等&#xff0c;…

静态代理IP是如何助力跨境电商运营的?我的跨境电商发展史

跨境电商这几年的火爆程度已经不需要我多说什么了&#xff0c;我自己与跨境电商结缘还是无意之间在某乎上看了那种所谓的“0基础小白如何在家做跨境电商&#xff0c;副业月入XX&#xff0c;附选品指南&#xff01;”。 我不知道你们刷到过这种类似的帖子没有&#xff0c;当时…

InternLM第3节课笔记

基于 InternLM 和 LangChain 搭建你的知识库 1 大模型开发范式 LLM局限性&#xff1a;时效性、专业能力有限、定制化成本高 RAG&#xff08;检索、增强、生成&#xff09; 外挂知识库 微调 2 LangChain简介 LangChain 核心组成模块 Chains&#xff1a;组件组合实现端到…

MATLAB--pie函数绘制分类饼图(1)--附案例代码

MATLAB–pie函数绘制分类饼图&#xff08;1&#xff09; 目录 MATLAB--pie函数绘制分类饼图&#xff08;1&#xff09;摘要1. pie函数概述2. 使用pie函数绘制分类图的步骤步骤1&#xff1a;准备数据步骤2&#xff1a;调用pie函数步骤3&#xff1a;定制图形&#xff08;可选&…

杭州中科微 BDS/GNSS 全星座定位导航模块 ATGM332D-5N31使用笔记

一、BDS/GNSS 全星座定位导航模块 ATGM332D-5N31 介绍 ATGM332D-5N 系列模块是 12X16 尺寸的高性能 BDS/GNSS 全星座定位导航模块系列的总称。该系列模块产品都是基于中科微第四代低功耗 GNSS SOC 单芯片—AT6558&#xff0c;支持多种卫星导航系统&#xff0c;包括中国的 BDS&a…

LeetCode+ 56 - 60

合并区间 双指针算法、位运算、离散化、区间合并_小雪菜本菜的博客-CSDN博客 class Solution { public:vector<vector<int>> merge(vector<vector<int>>& a) {vector<vector<int>> res;if(a.empty()) return res;sort(a.begin(),a.en…

数据结构二叉树创建及例题(上)

今天就带领大家来到树的世界,树无论是在考试上还是实际学习方面都是比较重点的,大家在这块知识要花时间搞懂. 文章目录 前言 一、树的二叉链表定义 二、二叉树三种遍历方式(递归方式) 1.先序遍历方式(根左右) 2.中序遍历方式(左根右) 3.后序遍历方式(左右根) 三、二叉树的…

智能小程序小部件(Widget)开发详解

Widget 代表应用的一个小部件&#xff0c;负责小部件的展示和交互。 小部件(Widget) 的开发在智能小程序的基础上增加一个目录即可&#xff0c;用于存放小部件(Widget)的代码。并在 project.tuya.json 中增加一个声明。 创建小部件(Widget)项目 在 Tuya MiniApp Tools 中&…

强化学习应用(一):基于Q-learning的无人机物流路径规划研究(提供Python代码)

一、Q-learning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个价值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…

在windows11系统上利用docker搭建linux记录

我的windows11系统上&#xff0c;之前已经安装好了window版本的docker&#xff0c;没有安装的小伙伴需要去安装一下。 下面直接记录安装linux的步骤&#xff1a; 一、创建linux容器 1、拉取镜像 docker pull ubuntu 2、查看镜像 docker images 3、创建容器 docker run --…

Ubuntu 22.04 编译安装 Qt mysql驱动

参考自 Ubuntu20.04.3 QT5.15.2 MySQL驱动编译 Ubuntu 18.04 编译安装 Qt mysql驱动 下边这篇博客不是主要参考的, 但是似乎解决了我的难题(找不到 libmysqlclient.so) ubuntu18.04.2 LTS 系统关于Qt5.12.3 无法加载mysql驱动&#xff0c;需要重新编译MYSQL数据库驱动的问题以…

代码随想录算法训练营第六天|哈希表理论基础,242.有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和

刷题建议 刷题建议与debug 代码随想录目前基本都有了视频讲解&#xff0c;一定要先看视频&#xff0c;事半功倍。写博客&#xff0c;将自己的感悟沉淀下来&#xff0c;不然会忘大家提问的时候&#xff0c;记得要把问题描述清楚&#xff0c;自己在哪一步遇到了问题&#xff0c…