Linux网络编程——套接字网络编程预备知识

news2024/11/16 6:54:56

0.关注博主有更多知识

操作系统入门知识合集

目录

1.理解IP地址和MAC地址

2.认识端口号

3.感性认识TCP协议和UDP协议

4.网络字节序

5.socket编程接口

1.理解IP地址和MAC地址

事实上在上一篇博客当中粗浅了介绍一个IP地址MAC地址,其中我们知道IP地址提供方向,MAC地址提供可行性。那么到底应该怎么理解方向和可行性呢?我们以一个简单的例子来理解:我们知道四大名著中的《西游记》是唐僧一行人去西天取经的故事,那么唐僧会经过很多地方并且遇到很多人,假设现在唐僧走到了女儿国,女儿国国王问:你从哪里来?你要干什么去?唐僧回答道:贫僧从东土大唐而来,要去西天拜佛求取真经。女儿国国王又问:那你上一次上一次是从哪里来的呀?唐僧回答道:贫僧从车迟国而来。女儿国国王又说道:那你要去西天取经,那你下一个国家就要去祭赛国。然后唐僧一行人收拾行李出发前方祭赛国......到了祭赛国,祭赛国国王问道:你们从哪里来?要干什么去?唐僧回答道:贫僧从东土大唐而来,要去西天拜佛求取真经。祭赛国国王又说:你要去西天拜佛求经,那你现在要先去朱紫国。然后唐僧一行人又收拾行李赶路......那么在这个例子中,唐僧口中的地址就有两种地址,我们这里把它称为绝对目标和相对目标,绝对目标指的是唐僧要从东土大唐到西天拜佛求经;相对目标指的是求取真经的路上途径的各个国家,例如从车迟国前往女儿国、女儿国前往祭赛国。

在上面的例子当中,我们不难分析出什么是IP地址,什么是MAC地址,即IP地址就是东土大唐和西天,那么要从东土大唐到西天,东土大唐就是源IP地址,西天就是目的IP地址;MAC地址就是途径的各个国家,其中从某个国家出发,这个国家称为源MAC地址,到哪个国家去,这个国家称为目的MAC地址。

那么由此可得,IP地址在网络通信当中,即数据传输当中,是保持长期不变的;而MAC地址是经常变化的。所以数据传输的过程就好像唐僧拜佛求经的过程,即数据从源IP地址出发,它的目标是达到目的IP地址;而数据在传输的过程中不是嗖的一下就过去,而是要脚踏实地、顺序地经过源IP和目的IP当中的各个机器,而这些各个机器是通过直接相连而组成局域网的,那么在局域网当中通信需要的地址为MAC地址,所以MAC地址在数据传输的过程中是经常变化的。这也就印证了IP地址提供方向,MAC地址提供可行性的说法。

2.认识端口号

那么既然IP地址既然能够确定方向,那么也能推测出IP地址在全网当中唯一(暂时这么理解),也就是说,IP地址确定一台唯一的主机。而我们之前说过,数据从源主机传输到目的主机不是通信的目的,而是通信的手段,通信的目的是为了解决人的需求,而人的需求需要通过软件、程序来表达。那么也就是说,网络通信的实质就是进程间通信

通信的进程之间需要具有唯一性(总不可能让QQ的消息推送到抖音里面去吧?)。我们在介绍Linux进程实现的时候知道,进程创建的时候操作系统会为该进程分配一个可用的PID,那么这个PID是用来描述进程的唯一标识符。事实上在网络通信当中用PID来表示通信双方进程的唯一性是可以的,至少在技术上是可以的,但实际上网络通信当中并不采用这种方案,原因如下:

  1.使用PID描述通信双方的进程就会给操作系统和网络加上一层耦合关系,因为具有耦合关系,那么操作系统、网络任何一方都不能出错

  2.通常通信的两个进程一个为客户端,一个为服务端,那么客户端在使用的时候需要知道服务端的IP地址和唯一的进程标识,也就是说客户端希望绑定一个长期不变、甚至永久不变的唯一进程标识。那么如果进程的唯一标识使用PID来表示的话,如果服务端的机器一旦出现异常而引发进程退出、重启等等,那么进程就要重新加载,进程重新加载就会造成PID的变化

  3.并不是所有的进程都需要网络服务

