首页 > 系统相关 >linux网络编程基础知识汇总(更新中)

linux网络编程基础知识汇总(更新中)

时间:2024-02-27 22:22:08浏览次数:20  
标签:addr int IP chj 编程 基础知识 地址 linux 进程


阿帕网 arpanet
阿帕网为美国国防部高级研究计划署开发的世界上第一个运营的封包交换网络,它是全球互联网的始祖。


局域网 LAN(Local Area Network ):通过路由器和交换机把计算机连接在一起
广域网 WAN(Wide Area Network )
//广域网和局域网没有明显的界限,是一个相对的概念,一般把只经过交换机,而不经过路由器的网络称为局域网.经过路由器的叫广域网


局域网的一般原理: 主机向局域网内发数据,其他的所有主机都能收到.其他主机通过不同的抓包方式,获取需要的数据包,从而达到通信
//以太网是局域网的一种实现方式

$$ 网络效应 network effect(也称网络外部性 network externality 或需求方规模经济 demand-side economies of scale),
是一个经济学和商业方面的术语,用于描述对于一个产品(或服务),每增多一名用户,都会对该产品的其他用户产生新的价值。
当网络效应出现时,产品(或服务)的价值,会随着使用该产品(或服务)的人数的增加而增加。
/*
一个经典的例子是电话,越多的人使用电话,对每个电话使用者的价值就越大。
一部电话的使用者,本来无意为其他用户创造价值,但当大家都购买电话时,这个行为就产生了正外部性。
像微信、微博、Twitter、Facebook 这样的线上社交网络,也是一样,随着更多用户的加入,每个用户获得的价值都在增加。
当网络的价值越来越大,越来越多的人加入时,网络效应还会带来从众效应,从而形成正反馈循环。
网络效应这一表述不仅适用于正向的网络外部性,如电话的案例;消极的网络外部性也可能发生,即用户越多,产品的价值就越低,
这种情况通常被称为“拥堵”,例如交通拥堵或网络拥堵。
*/

网络技术盈利方式较少,大多数都是一套标准,谁做的好就用谁的

 

 


$$ 协议
"协议"是一种约定,遵守协议能够大大减少沟通成本

$$ 网络协议初识
//目前最主流的协议是TCP/IP协议

 


$$ 网络协议的层状结构

来源:传输举例变长,产生各种问题
1.长距离传输,数据异常的问题(丢失)
2.如何定位一台主机
3.怎么进行数据转发,和路径选择(路由,中间主机...)
4.01010...01等硬件级别的协议

上面的问题,是有先后上下起因关系的 ,如传输数据前必须先定位主机位置.如何传输,用什么方式...
---> 因此我们可以在设计的时候将不同的功能模块设计成不同的模块,并根据先后上下关系,最终设计成层状结构


// 继承体系也是一种软件分层
//任何计算机问题都可以在中间加一层软件层来解决 ---> 软件分层在软件中很常见

软件分层之后,每一层都只关注自己同层的功能,只使用下层的接口,任何一层出现问题,都不会直接影响另一层,减少后期开发者的维护成本
软件出现问题后,工程师一般会先定位问题在哪一层,然后再解决问题.
---> 高内聚,低耦合

 

$ OSI七层模型
OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范;
OSI把网络从逻辑上分为了7层. 每一层都有相关、相对应的物理设备,比如路由器,交换机;
OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输;
.它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整.
通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯;
但是, 它既复杂又不实用; 所以我们按照TCP/IP四层模型来讲解.


//实际实现时,把5-会话层,6-表示层,7-应用层 合并成1层:5-应用层
TCP/IP五层模型包括了物理层(硬件),四层模式不包括物理层(通常)

