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

前言     

        本专栏将从零开始制作一个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的知识,但又学完了基础课程的情况下。所以我希望能写一些内容,对和我一样的同学有些帮助。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jmerer之FTP测试

1、文件上传下载测试&#xff0c;可以使用sample:FTP请求&#xff0c;当然也可以使用HTTP Request采样器中的File Upload向服务器上传文件 2、本章重点介绍FTP请求进行文件的上传下载测试&#xff0c;添加 FTP请求&#xff0c;界面主要配置如下&#xff1a; Server Name or I…

5G前装搭载率即将迈过10%大关,车载通讯进入多层次增长通道

对于智能化来说&#xff0c;车载通讯性能的提升&#xff0c;对于相关功能的用户体验优化、进一步减少通讯时延以及打开应用新空间&#xff0c;至关重要。 目前&#xff0c;2G/3G正在进入运营商逐步关闭运营的阶段&#xff0c;4G依然是主力&#xff0c;但5G也在迎来新的增长机会…

【深度学习 | 风格迁移】神经网络风格迁移,原理详解附详细案例源码

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

将Python程序打包成exe文件

一、什么是exe可执行文件&#xff1f; **exe文件英文全名是executable file&#xff0c;翻译为可执行文件&#xff08;但它不等于可执行文件&#xff09;&#xff0c;可执行文件包含两种&#xff0c;文件扩展名为.exe的是其中的一种。exe文件可以在Windows平台上直接双击运行&a…

蓝凌EIS智慧协同平台 多处SQL注入漏洞复现

0x01 产品简介 蓝凌EIS智慧协同平台是一款专为企业提供高效协同办公和团队合作的产品。该平台集成了各种协同工具和功能,旨在提升企业内部沟通、协作和信息共享的效率。 0x02 漏洞概述 由于蓝凌EIS智慧协同平台 doc_fileedit_word.aspx、frm_form_list_main.aspx、frm_butt…

力士乐触摸屏维修触控屏VR2109.01-00-01-N2-NNN-A

Rexroth力士乐触控屏VCP20.1BUN.768PB-NN-PW数控系统屏幕维修及排查&#xff1a; 力士乐数控机床故障诊断的一般步骤都是相同的。当数控机床发生故障时&#xff0c;除非出现危险及数控机床或人身的紧急情况&#xff0c;一般不要关断电源&#xff0c;要尽可能地保持机床原来的状…

zotero云存储免费扩容到25G

zotero免费云存储只有300MB&#xff0c;若想扩容要么付费购买zotero的云空间&#xff0c;要么通过WebDAV方式使用云盘空间进行同步。很久以前好像尝试过坚果云盘&#xff0c;但是感觉有点辣鸡&#xff0c;网上的博客教程大部分都是用的坚果。 最后在淘宝上花了20找人远程了一下…

Google的Ndk-Sample学习笔记之一(hello-jniCallback)

前言: 近段时间因为项目的需求,需要使用JNI,所以下载了Google的Ndk-Sample学习下,准备记录 下来,留给后期自己查看 问题点一:JNI_OnLoad方法必须返回JNI的版本 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env;memset(&g_ctx, 0, sizeof(g_…

竞赛保研 基于深度学的图像修复 图像补全

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学的图像修复 图像补全 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-se…

C#编程-实现多线程

实现多线程 多线程帮助同时执行各种操作。这为用户节省时间。多线程程序包括一个主线程和其他用户定义的线程以同时执行多个任务。 微处理器为执行的进程分配内存。每个进程占有内存中它们自己的地址空间。但是,所有在进程中的线程占有相同的地址空间。多线程允许在一个程序…

美团2024届秋招笔试第一场编程真题(js版本)

1.小美的外卖订单 简单的加法逻辑&#xff0c;需要注意的是各个数据的边界问题 折扣价不能超过原价减的价格不能超过满的价格满减优惠仅限原价购入 const rl require("readline").createInterface({ input: process.stdin }); void (async function () {let count…

JavaScript保留字和预定义的全局变量及函数汇总

保留字也称关键字&#xff0c;每种语言中都有该语言本身规定的一些关键字&#xff0c;这些关键字都是该语言的语法实现基础&#xff0c;JavaScript中规定了一些标识符作为现行版本的关键字或者将来版本中可能会用到的关键字&#xff0c;所以当我们定义标识符时就不能使用这些关…

典型场景解析|PolarDB分布式版如何支撑SaaS多租户?

SaaS多租户背景 很多平台类应用或系统&#xff08;如电商CRM平台、仓库订单平台等等&#xff09;&#xff0c;它们的服务模型是围绕用户维度&#xff08;这里的用户维度可以是一个卖家或品牌&#xff0c;可以是一个仓库等&#xff09;展开的。因此&#xff0c;这类型的平台业务…

文件上传漏洞(全网最详细)

目录 前言 文件上传漏洞介绍 文件上传漏洞危害 文件上传漏洞满足条件 文件检测流程 CTFSHOW 151关-170关 151关&#xff1a;前端验证绕过 152关&#xff1a;后端校验 Content-Type 校验文件格式 153关&#xff1a;filename字段文件后缀校验 154关&#xff1a;关键字过…

18_类加载

文章目录 类加载器类加载时机Java代码的3个阶段 反射关于Class配置文件(.properties)Properties类通过反射获取构造方法(Constructor)通过反射获取成员变量(Field)通过反射获取成员方法(Method) 其他API自定义类加载器反射的应用 类加载器 分类&#xff1a; Bootstrap ClassLo…

Visual Studio中项目添加链接文件

这个需求在VS里面使用还真不多见&#xff0c;只是最近在做项目的版本编号的时候遇到一个头大的问题&#xff0c;我一个解决方案下面有几十个类库&#xff0c;再发布的时候这几十个类库的版本号必须要统一&#xff0c;之前我们都是在单个的AssemblyInfo.cs里面去改相关的信息&am…

轻量级图床Imagewheel本地部署并结合内网穿透实现远程访问

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

Echarts的常用API,以及常用的写法

ECharts是一款基于JavaScript的开源可视化库&#xff0c;用于构建交互式的图表和可视化数据。它提供了丰富的API用于定制图表和处理数据。下面是一些常用的ECharts API和写法的简介&#xff1a; 初始化图表容器&#xff1a; var myChart echarts.init(document.getElementBy…

win11更改桌面默认存储路径

打开文件资源管理器 右击桌面点击属性 在属性中找到位置选项卡&#xff0c;在里面有一个移动&#xff0c;点击它选择你想要的位置 选好位置后点击应用&#xff0c;随后会出现一个进度条&#xff0c;跑完后点击确认 到这里就完成了桌面默认位置的转移

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

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