所以在网络通信当中,通信双方的进程使用端口号来标识进程的唯一性。由此可以得出,IP地址+端口号能够确定全网当中唯一的一台主机上的唯一一个进程。端口号(port)是传输层协议的内容,也就是说端口号是操作系统提供的,它是一个2字节16位的数字,能用来标识一个进程,并且值得注意的是:一个端口号只能被一个进程绑定,但是一个进程可以绑定多个端口号。因为进程本身就具有PID,那么进程也不一定需要网络服务,所以绑定了端口号的进程一定就是网络服务进程

那么操作系统在网络通信当中,一定是根据端口号去找到进程的PCB,寻找的方式就是通过哈希表。那么此时就会产生一个问题,我们说过进程控制块(PCB)是被链入一个双向循环链表的数据结构当中的,怎么这里是通过哈希表去查找的呢?事实上我们以前介绍的概念没有错误,而是因为操作系统内部实现非常复杂,PCB不单单被链入了双向循环链表当中,而且还被链入了哈希表这样的数据结构。也就是说,在操作系统当中,某一份资源不仅仅只占用了一个数据结构,还同时占用了多个数据结构。我们通过一张图来理解:

既然网络通信的实质是进程间通信,那么进程间通信需要有一个必要前提,那便是看到同一份资源,那么很显然,这个同一份资源就是网络。通信双方的进程通过网络进行数据传输,那么由此可得,网络通信就是在做I/O操作,所以我们人的上网行为无外乎就两种:要么发数据,要么接收数据。其中,我们需要认识到网络通信是相互的,所以客户端给服务端发送数据时,服务端接收到消息后,很有可能会向客户端回复一些消息,既然需要发送数据,那么就必须知道目标主机的IP地址和对应进程的端口号,所以无论是通信双方的哪个进程,只要向对方发送数据了,那么一定会多发一部分数据,这个多出来的数据就是自己进程本身的IP地址和端口号,而多出来的IP地址和端口号以协议的形式呈现

3.感性认识TCP协议和UDP协议

TCP协议和UDP协议都是传输层的协议,也就是说我们在编写应用层的代码时,必须使用传输层提供的接口,虽然可以跳过传输层而使用更底层的接口,但我们不那么做。

TCP协议:TCP(Transmission Control Protocol,传输控制协议):

  1.TCP协议是传输层协议

  2.使用TCP协议进行网络通信时,必须建立连接。例如打电话的过程,当用户A想跟用户B通话时不是直接对着电话一顿输出,而是要先把电话拨通,用户B再接听,此时才能正常通话

  3.TCP协议是可靠传输协议,数据在网络的传输过程中,一定会产生各种各样的错误,例如丢包、多发、少发等等,那么TCP协议有传输控制策略,即TCP协议有办法、有策略应对网络通信时发生错误的情况

  4.面向字节流传输

UDP协议:(User Datagram Prorocol,用户数据报协议):

  1.UDP协议是传输层的协议

  2.使用UDP协议进行网络通信时,无需建立建立链接。例如发邮件的过程,用户A向用户B发送一封邮件时,用户B不需要实时在线等待该邮件的到来

  3.UDP协议是不可靠传输协议,也就是说网络错误发生时UDP协议不会做出任何动作

  4.面向数据报传输

那么上面介绍到了可靠传输和不可靠传输,在这里需要强调的是,可靠传输不是一个褒义词,不可靠传输也不是一个贬义词。这叫特性。那么TCP协议是可靠传输协议,这就意味着TCP协议需要实现更多的策略、机制来应对网络发生错误的情况,所以TCP协议的实现会复杂,并且难以维护和使用;而UDP协议是不可靠传输协议,这就意味着传输速度一定比TCP协议快,并且实现机制简单,易于维护和使用。

TCP协议和UDP协议都有特定的使用场景,谁也不能取代谁。TCP协议的使用是非常广泛的,例如应用于银行系统、电商系统等等;而UDP协议因为其传输速度更快,所以更适合于视频直播这种类似的场景。

4.网络字节序