$$ TCP/IP五层(或四层)模型
TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇.
TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求.
.物理层: 负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、早期以太网采用的的同轴电缆
(现在主要用于有线电视)、光纤, 现在的wifi无线网使用电磁波等都属于物理层的概念。物理层的能力决
定了最大传输速率、传输距离、抗干扰性等. 集线器(Hub)工作在物理层.
.数据链路层: 负责设备之间的数据帧的传送和识别. 例如网卡设备的驱动、帧同步(就是说从网线上检测
到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作. 有以太
网、令牌环网, 无线LAN等标准. 交换机(Switch)工作在数据链路层.
.网络层: 负责地址管理和路由选择. 例如在IP协议中, 通过IP地址来标识一台主机, 并通过路由表的方式规
划出两台主机之间的数据传输的线路(路由). 路由器(Router)工作在网路层.
.传输层: 负责两台主机之间的数据传输. 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标主机.
.应用层: 负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问
协议(Telnet)等. 我们的网络编程主要就是针对应用层

物理层我们考虑的比较少. 因此很多时候也可以称为 TCP/IP四层模型.
一般而言
对于一台主机, 它的操作系统内核实现了从传输层到物理层的内容;
对于一台路由器, 它实现了从网络层到物理层;
对于一台交换机, 它实现了从数据链路层到物理层;
对于集线器, 它只实现了物理层;
但是并不绝对. 很多交换机也实现了网络层的转发; 很多路由器也实现了部分传输层的内容(比如端口转发);

 

// 这些网络分层结构就是协议栈 -- 网络协议栈


用户层 ... | 应用层
|-----------------------------------------------------------------
sys_call 文件系统调用 键盘 显示器 ... | 网络系统调用
|----------------------------------------------------------------
| | 传输层 (TCP协议)
OS 进程管理 文件系统 内存管理 驱动管理...| 网络层 (IP协议)
|-----------------------------------------------------------------
驱动 键盘驱动 显示器驱动 声卡 ... | 网卡驱动 (数据链路层)
|-----------------------------------------------------------------
硬件 键盘 显示器 声卡 ... | 网卡 (物理层)


网络也是属于OS一部分,OS和网络不分家

网络协议栈最核心的部分是属于操作系统的部分 传输层(TCP协议)和网络层(IP协议) . 所以一般以TCP/IP协议栈命名

linux中一切皆文件,网络在linux中也不例外.在linux系统中隶属于文件系统模块

OS有很多款,但网络只能有1种(网络指TCP/IP层,且必须是一样的).

数据链路层是有多种标准的,最常见的是以太网.
为什么有多种标准? 局域网环境不同,硬件实现 等不同而不同


物理层是基础
数据链路层是丰富
网络层重点是提供了能力,但有能力传输,但不一定可靠
传输层重点是"可靠"传输
应用层是使用数据,根据不同场景有不同的实现(应用程序).

一般而言,上层的一定有下层的所有功能,下层的一般不会有上层的功能.
如路由器一般工作在网络层,但有些路由器具备应用层的功能

 

 

 

1.同一个局域网(子网)
同一个局域网的两台机器,可以直接通信.如cs局域网联机.如一条网线连接两台电脑可以识别...
实质:两个主机通信的实质是,两个主机的OS在通过网络协议栈进行通信

网络协议栈的每一层,都要有自己的协议.
协议的表现形式:报头/协议报头

报文:报头+有效载荷

//在物流体系中:物流的主体是快递单+商品. 快递单是报头,商品是数据

用户向另一台主机发送数据的过程:
把数据从应用层,层层往下传,每一层都会把上一层发来的数据加工/封装(加上自己的报头),依次传到物理层,发送给另一台主机的硬件(物理层),然后层层往上传

接收数据的过程:
数据从物理层接收到后,将数据层层往上传,每一层都会把自己所在层的报头去掉,把有效载荷传给上一层

逻辑上,同层协议,都认为自己在和对方的同层协议在通信

同一层都能够互相认识对方的报头,所以
a.能将报头和有效载荷进行分离
b.将有效载荷交付给上一层的哪个具体协议 --- 数据包分用功能
//这是每层协议都要有的公共功能

 


有效载荷:对每一层所接收到的上一层数据叫做"有效载荷",有效载荷是上一层交付给自己的数据
每一层的协议 = 自己的报头(header)+有效载荷

 

发送:自顶向下 --- 封装
接收:自底向上 --- 解包+分用

 


局域网的一般原理: 主机向局域网内发数据,其他的所有主机都能收到.其他主机通过不同的抓包方式,获取需要的数据包,从而达到通信

过滤模式:把不是发给自己的数据包丢弃掉 -- 常规
混杂模式:把局域网内抓到的所有包都收集起来 -- 抓包软件的抓包原理,把网卡的工作模式设为混杂


局域网的数据碰撞问题:数据量大了,冲突的概率变大,导致系统处理不过来...


局域网中,任何时刻,只允许一个人在向局域网发送消息?

任何人要通信,需要有唯一的一个标识符
对机器也是如此,每台机器都配有网卡,网卡在出产时,就在网卡内部写入了sn号(出产序列号,机器码、认证码、注册申请码等)
每块网卡出厂时,都预先分配了一个全球唯一的 MAC地址 ,并烧进硬件。 不管后来网卡身处何处,接入哪个网络,MAC 地址均不变。
当然,某些操作系统也允许修改网卡的 MAC 地址。


MAC网络协议中的链路层地址,又称物理地址,有兴趣查查网络七层协议,ID一般指的是这个设备的编号或者名字,
而SN是这个设备的序列号, 一般是设备厂商用来唯一标识这个设备用的,就跟身份证号一样

局域网通过mac地址来识别

 

 

 

 


网卡管理
Linux 上有不少工具命令可以查看系统当前接入的网卡以及每张网卡的详细信息。

首先是 ifconfig 命令,他默认显示已启用的网卡,详情中可以看到每张网卡的物理地址:
//if:interfaces

fasion@u2004 [ ~ ] ➜ ifconfig
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.2.15 netmask 255.255.255.0 broadcast 10.0.2.255
//inet就是ip网络,就是ip地址,这里是内网的ip地址,公网的看不到 //Internet
inet6 fe80::a00:27ff:fe49:50dd prefixlen 64 scopeid 0x20<link>
ether 08:00:27:49:50:dd txqueuelen 1000 (Ethernet)
//ether 以太,表示基于以太网表示的mac地址, 48位,每个字节用冒号:分隔,以十六进制表示

RX packets 3702 bytes 4881568 (4.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 538 bytes 42999 (42.9 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.56.2 netmask 255.255.255.0 broadcast 192.168.56.255
inet6 fe80::a00:27ff:fe56:831c prefixlen 64 scopeid 0x20<link>
ether 08:00:27:56:83:1c txqueuelen 1000 (Ethernet)
RX packets 4183 bytes 1809871 (1.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2674 bytes 350013 (350.0 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 679 bytes 1510416 (1.5 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 679 bytes 1510416 (1.5 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
例子中,系统总共有 3 块已启用网卡,名字分别是 enp0s3 、 enp0s8 以及 lo 。其中 lo 是环回网卡,用于本机通讯。ether 08:00:27:49:50:dd 表明,网卡 enp0s3 的物理地址是 08:00:27:49:50:dd 。

请注意,ifconfig 是一个比较老旧的命令,正在慢慢淡出历史舞台。
ip 命令也可以查看系统网卡信息,默认显示所有网卡:

fasion@u2004 [ ~ ] ➜ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 08:00:27:49:50:dd brd ff:ff:ff:ff:ff:ff
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 08:00:27:56:83:1c brd ff:ff:ff:ff:ff:ff
ip 命令输出信息比较紧凑, link/ether 08:00:27:49:50:dd 这行展示网卡的物理地址。

ip 命令是一个比较新的命令,功能非常强大。它除了可以用于管理网络设备,还可以用于管理路由表,策略路由以及各种隧道。因此,推荐重点学习掌握 ip 命令的用法。

 

 

 

2.跨一个路由器的两个子网

$ 认识路由器:
工作层:
3.网络层 --- 首先工作在网络层,所以要有网络层的功能,然后下层的所有功能也都要有
2.数据链路层
1.物理层
路由器也是一台主机(通常称为节点),路由器一定要有路由的功能,即跨网络转发,所以路由器至少要级联两个子网,所以至少要有两个网络接口(两个以上接口)
//路由器就是把不同局域网连接起来的工具

// 令牌环:有token的主机才能发送数据包,也能保证任何时刻只能允许一台主机发送数据包.和以太网是两种不同的通信标准,不同的数据链路层的实现方案.

表示一台主机,用IP地址,能表示主机在公网环境的唯一性

目前是IPV4 ---> 4字节的整数,每个字节表示一个整数,范围是0-255
取值范围:[0.0.0.0 , 255,255,255,255] , 如192.168.x.y ,这种一般是字符串风格(点分十进制)表现的,用于给人看
int srcIP = XXX;
struct ip *p = (struct ip*)&srcip;
p->part1; //ip的第一部分
p->part2; //ip的第二部分
...

数据链路层有路由表,IP协议会判断主机是否在当前局域网内,如果不在才会发送给路由器 (默认路由)

IPB:最终目的,传输途中会根据IPB进行路径选择
目的mac:传输途中下一站的地址,根据选择的路径来决定下一跳主机


现象:IP层及以上,所有网络的传输都是没有差异. IP层一下的,物理层等,有不同的传输方式
说明:IP地址屏蔽了底层子网机制的差异!


$ 数据包封装和分用
不同的协议层对数据包有不同的称谓,
在应用层叫做请求-响应
在传输层叫做数据段/段(segment),
在网络层叫做数据报 (datagram),
在链路层叫做帧(frame).

应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装(Encapsulation).
首部信息中包含了一些类似于首部有多长, 载荷(payload)有多长, 上层协议是什么等信息.
数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部, 根据首部中的 "上层协议
字段" 将数据交给对应的上层协议处理


题目描述:
实现函数将 unsigned int 整型数值转为点分十进制记法表示:
点分十进制(Dotted Decimal Notation)全称为点分(点式)十进制表示法,
是IPv4的IP地址标识方法。
IPv4中用四个字节表示一个IP地址,每个字节按照十进制表示为0~255。
点分十进制就是用4个从0~255的数字,来表示一个IP地址。
char * my_DotDec(unsigned int ip,char *buffer);
参数说明:
value:欲转换的数数值。
buffer:目标字符串的地址。
示例: ip = 2148205343; buffer = “128.11.3.31”;

分析:
灵活地利用联合体内存共享的特点
题目中要求将数字转换成字符串,我们就要想到使用sprintf函数,
union IPNode
{
unsigned int addr;
struct
{
unsigned char part1;
unsigned char part2;
unsigned char part3;
unsigned char part4;
};
};
char* my_DotDec(unsigned int ip,char *buffer)
{
IPNode x;
x.addr = ip;
sprintf(buffer,"%d.%d.%d.%d",x.s4,x.s3,x.s2,x.s1);
return buffer;
}
注意事项:
windows系统下栈是在低地址,堆在高地址,栈和堆从中间向两头扩展,LINUX系统上是从两头向中间扩展,
pc端是小端–低地址存放低数据而x又是从堆上开辟的(因为结构体的定义是在函数的外面,
如果结构体定义在函数内部就是从栈上开辟),堆是由低地址向高地址开辟内存,
所以s4在高地址,存放的是大数据。sprintf函数的第一个参数是字符串的输入指向char指针型,
第二个参数是控制输入的字符的形式(%d,%f,%0x,%x),用“ ”括起来,第三个参数是输入的数据。

 

 

$$ 网络中的地址管理
$ 认识IP地址
IP协议有两个版本, IPv4和IPv6. 我们整个的课程, 凡是提到IP协议, 没有特殊说明的, 默认都是指IPv4
IP地址是在IP协议中, 用来标识网络中不同主机的地址;
对于IPv4来说, IP地址是一个4字节, 32位的整数;
我们通常也使用 "点分十进制" 的字符串表示IP地址, 例如 192.168.0.1 ; 用点分割的每一个数字表示一个
字节, 范围是 0 - 255;

$ 认识MAC地址
MAC地址用来识别数据链路层中相连的节点;
长度为48位, 及6个字节. 一般用16进制数字加上冒号的形式来表示(例如: 08:00:27:03:fb:19)
在网卡出厂时就确定了, 不能修改. mac地址通常是唯一的(虚拟机中的mac地址不是真实的mac地址, 可
能会冲突; 也有些网卡支持用户配置mac地址).

$ 理解源IP地址和目的IP地址
唐僧例子1 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址(用于回复), 和目的IP地址.
思考: 我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上,
但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进行解析.


网络通信的数据包都有两套地址
1.源IP和目的IP //起点和终点
2.源mac和目的mac //上一站和下一站

$ 目的IP:为报文定制最终目标,路上根据该地址进行路径选择.
$ 目的mac:根据路径选择的结果,来选择下一跳的主机.

 

网络通信其实是进程间通信.属于进程间通信的范畴
1.先将数据通过OS发送到目标主机,手段:TCP/IP协议完成.IP可以表示在互联网上唯一一台主机.
2.主机收到的数据,要推送给自己上层的指定进程. ---> 如何标识自己主机上网络进程?

$ 标识主机上网络进程: 端口号
认识端口号:
1.端口号(port)是传输层协议的内容
a.端口号是一个2字节的整数
b.端口号用来标识一个进程
c.IP地址+端口号能够标识网络上某一台主机的某个进程
d.一个端口号只能被一个进程占用


$ 理解源端口号和目的端口号
唐僧例子2
送快递例子
传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 "数据是谁发的, 要
发给谁";


"源IP,源端口 --- 目标IP,目标端口" 方案称为:套接字通信/socket通信

$ 网络通信的本质:
是 通过IP+PORT构建进程唯一性.来进行的基于网络的进程间通信.

为什么不用PID?
1.不是所有的进程都要进行网络通信.使用PID会导致难以区分哪些进程进行网络 通信,哪些进程不是
2.PID属于进程管理模块,如果网络通信使用PID,则网络模块也要有进程管理模块.会增加进程管理和网络管理的耦合度.(重点)


$ 一个进程可以关联多个端口号, 但一个端口号只能关联1个进程
//一个进程可以关联多个端口号是什么意思?

$ TCP/IP的数据报文里有一个四元组字段:
src_ip src_port dst_ip dst_port [ payload ]

$ 一个端口号只能关联1个进程,一个进程可以关联多个端口号的实现方式:hash表/hash数组

 

socket”为什么翻译为“套接字”
/*
概念:套接字基本是两个端点的程序之间信息通道
首先套接字原词为“socket”,直译就是插座的意思,最先采用这个词的人,觉得网络连接,就像插口和插座一样,一方插,一方被插(知乎用户的回答)。
除此之外,linux等系统中“套接字”对应“socket word”,所以“字”也就是对应“word”,
这个“word”可能指储存socket的数据标识,因为端口号是两字节,就是一个WORD。

赞同的解释是:套接指的是套接管,就是将两根水管套接起来的管子,
然后“字”是此连接的数据标识,即一个WORD,所以套接字就是一个标识连接的数据体。
*/

 

认识TCP协议
TCP(Transmission Control Protocol 传输控制协议) //面向链接的协议
.传输层协议
.有连接
.可靠传输 // 数据丢失能自动补发
.面向字节流 //不关心读到的数据如何解释,只关心接收到多少数据,然后传多少数据给上层应用,由上层应用解释数据


UDP(User Datagram Protocol 用户数据报协议)
传输层协议
无连接 //通信前不建立通信
不可靠传输 //真丢失
面向数据报

 

网络字节序
规定:大端
用途:统一网络传输过程中的传输序列

网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,
磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,
网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
1.发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
2.接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
3.因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
//理解:低地址先进队列,而队列头是高地址

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节. //规定
不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

//用户数据字节序无所谓,由用户协议定制

 

为使网络程序具有可移植性,使同样的C代码在大端和小端机器上编译后能够正常运行,
可以调用以下库函数做网络字节序和主机字节序的转换.(方便和提高可读性)
{
常用:整数,端口号

注:recvfrom,sendto函数等,以及TCP的读写方法,会自动进行大小端转换,只有服务器启动,bind等阶段,才需要用户手动进行大小端转换

#include <arpa/inet.h>
//主机转网络
uint32_t htonl(uint32_t hostlong); //h:host(本主机) to n:network(网络) l:long(32位长整数主机)
uint16_t htons(uint16_t hostshort); //h:host(本主机) to n:network(网络) s:short(16位短整数主机)
//描述:: htonl表示:将32位的长整数从主机字节序转换成网络字节序,例如将端口号转换后准备发送

//网路转主机
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

//只有小端才会转,大端不做变换
}

不同风格的ip转换接口
{
//man inet
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
//和inet_addr差不多,二选一.inet_aton(serverip.c_str(),&server.sin_addr);

in_addr_t inet_addr(const char *cp);
//将点分十进制的IP地址转换成4字节地址,并由主机地址转成网络字节序的地址

in_addr_t inet_network(const char *cp);

char *inet_ntoa(struct in_addr in);
//将IP转成点分十进制(字符串)
注意:这个函数不一定是线程安全的(看平台实现).因为他创建的字符串常量,放在常量区,多次调用这个函数,可能会出现覆盖上一次的结果
所以最好用inet_ntop / inet_pton
多线程调用inet_ntoa测试代码,在需要测试的平台上测试
{
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
void* Func1(void* p) {
struct sockaddr_in* addr = (struct sockaddr_in*)p;
while (1) {
char* ptr = inet_ntoa(addr->sin_addr);
printf("addr1: %s\n", ptr);
}
return NULL;
}
void* Func2(void* p) {
struct sockaddr_in* addr = (struct sockaddr_in*)p;
while (1) {
char* ptr = inet_ntoa(addr->sin_addr);
printf("addr2: %s\n", ptr);
}
return NULL;
}
int main() {
pthread_t tid1 = 0;
struct sockaddr_in addr1;
struct sockaddr_in addr2;
addr1.sin_addr.s_addr = 0;
addr2.sin_addr.s_addr = 0xffffffff;
pthread_create(&tid1, NULL, Func1, &addr1);
pthread_t tid2 = 0;
pthread_create(&tid2, NULL, Func2, &addr2);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}

} //测试代码__End;
Advanced Programming in the UNIX Environment(APUE) Unix环境高级编程 -- 教材

struct in_addr inet_makeaddr(int net, int host);

in_addr_t inet_lnaof(struct in_addr in);

in_addr_t inet_netof(struct in_addr in);


}


socket常见API
{
//创建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 connet(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

}

$ sockaddr结构 // addr通用的结构设计思想
{
sockaddr:套接字地址 / 通用地址结构

socket API 是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4,IPv6,以及UNIX Domain Socket.
但各种网络协议的地址格式并不相同

sockaddr, sockaddr_in, sockaddr_un
|-------------| |-------------| |-------------|
|16位地址类型 | |16位地址类型 | |16位地址类型 | //结构体相同的头部类型,用于区别不同sock类型
| | | AF_INET | | AF_UNIX | //是一个宏,整数
|-------------| |-------------| |-------------|
| | | 16位端口号 | | |
| | |-------------| | |
|14位地址数据 | | 32位IP地址 | |108字节路径名|
| | |-------------| | |
| | | 8字节填充 | |14位地址数据 |
--------------- | ... | | |
------------- | ... |
-------------

1.sockaddr
结构:
struct sockaddr {
sa_family_t sin_family;//地址族
char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
  };
注解:用于统一的父结构体(父类)


2.struct sockaddr_in
头文件:<arpa/inet.h>

C结构:
struct sockaddr_in
{
//16位的地址类型
__SOCKADDR_COMMON (sin_); // sa_family_t sin_family :协议家族
用途: 和socket()的domain一样,用于指定通信域
/*
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
//该宏用于声明用于套接字地址的数据类型的初始公共成员,`struct sockaddr'、sockaddr_in、sockaddr_un'等。

typedef unsigned short int sa_family_t; //无符号短整型


*/

//服务器要绑定的端口号
in_port_t sin_port; // s:socket in:inet:Internet port:端口号
/*
in_port_t : uint16_t;
*/

//ip地址
struct in_addr sin_addr;
/* // struct in_addr 的结构
typedef uint32_t in_addr_t;
struct in_addr //直接放uint32不行吗?为什么要用结构体
{
in_addr_t s_addr; //网络字节序的地址类型
};
*/


//填充字段 -- 结构体很大,用不完,全部清零,填充满占位符
/* Pad to size of `struct sockaddr'.填充到结构体sockaddr的大小 */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};


}


//socket构建那时,C还没支持void*(不用强转,可以接收任意类型).所以代码比较丑陋,必须先强转才能传参


struct sockaddr_in //i:internet

 

$ int socket(int domain, int type, int protocol)
{

头文件:
#include <sys/socket.h>

socket() creates an endpoint for communication and returns a descriptor.
socket()创建一个通信端点并返回一个描述符。

RETURN VALUE
On success, a file descriptor for the new socket is returned. On error, -1 is
returned, and errno is set appropriately.
成功将返回文件描述符

domain:
The domain argument specifies a communication domain;
域参数指定通信域;
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7) //本地通信,域间通信
AF_INET IPv4 Internet protocols ip(7) //IPv4网络通信
AF_INET6 IPv6 Internet protocols ipv6(7)

AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK Appletalk ddp(7)
AF_PACKET Low level packet interface packet(7)
//AF: address familiers 地址家族
//PF: protocol familiers 协议家族
//两种写法都可以 如 AF_INET 或 PF_INET相同

type:
SOCK_STREAM : 字节流,TCP用
Provides seq uenced, reliable, two-way, connection-based byte streams.
An out-of-band data transmission mechanism may be sup‐ ported.

SOCK_DGRAM : datagrams的简写
Supports datagrams
(connectionless, unreliable messages of a fixed maximum length).

protocol:
默认为0,根据传输数据类型自动识别传输协议

}

 

$ 绑定
{
声明:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
头文件:
#include<sys/types.h>
#include<sys/socket.h>

sockfd: 套接字文件描述符

struct sockaddr *addr:

addrlen: 解决sock_addr,sockaddr_in,sockaddr_un等类型大小不一致的问题,得到地址后再根据长度来获得完整的类型

注:只有在绑定阶段,才需要用户手动字节序转换




}

$ 问题1: 云服务器不允许用户直接绑定IP地址
{
报错: bind socker error : Cannot assign requested address

原因: 云服务器,不需要bind IP地址,需要让服务器自己指定IP地址,
因为云服务器,或服务器,一般会有多张网卡 , 但bind只能绑定一个,即只传数据给一张网卡,就会导致接收的数据量变少.
而我们期望只要是发送到这台机器上的数据,都要接收.所以,不允许使用IP来甄别数据,而是使用端口,只要是这个端口的,我都接收.
因此: 服务器或云服务器不允许使用明确的IP.
注:自己本地装的虚拟机,或者物理机器是允许的.

如何让服务器自己指定IP地址?
解决: 使用宏INADDR_ANY 来让我们的server在启动时,bind本主机上的任意IP/自 动分配IP
//用例: local.sin_addr.s_addr = INADDR_ANY; //如果置零sockaddr_in了,不写也行
//#define INADDR_ANY ((in_addr_t) 0x00000000)


}


数据接收 -- receive
{
//man 2 recv

接口:recvfrom
声明:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数:
sockfd:套接字
buf: 用户定义的缓冲区
len: 缓冲区大小
flags: 读取方式,默认读取方式为阻塞读取,目前只需要设置为0;
sockaddr*: 输出型参数,存放着发送方的IP和Port.
socklen_t: sockaddr的实际长度
返回值: ssize_t , 实际读到的字节数
注意: 缓冲区内的数据是什么类型是未知的,需要由程序员制定协议来规定. 目前先设为C风格字符串
//peer 对等,对方 ,相同(地位/年龄)的人

//自动进行大小端转换

}

数据接收 - sendto
{
声明:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数:和recvfrom差不多


//自动进行大小端转换

}

 

netstat
{
netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),
masquerade 连接,多播成员 (Multicast Memberships,即一对多通信) 等等。

选项:
-a (all)显示所有选项,默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化成数字。
-l 仅列出有在 Listen (监听) 的服务状态
-p 显示建立相关链接的程序名,(进程process)
-r 显示路由信息,路由表 (router)
-e 显示扩展信息,例如uid等 (extend)
-s 按各个协议进行统计 (stat)
-c 每隔一个固定时间,执行该netstat命令。 (cycle)

#以下不常用
-I # 显示iFace接口表
-i # 显示接口表
-g # 显示多播组成员
-s # 显示网络统计数据(如SNMP)
-M # 显示伪装连接
-v # 显示详细verbose
-W # 不要截断IP地址
-N # 解析硬件名称
-e # 显示其他/更多信息
-o # 显示计时器


例:
[chj@kk ~ 14:34:33]$ netstat -nupa
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
udp 0 0 0.0.0.0:716 0.0.0.0:* -
udp 0 0 0.0.0.0:8080 0.0.0.0:* 7600/./server //0.0.0.0表示任意IP/随机绑定IP.
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp 0 0 0.0.0.0:111 0.0.0.0:* -
udp 0 0 127.0.0.1:323 0.0.0.0:* -
udp6 0 0 127.0.0.1:60880 127.0.0.1:38457 ESTABLISHED -
udp6 0 0 :::716 :::* -
udp6 0 0 :::111 :::* -
udp6 0 0 ::1:323 :::* -


查看端口号占用情况
netstat -ano

查看活动连接数
netstat -antpu |grep 8888

}

$$ 0.0.0.0和127.0.0.1
{
$ 0.0.0.0
IPV4中,0.0.0.0地址被用于表示一个无效的,未知的或者不可用的目标。

在服务器中,0.0.0.0指的是本机上的所有IPV4地址,如果一个主机有两个IP地址,192.168.1.1 和 10.1.2.1,并且该主机上的一个服务监听的地址是0.0.0.0,那么通过两个ip地址都能够访问该服务。
在路由中,0.0.0.0表示的是默认路由,即当路由表中没有找到完全匹配的路由的时候所对应的路由。
用途总结
当一台主机还没有被分配一个IP地址的时候,用于表示主机本身。(DHCP分配IP地址的时候)
用作默认路由,表示”任意IPV4主机”。
用来表示目标机器不可用。

用作服务端,表示本机上的任意IPV4地址。

 


$ 127.0.0.1
127.0.0.1属于{127,}集合中的一个,而所有网络号为127的地址都被称之为回环地址,所以回环地址!=127.0.0.1,它们是包含关系,即回环地址包含127.0.0.1。
//本地环回,就表示当前主机,通常用来进行本地通信或者测试. --- 走一遍网络协议栈后发回来给自己
回环地址:所有发往该类地址的数据包都应该被loop back(发回来)。

 

$ localhost
相比127.0.0.1,localhost具有更多的意义。localhost是个域名,而不是一个ip地址。
之所以我们经常把localhost与127.0.0.1认为是同一个是因为我们使用的大多数电脑上都讲localhost指向了127.0.0.1这个地址。

用途
回环测试,通过使用ping 127.0.0.1 测试某台机器上的网络设备,操作系统或者TCP/IP实现是否工作正常。
DDos攻击防御:网站收到DDos攻击之后,将域名A记录到127.0.0.1,即让攻击者自己攻击自己。
大部分Web容器测试的时候绑定的本机地址。

}


client and server
{
一.客户端不需要自己绑定端口号,由系统自动绑定
因为客户端中有很多进程,假设客户端端口号是固定的,如果出现相同进程同时使用同一个端口号,就会导致冲突,必定有一个进程无法运行.
所以客户端的端口号不能是一个固定的端口号.交由操作系统自动为客户端选择端口号.

二.服务端的端口号必须是众所周知且不能随意改变的,和110等公共电话一样
//不同服务器的端口号可以一样


server和service的区别主要体现在以下三个方面:
1. 含义不同server 主要有两种含义,一是服务器,二是服务器上安装的应用程序。
service 则主要有三种含义,一是服务,二是服务端程序中包含的一种服务(也就是一个函数),三是动词提供服务。
2. 侧重不同server侧重于表示提供服务的硬件设备,比如电脑服务器、云服务器等。
service侧重于表示软件层面上的服务,比如应用程序、系统服务等。
3. 语境不同在计算机领域,server通常指的是服务器,是提供计算服务的设备。
service通常指的是应用程序或系统服务,是一种软件层面的服务。
4. 总的来说,server和service虽然都与“服务”有关,但在实际使用中,需要根据具体的语境来选择使用哪个词。


}


测试
{
端口分 tcp、udp

测试TCP,使用telnet:
1. telnet 使用的是 tcp 协议
2. telnet 公网IP 端口号
能连接成功说明可以访问

测试UDP,使用netcat
1.服务端使用 nc -uvlp 端口号
2.客户端使用 nc -u 公网IP 端口号
客户端发送消息后服务端能回显说明可以访问

检查linux防火墙
systemctl status firewalld
iptables -nL

查看IP
https://www.ip138.com/

查看端口号占用情况
netstat -ano

查看活动连接数
netstat -antpu |grep 8888

 

}

 


$ 网络多服务器多线程
{

网络编程中, 经常要使用到回调函数。 当底层的网络框架有数据过来时,往往通过回调函数来通知业务层。 这样可以使网络层只专注于 数据的收发, 而不必关心业务
在c语言中, 回调函数的实现往往通过函数指针来实现。 但是在c++中 , 如果回调函数是一个类的成员函数。这时想把成员函数设置给一个回调函数指针往往是不行的
因为类的成员函数,多了一个隐含的参数this。 所以直接赋值给函数指针肯定会引起编译报错
c++11 为我们带来了bind, 可以很好的解决这个问题
//用于线程执行类成员方法
}


$ TCP相关函数
{

//在TCP中,socket创建的套接字叫监听套接字,用于获取通信连接

1.listen
man : man 2 listen
声明:int listen(int sockfd, int backlog);
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
a. sockfd:监听套接字
b. backlog
返回值:
成功返回0,错误返回-1,并设置错误码


2.accpet
man : man 2 accpet
声明:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
头文件:
#include<sys/types.h>
#include<sys/socket.h>
参数:
a. sockfd:监听套接字,socket函数的返回值,
b/c. 输出型参数,返回客户端的套接字地址
返回值:
返回tcp服务的文件描述符

3.connect
man 2 connect
声明:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:和accpet相同,用于连接服务端,向目标地址发起连接
返回值:连接(或绑定)成功,返回0,否则返回-1,并设置错误码
//建立连接成功后会自动bind


}

TCP客户端/服务器
{
服务器:监听,TCP需要监听是否有需要与服务器建立连接的客户端

服务器:
1.不需要监听(listen).客户端只需要向服务器发起连接请求
2.connect成功后,会自动bind

 

}

TCP中信息传递手段
{
1.read/write


}

TCP错误问题
{
./server: socket bind error :Address already in use

}

 

日志
{


#include <stdarg.h>

int vprintf(const char *format, va_list ap); //只需要初始化ap,格式转换由vprintf完成,然后打印到显示器
//如果不想打印到显示器上,下面3个打印到字符串,文件等其他缓冲区中...
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

 

 

$ 时间接口
1.time
2.gettimeofday
3.localtime


}
------------------------------------------------------
$ C语言可变参数用例演示

enum
{
DEBUG = 0,
INFO,
WARNING,
ERROR,
FATAL,
UKNOWN //nknown
};

static std::string toLevelString(int level)
{
switch(level)
{
case DEBUG:
return "DEBUG"; //开源项目中很多是直接return,不使用break
case INFO:
return "INFO";
case WARNING:
return "WARNING";
case ERROR:
return "ERROR";
case FATAL:
return "FATAL";
default:
return "NKNOWN";
}
}

static std::string getTime()
{
//struct tm
// int tm_sec; /* Seconds. [0-60] (1 leap second) */
// int tm_min; /* Minutes. [0-59] */
// int tm_hour; /* Hours. [0-23] */
// int tm_mday; /* Day. [1-31] */
// int tm_mon; /* Month. [0-11] */
// int tm_year; /* Year - 1900. */
// int tm_wday; /* Day of week. [0-6] */
// int tm_yday; /* Days in year.[0-365] */
// int tm_isdst; /* DST. [-1/0/1]*/


time_t curr = time(nullptr);
struct tm *tmp = localtime(&curr); //参数事time_t类型的时间戳
char buffer[128];
snprintf(buffer,sizeof(buffer),"%d-%d-%d %d:%d:%d",tmp->tm_year+1900,tmp->tm_mon+1,tmp->tm_mday,tmp->tm_hour,tmp->tm_min,tmp->tm_sec);
return buffer;
}

//日志格式: 日志等级 时间 文件 行 pid 消息体
void logMessage(int level, char *format, ...)
// levet:日志等级
// char * format, ... : 格式化可变参数 (可变参数要求必须有一个具体的参数) // format实例:"hello %d %s"
// 可变参数列表分为:具体参数+可变参数部分,即 具体类型+...
{
//预备知识
//va_list p;////定义一个指针// VC5.0: typedef char *va_list 或其他平台 void*
//va_start(p,format); //初始化,让p指向可变参数的 可变参数部分的起始地址,在紧贴着farmat之后
//va_arg(p,int); // 根据类型提取参数,那我怎么知道类型是什么? --> C语言%d,%s这些格式控制就是类型
//va_end(p); //很简单,就是不使用了,置空,p=NULL

//封装:stdarg.h --vprintf系列
char logLeft[1024] ; //左半部分:日志等级 时间 pid //文件和行有点麻烦, 先不做处理
std::string level_string = toLevelString(level);
std::string curr_time = getTime();
snprintf(logLeft,sizeof(logLeft),"[%s] [%s] [%d] ",level_string.c_str(),curr_time.c_str(),getpid());

char logRight[1024]; //右半部分:消息体
va_list p;
va_start(p,format);
vsnprintf(logRight,sizeof(logRight),format,p);
va_end(p);

//打印
printf("%s %s \n",logLeft,logRight);

//或者保存到文件中,以后只需要在logMessage中修改定义就可以实现日志信息的流向


}

 


$ linux ps axj/efj 补充
{
[chj@47 ~ 17:47:27]$ ps axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
进程组Id 会话ID 终端

# PID.PPID.PGID.SID
.Process ID(PID)
Linux中标识进程的一个数字,它的值是不确定的,是由系统分配的(但是有一个例外,启动阶段,kernel运行的第一个进程是init,
它的PID是1,是所有进程的最原始的父进程),每个进程都有唯一PID,当进程退出运行之后,PID就会回收,可能之后创建的进程会分配这个PID
.Parent Process ID(PPID)
字面意思,父进程的PID
.Process Group ID(PGID)
PGID就是进程所属的Group的Leader的PID,如果PGID=PID,那么该进程是Group Leader
.Session ID(SID)
和PGID非常相似,SID就是进程所属的Session Leader的PID,如果SID==PID,那么该进程是session leader
.Session和Group都是管理多个进程的方式,同一个Group的进程属于同一个Session,一个Session里可以包含多个Group
意义
group和session虽然都是进程的集合,但是他们的意义不同。fork出的子进程,会继承group和session(PGID和SID与父进程相同)
session与终端相关(Control terminal),同一个终端启动的进程默认会在一个session里。例如图形界面的终端(比如GNOME按ctrl+atl+T呼出的命令行界面),都是虚拟终端(Virtual terminal),他们实质上只有一个终端在真正起作用,输入w命令,可以看到所有的control terminal。
group则是方便管理,比如发送信号,kill可以一次向一个group的进程发送同一个信号,ctrl+z进入后台、bg、fg都可以对一个group的进程起作用。比如ctrl+z可以将一个group的进程stop暂停运行,fg可以让一个group继续运行

# TTY:终端:终端文件
?号表示 与终端没有关系
pts/25 终端文件,一般在/dev/pts/下 --- pts/4说明这个进程终端关联了4号终端
//就是显示命令行的地方
如 echjo "hello world" > /dev/pts/5 //这样就是直接把hello world输出到终端5
//一般把和终端有关的进程,一般与会话有关系


[chj@47 ~ 18:10:56]$ sleep 4000|sleep 5000 |sleep 6000 &
//...
[chj@47 ~ 18:08:17]$ sleep 10000|sleep 20000|sleep 30000
[chj@47 ~ 18:15:55]$ ps -axj|head -1 && ps axj|grep sleep
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3664 3717 3717 3664 pts/4 3717 S+ 1000 0:00 sleep 10000
3664 3718 3717 3664 pts/4 3717 S+ 1000 0:00 sleep 20000
3664 3719 3717 3664 pts/4 3717 S+ 1000 0:00 sleep 30000
636 3839 628 628 ? -1 S 0 0:00 sleep 60
3723 3840 3840 3723 pts/4 3845 S 1000 0:00 sleep 4000
3723 3841 3840 3723 pts/4 3845 S 1000 0:00 sleep 5000
3723 3842 3840 3723 pts/4 3845 S 1000 0:00 sleep 6000

// 同时打开的,建立了管道通信的3个进程,属于同一个进程组,并且在同一个会话中(管道)
// 属于同一个进程组时,组PID是以第一个进程的PID命名的
//不是同时打开的两个进程,都属于同一个会话
//同一个终端打开的所有进程,都是同一个会话 pts/N

[chj@47 ~ 18:19:07]$ ps -axj|head -1 && ps axj|grep 3723
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3722 3723 3723 3723 pts/4 3871 Ss 1000 0:00 -bash
//说明 会话进程就是bash


小结:
会话>=进程组>=进程
一个会话可以包含多个进程组,一个进程组可以包含多个进程
linux: 当用户登录linux时,linux会给用户创建一个会话,会话中至少有一个自成进程组的bash进程(命令行解释器)
( .进程组:用来组合式完成某种任务的几个进程.在用户视角,可以把进程组当作一个任务,这些进程组共同完成一个任务.
进程组还分为 前台进程组,和后台进程组 //前台的意思就是让谁(哪一个进程组)去用这个终端
//一个会话中任何时刻都只能有一个前台进程组在运行 --- 其他程序就无法从终端启动了
//把前台任务ctrl+C干掉后,bash会自动成为前台任务.
)
$ .打开一个终端(用户在别的终端登录,或者其他用户登录) === 创建新的会话 ---> 新的SID
$ .退出会话(退出登录)时,会销毁会话 --- > 可能会影响会话内部的所有任务/进程组(可能存在,可能会全部干掉,但不能保证不受影响)
$ .因此在linux中(不同OS不同做法),一般的网络服务器,为了不受到用户的登录注销的影响, 会以守护进程的方式进行运行

}


$$$ linux -- shell中控制进程组的命令
{
# jobs :查看当前会话的后台任务(看不到别的终端的) ,并且显示后台任务的任务号( [1]方括号括起来的那个编号就是)
[chj@47 ~ 18:37:59]$ jobs
[1]+ Running sleep 4000 | sleep 5000 | sleep 6000 &

# jobs -l //能看到PID
[chj@47 ~ 18:39:06]$ jobs -l
[1]+ 3840 Running sleep 4000
3841 | sleep 5000
3842 | sleep 6000 &

# fg 任务号 //将后台任务调到前台
[chj@47 ~ 18:39:07]$ fg 1
sleep 4000 | sleep 5000 | sleep 6000
(恢复成前台卡住状态)

# ctrl+z //将前台任务暂停,并自动转入后台

# bg 任务号 //将暂停的后台任务恢复运行

}


守护进程:
{

.即独立在会话外,与用户登录的生成的会话成并列关系的进程组
.本质是孤儿进程的一种

关闭守护进程:kill -9 或killall

1.setsid 接口
man 2 setsid
头文件: #include <unistd.h>
声明: pid_t setsid(void);
$ 作用:谁调setsid(),就把谁设成一个新的独立会话
返回值:成功,返回调用进程的新的会话id,失败返回-1并设置错误码
$ 条件:
1.调用的进程不能是组长
2.守护进程要忽略异常信号
3.文件描述符0,1,2(标准输入输出,和标准错误)要做特殊处理
4.进程的工作路径可能要更改,可能想要改成根目录,不想在某个用户的目录下

# /proc/[pid] //pid对应的进程所在的工作目录/
$ /proc:内存文件系统,里面放的是当前系统实时的 进程信息。
[chj@47 ~ 19:59:45]$ ll /proc/4843
total 0
dr-xr-xr-x 2 chj chj 0 Jan 23 20:00 attr
-rw-r--r-- 1 chj chj 0 Jan 23 20:00 autogroup
-r-------- 1 chj chj 0 Jan 23 20:00 auxv
-r--r--r-- 1 chj chj 0 Jan 23 19:59 cgroup
--w------- 1 chj chj 0 Jan 23 20:00 clear_refs
-r--r--r-- 1 chj chj 0 Jan 23 20:00 cmdline
-rw-r--r-- 1 chj chj 0 Jan 23 19:59 comm
-rw-r--r-- 1 chj chj 0 Jan 23 20:00 coredump_filter
-r--r--r-- 1 chj chj 0 Jan 23 20:00 cpuset
lrwxrwxrwx 1 chj chj 0 Jan 23 19:59 cwd -> /home/chj/git_repositorier/linux_code/22.socket_service/3.TCP_Version
// 进程当前的工作目录/路径,默认是启动时所在的目录
-r-------- 1 chj chj 0 Jan 23 20:00 environ
lrwxrwxrwx 1 chj chj 0 Jan 23 19:59 exe -> /home/chj/git_repositorier/linux_code/22.socket_service/3.TCP_Version/server
//可执行程序文件所在的路径
dr-x------ 2 chj chj 0 Jan 23 20:00 fd
dr-x------ 2 chj chj 0 Jan 23 20:00 fdinfo
-rw-r--r-- 1 chj chj 0 Jan 23 20:00 gid_map
-r-------- 1 chj chj 0 Jan 23 20:00 io
-r--r--r-- 1 chj chj 0 Jan 23 20:00 limits
/... 略


2.daemon // 运行到后台 -- Linux提供的守护进程接口
头文件: #include <unistd.h>
声明: int daemon(int nochdir, int noclose);
参数:
a. nochdir==no change dir :是否更改工作目录,默认是不修改. 1为真,即不修改. 0就是false,修改成根目录\
b. noclose :要不要关闭0,1,2.默认是1,不关闭. 如果为0,false,daemon将重定向标准输出,输入,错误到 /dev/null(黑洞,丢失了).否则不修改
//程序员喜欢自己写.daemon

# /dev目录:字符型设备目录,
/dev/null: 所有linux都存在,信息黑洞,任何向这个文件内写的,都会被丢弃,从这里读, 什么都读不到,会立刻返回/结束
功能: 用于实现类似close(fd)的效果,比close温柔.不会影响到cout输入输出流等
.

}

【Networkk】一篇文章完全搞清楚 scoket read/write 返回码、阻塞与非阻塞、异常处理 等让你头疼已久的问题
https://www.cnblogs.com/junneyang/p/6126635.html
---------------------------------------------------------------------- new socket___END

 


$ 序列化与反序列化(属于编码设计)
{
. 网络中结构化数据发送
解决方案:将结构化数据转换成一条大字符串发送,接收端再把这条大字符串转换成结构化数据,
---- 在这个过程中,发送端做的是序列化,接收端做的是反序列化
. 序列化与反序列化的作用:方便网络通信

? 接收端如何知道要把接收到的数据转成什么格式呢? ---> 协议

. 协议本质就是双方约定好的某种格式的数据,常见的就是用结构体或者类来进行表达

.约定方案
. 定义结构体来表示我们需要交互的信息;
. 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
. 这个过程叫做 "序列化" 和 "反序列化"


}


telnet工具
{
启动:
# telnet IP Port
# ctrl+] 进入telnet命令行


}

 


防火墙关闭:
1.sudo systemctl stop firewalld
2.sudo systemctl disable firewalld


md5sum:linux计算md5工具 ---- 在git bash中,叫做md5sum.exe
CLI:md5sum 文件1 文件2 ...
certutil:windows计算md5工具(CLI:command line interface)
CLI:certutil -hashfile 文件 md5

 

标签:addr,int,IP,chj,编程,基础知识,地址,linux,进程
From: https://www.cnblogs.com/DSCL-ing/p/18038556

相关文章

  • Linux使用命令行编译并用st-link烧录STM32
    创建工程在STM32CubeMX中配置,选择Makefile并生成。环境安装编译工程需要用到arm-none-eabi,去官网下载对应系统版本,下载后解压到任意位置。添加环境变量添加环境变量到.bashrc文件:echo'exportPATH="/toolchain/arm-none-eabi/bin:$PATH"'>>~/.bashrc我解压的位置为/too......
  • linux基本知识汇总1(基础命令) 20000字汇总
    linux版本号主版本号.次版本号.修正次数2.6.30--次版本号为偶数:稳定版奇数:测试版$$$$命令选项查看方式1.内建命令(help)格式:help+内建命令####help命令//命令使用说明2.外部命令(–help)一般是Linux命令自带的帮助信息,并不是所有命令都自带这个......
  • Windows下写脚本无法运行在linux上?怎麽办?
    Windows下写脚本无法运行在linux上?怎麽办?$‘\r‘:commandnotfound的解决方法在Linux系统中,运行Shell脚本,出现了如下错误:one-more.sh:line1:$'\r':commandnotfound1出现这样的错误,是因为Shell脚本在Windows系统编写时,每行结尾是\r\n,而在Linux系统中行每行结尾是\n,......
  • [1] C++编程语言
    week9day1 输出指令//控制台打印std::cout<<"HelloWorld";//简化std命名空间usingnamespacestd;//转义字符cout<<"\n";//\n会被渲染成前面有\的前提下\n不会被渲染cout<<"\\n";\n;<<endl;换行; 系统指令//system()可以调用CMD......
  • Linux 命令指南
    做这个东西有两个用处,一是初赛会考,二是考场上用windows哪里数组越界你都不知道直接RE爆炸。sudo-s输入后填写密码获得管理员权限。cd打开文件或者目录,用法是cd目录名。cd/退回到根目录。mkdir创建一个目录,使用方法为mkdir目录名。ls显示当前目录下的......
  • 安装虚拟机(Linux)
    安装虚拟机的过程:1.创建虚拟机2.不用动,点击下一步3.选择稍后安装操作系统,然后继续点击下一步4.选择Linux,版本选择CentOS7645.设置虚拟机的名称以及位置6.设置磁盘大小7.点击完成8.配置虚拟机9.设置内存10.设置处理器11.配置ISO映像文件12.打开虚拟机1......
  • linux虚拟机安装
    1.点击文件——新建虚拟机或创建新的虚拟机2.选择典型点击下一步3.选择稍后安装操作系统,点击下一步4.客户机操作系统选择Linux(L),版本选择CentOS764位5.可随意更改名称,创建文件夹6.设置虚拟机可用内存7.虚拟机创建完成点击完成即可......
  • Python并发编程的协程
    一、协程的概念1、协程是单线程下的并发,它是程序员级别的,我们来控制如何切换。2、进程的开销>>>>>>线程的开销>>>>>>协程的开销3、协程的使用需要借助于第三方模块gevent模块或者asyncio模块4、gevent 和 asyncio 是两个在Python中常用的协程框架,它们都提供了在......
  • 2024-02-27-物联网系统编程(7- 共享内存)
    7.共享内存7.1共享内存概述​共享内存允许两个或者多个进程共享给定的区域共享内存的特点共享内存是进程间共享数据的一种最快的方法;一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。使用共享内存要注意的是多个进程之......
  • 《安富莱嵌入式周报》第333期:F35战斗机软件使用编程语言占比,开源10V基准电源,不断电运
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 视频版:https://www.bilibili.com/video/BV1y1421f7ip目录:1、F35战斗机软件使用编程语言占比2、开源10V基准电源,不断电运行一年,误差小于1uV3、资讯(1)苹果开源配置语言Pkl......