我们知道数据都是存储在内存当中的,而内存当中的每个数据单元都有对应的地址,既然有地址,那么就一定有高地址和低地址之分,那么数据就有两套不同的存储方式:数据的高权值位存放到内存的高地址处,我们称为大端存储模式;数据的高权值位存放到内存的低地址处,我们称为小端存储模式。因为有大端和小端的存在,所以在网络通信时,主机与主机之间使用的存储模式都是不一样的,这就意味着网络必须屏蔽掉这种差异。解决这种差异的手段有很多,例如在报文当中添加一个标记位用来标记发送数据的机器是大端还是小端,接收数据的机器根据该标记位来确定是否需要将收到的数据进行转化......解决的手段非常多,在技术上也一定能够实现。但是网络采用了一种非常暴力的解决方案,即规定在网络当中的数据都是大端字节序。这就意味着无论通信双方的主机是大端还是小端、是接收方还是发送方,都要遵守以下规则:

  1.发送数据的主机通要将缓冲区当中的数据按内存地址由低到高发送

  2.接收数据的主机要把接收到的数据保存在缓冲区中,并且按内存地址由低到高的顺序存储

  3.如果发送数据的主机是小端机,那么发送到网络之前需要将其转换为大端字节序;如果主机已是大端机,那么跳过转换,直接发送到网络

  4.接收数据的主机如果是小端机,那么接收到的数据一定是大端字节序,所以使用之前必须将其转换为小端字节序;反之直接使用

那么大小端字节序之间的转换在网络当中确实比较麻烦,所以<arpa/inet.h>库为我们做好了转换函数的封装

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htonl(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t htonl(uint16_t netshort);

这些接口看似并不能满足我们的使用需求,这是因为我们还没有开始编程,具体事情放到具体的情况再介绍。这些函数的命名是有道理的,"h"表示host,即主机;"to"直接翻译,即向...干嘛;"n"表示network,即网络,"l"表示long,即长整型;"s"表示short,即短整型,所以"htonl"表示主机将32位的长整数从主机序列转换为网络字节序(大端字节序),然后在函数内部实现转换,其返回值就是转换之后的结果。

5.socket编程接口

首先我们要知道什么是socket,socket翻译过来就是插座的意思,就好像数据从源主机发送到目的主机上,像是一个插头寻找插座的过程。那么"插头"我们认为是源IP和源端口号,"插座"我们认为是目的IP和目的端口号,IP和端口号我们称为套接字。所以套接字编程实际上就是在为IP和端口号编程。那么socket编程接口如下:

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,socklen_t address_len);

// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

这些接口我们一个都不认识,但是我们能够发现一个共性,即大多数接口都需要一个参数,即"struct sockaddr *address",那么这个参数是什么东西呢?

我们需要知道,套接字编程分为很多种类,常见的种类就有三种:网络套接字编程、原始套接字编程、Unix域间套接字编程。其中,网络套接字编程直接使用传输层提供的接口,既可以跨网络通信也可以本地通信;而原始套接字编程可以跳过传输层而使用更底层的接口;Unix域间套接字编程只能本地通信,与本地进程间使用命名管道通信的效果差不多。那么这么多种类的套接字编程,就需要多套编程接口,而设计多套编程接口无论在维护、使用上面都是非常麻烦的,所以接口的设计者设采用了一种非常高明的手段,即通过传递不同的参数来确定套接字编程的种类,这就是一种多态的表现。

那么网络套接字对应的结构体为struct sockadd_in,其中的"in"不要理解为里面,而要理解为inet,即IP地址;Unix域间套接字对应的结构体为struct sockaddr_un,其中"un"要理解为Unix:

因为这些结构体的类型不同,这就注定了我们在使用套接字接口时需要做强制类型转换。那么套接字接口接收到我们传递的参数之后(强制类型转换成统一的struct sockaddr *)之后,接口内部先检查前两个字节以确定套接字的种类,然后再将struct sockaddr *强转为对应的套接字类型的结构体:

那么我们知道,void *类型可以接收任何类型的指针,但是为什么接口的设计者不采用这种方案呢?答案一定意想不到,因为那个时候还没有void *这个语法。那么现在支持了,为什么不从新设计以下这些接口呢?因为软件的设计需要保证向前兼容,不排除有的编译器仍然很老旧,不排除有的用户、公司、企业的业务逻辑已经默认采用原始的方案,如果接口一旦从新设计,那么操作系统的代码需要重新编排,所以修改接口的代价是非常大的。

那么对于这些结构体的具体内容,我们到真正编程的时候再来一个一个分析。

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

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

相关文章

坐标系变换推导(欧拉角、方向余弦矩阵、四元数)+代码解析

一、为什么选择四元数 描述两个坐标系之间的变换关系主要有几个方法 1、欧拉角法(存在奇异性和万向锁而且三个轴旋转的顺序不好定) 2、方向余弦矩阵法(翻译为Directional cosine matrix&#xff0c;简称DCM&#xff0c;也称为旋转矩阵&#xff0c;看了很多博客写的是C11-C33的那…

【Python实操】如何快速写一个乒乓球游戏?

文章目录 前言一、 导入 turtle 和 Screen二、创建一个球3.创建一个 AI 挡板4.创建自己的挡板5.创建移动AI挡板的函数6.创建一个函数以移动你的挡板并用键盘控制它7.全部代码总结 前言 本文提供了一个 Python 实现的乒乓球游戏代码&#xff0c;你只需要将代码复制并粘贴到编辑…

如何防御流量攻击

随着互联网的发展&#xff0c;网络安全问题也日益突出。其中&#xff0c;流量攻击成为网络攻击的一种常见手段。那么流量攻击属于什么攻击&#xff0c;服务器防御流量攻击的方法有哪些呢?本文小编将为您一一解答。 一、流量攻击是什么? 流量攻击即DDoS攻击&#xff0c;全称为…

如何进行DNS优化

在互联网时代&#xff0c;网站的访问速度直接影响着用户体验和转化率。而DNS(Domain Name System)作为域名解析系统&#xff0c;负责将域名转换为IP地址&#xff0c;是网站访问速度的重要因素之一。因此&#xff0c;DNS优化成为了提升网站速度的重要手段之一。 DNS优化到底是什…

SpringMVC-RESTful

REST风格 1. REST简介1.1 REST介绍1.2 RESTful介绍1.3 注意事项 2. RESTful入门案例2.1 快速入门2.2 PathVariable介绍2.3 RequestBody、RequestParam、PathVariable区别和应用 3. REST快速开发【重点】3.1 代码中的问题3.2 Rest快速开发 4. 案例&#xff1a;基于RESTful页面数…

拿捏c语言循环

&#x1f4d5;博主介绍&#xff1a;目前大一正在学习c语言&#xff0c;数据结构&#xff0c;计算机网络。 c语言学习&#xff0c;是为了更好的学习其他的编程语言&#xff0c;C语言是母体语言&#xff0c;是人机交互接近底层的桥梁。 本章用循环去写一些题目。 让我们开启c语言…

Kubernetes系列---Kubernetes 理论知识 | 初识

Kubernetes系列---Kubernetes 理论知识 | 初识 1.K8s 是什么&#xff1f;2.K8s 特性3.小拓展&#xff08;业务升级&#xff09;4.K8s 集群架构与组件①架构拓扑图&#xff1a;②Master 组件③Node 组件 五 K8s 核心概念六 官方提供的三种部署方式 1.K8s 是什么&#xff1f; K…

springboot打包成jar包运行到服务器 java v 1.8

1.项目打包成jar包 1.1 1.2 2 jdk安装 2.1 jdk 官网 -> oracle 官方的jdk https://www.oracle.com/java/technologies/downloads/#java8 2.2 本地上传文件到服务器 2.3 配置安装 tar -zvxf jdk-8u131-linux-x64.tar.gz -->解压修改配置文件 source /etc/profile /…

Java 10 字符串

1.API 1.1API 概述 什么是API ​ API (Application Programming Interface) &#xff1a;应用程序编程接口 java 中的 API ​ 指的就是 JDK 中提供的各种功能的 Java 类&#xff0c;这些类将底层的实现封装了起来&#xff0c;我们不需要关心这些类是如何实现的&#xff0c;只…

C++好难(3):类和对象(中篇)

【本章目标】 类的6个默认成员函数构造函数析构函数拷贝构造函数赋值运算符重载const成员函数取地址及const取地址操作符重载 目录 【本章目标】 1.类的6个默认成员函数 2.构造函数 2.1概念 2.2构造函数的特性 特性一 特性二 特性三 特性四 特性五 特性六 特性七 …

Monkey Patching in Go

gomonkey 用来给函数打桩&#xff0c;这种使用一个新的方法实现来替换原来的实现逻辑&#xff0c;怎么看都觉得很神奇。举个例子&#xff0c;在单测中方法 json.Marshal 可以被 gomonkey 覆写成另一种逻辑实现&#xff0c;我准备从原理和使用的角度来看看 gomonkey。主要是来看…

LeetCode 第 344 场周赛

相当的惨烈&#xff0c;乱交 Q1 前后缀分解,用set统计不同元素的个数 class Solution {public:vector<int> distinctDifferenceArray(vector<int>& nums) {int n nums.size();vector<int> L(n 1, 0), R(n 1, 0); // 前缀不同数的个数set<int&g…

MLC LLM - 大模型本地部署解决方案

MLC LLM 是一种通用解决方案&#xff0c;它允许将任何语言模型本地部署在各种硬件后端和本地应用程序上&#xff0c;此外还提供了一个高效的框架&#xff0c;供每个人根据自己的用例进一步优化模型性能。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 我们的使命是让…

【Python】使用Print函数制作旋转的动画

1. 引言 如果你想有效地学习Python&#xff0c;这篇文章可能不适合你。接下来的一切都可能是愚蠢、和浪费时间&#xff0c;但哪有怎么样&#xff0c;毕竟这玩意很有趣呀&#xff01; 2. 好玩的脚本 首先&#xff0c;我们来看两个好玩的Python脚本&#xff0c;如下&#xff1…

开关电源基础03:正激和反激开关电源拓扑(3)-反激拓扑

说在开头&#xff1a;关于不确定性原理 1927年2月&#xff0c;那个冬天对海森堡来说简直是一场噩梦&#xff0c;越来越多的人转向了薛定谔和他那该死的波动理论&#xff0c;把他的矩阵忘得一干二净&#xff1b;而最让他伤心和委屈的是&#xff0c;玻尔也转向了他的对立面&…

大规模并行处理架构Doris编译部署篇

目录 1 Doris编译1.1 使用 Docker 开发镜像编译&#xff08;推荐&#xff09;1.1.1 遇到的问题1.1.1 遇到的问题 1.2 直接编译&#xff08;CentOS/Ubuntu&#xff09;1.2.1 环境准备1.2.2 系统依赖&#xff08;一次性安装&#xff09;1.2.3 手动安装系统依赖1.2.3.1 CMake 3.11…

Linux驱动开发:SPI子系统

1、SPI简介 1.1 四根线 MISO&#xff1a;主设备数据输入&#xff0c;从设备数据输出。 MOSI&#xff1a;主设备数据输出&#xff0c;从设备数据输入。 SCLK&#xff1a;时钟信号&#xff0c;由主设备产生。 CS&#xff1a; 从设备片选信号&#xff0c;由主设备控制。 1.2…

《string类的使用介绍》

本文主要介绍string的常见的接口的使用 文章目录 一、什么是string类二、string类的使用1、string类对象的常见构造2、string类对象的容量操作3、string类对象的访问及遍历操作①operator[ ]的用法②迭代器③范围for 4、string类对象的修改操作①push_back②append③operator&a…

刷题笔记8| 344.反转字符串, 541. 反转字符串II, 剑指Offer 05.替换空格

344.反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 输入&#xff1a;s ["h","e",…

索引—MySQL

文章目录 1.定义以及相关知识1.1定义1.2数据库保存数据的基本单位 2.MySQL中索引分类2.1B树和B树2.2主键索引&#xff08;聚簇索引&#xff09;2.3非聚簇索引2.4覆盖索引2.5复合索引&#xff08;联合索引&#xff09;2.6基于B树的索引2.7hash索引 1.定义以及相关知识 1.1定义 …