当前位置:   article > 正文

从TCP/IP协议到socket编程详解_tcpip socket

tcpip socket


我的所有学习笔记:https://github.com/Dusongg/StudyNotes⭐⭐⭐


文章目录

1 网络基础知识

  1. 查看网络信息
  • image-20240203025630400

  • image-20240203025654238

  1. 网口的 IP 地址、子网掩码、MAC 地址、网关地址
  2. 每层数据包的称呼

image-20240319142228157

1.1 查看网络信息

netstat -naup
# -p : 显示pid  sudo显示全部
# -a : 显示所有(all)
# -u : 显示udp信息
# -n : 将ip和mac地址写成数字
# -l : 仅列出listen状态的服务
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.2 认识端口号

  1. 用户使用应用层软件,完成数据发送和接收,本质是进程间通信
  2. 传输层怎么知道将报文交给哪一个上层应用 —— 端口号
  3. 在公 网上,IP地址表示唯一的一台主机,端口号port用来标识主机上唯一的一个进程
  4. 端口号与pid:两者都能标识进程的唯一性
    1. 不是所有进程都需要网络通信
    2. 系统和网络功能解耦
    3. 一个进程可以绑定多个端口号,一个端口号不能被多个进程绑定
  5. 端口号占两个字节

img

  1. 0-1023 :知名端口号,系统内置的,通过cat /etc/services可以查看(HTTP:40, HTTPS:443)

1.3 UDP

  • udp:无连接、不可靠传输、面向数据报

    • 面向数据报:应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并;
  • udp报头:双方规定了报文的固定长度

image-20240303203855722

  • 内核接收发放udp,通过结构体sk_buf管理多个udp报文
  • image-20240303210143940

1.4 TCP

  • tcp:有链接、可靠传输、面向字节流

image-20240329150219897

  • 首部长度:四位(0-15),基本单位4字节,所以首部长度范围为0-4*15

  • 窗口大小:填写自己接收缓冲区剩余空间的大小(在三次握手时,可以协商窗口大小)

  • 序列号:发送缓冲区中此次发送数据的最后一个下标

  • 确认号:序列号 + 1,表示确认号之前的数据都收到了,下次从该确认号开始发

  • 选项:

    • 最大报文长度(MSS): 在TCP连接的建立阶段,可以通过选项告知对方本端能够接收的最大报文长度,从而避免因为报文过长而导致的分片和重组,提高传输效率。
    • 窗口扩大因子: 用于在连接建立后通知对端本端接收窗口的扩大情况,从而提高数据传输的效率。
    • 时间戳: 可以在选项中包含时间戳信息,用于测量往返时间(RTT),对数据包进行排序等。
    • SACK(Selective Acknowledgment): 用于告知对方本端已经正确接收到的数据段,从而提高数据传输的可靠性。
    • 窗口比例因子: 用于在连接建立时通知对端本端接收窗口的规模,从而提高数据传输的效率。
  • 六个标记位 -> 区分报文的“类型”:

    • URG:紧急指针,指向数据的偏移量,紧急数据一般只能站数据的一个字节

      • 通过将sendrecv的第四个参数设置成MSG_OOB
      • image-20240310211940666
      • image-20240306181137163image-20240306181240030
      • 11
      • 应用场景:当服务器繁忙,无法处理用户数据时,用户可以采取发送紧急数据,服务端收到后立即处理(由于紧急数据很小),然后返回服务器繁忙的原因给客户端。
    • ACK:确认序号

    • PSH(push):当接收方缓冲区空间不足,上层没有取走数据时,PSH被设置,提示接收端应用程序立刻从TCP缓冲区里把数据取走

    • RST:当己方认为链接不一致或链接异常时,要求重新建立链接;RST称为复位报文段(建立链接时、或者正在通信服务端异常时)

      • 三次握手建立链接时:当client的ack发送之后就认为链接建立好了, 此时开始发数据,而如果此时最后一次ack服务端没有收到,但是收到了客户端发来的数据,则此时服务端发送报文将RST设置,表示重新建立链接
      • image-20240306180041178
    • SYN:请求建立链接;把携带SYN标识的称为同步报文段

    • FIN:通知对方,自己要断开链接了

1.4.1 确认应答机制

1.4.2 TCP三次握手/四次挥手

image-20240306194340418

为什么是三次握手
  1. 验证全双工,如果两次,有一方只能验证收/发其中一种能力
  2. 如果单次握手:服务端一旦收到SYN就建立连接 --> SYN泛洪
  3. 如果两次握手:当服务端收到SYN时就必须建立链接并发送ack,并且不知道客户端是否收到ack,导致当客户端没有收到ack并重传SYN时,服务端又会建立一次链接
  4. 如果时三次握手:第三次握手丢失,此时客户端建立链接,而服务端并没有建立链接,之后客户端重新建立链接,使得建立链接失败导致的后果由客户端承担,减少了服务端处理错误链接的负担
  • 第三次握手可以携带数据
为什么是四次挥手

一方关闭链接之后,另一方可能还有数据向对方发送,所以这时ACK与FIN不能同时发送

但是在特定情况下四次挥手可以变成三次挥手

listen 的第二个参数 backlog—— 全连接队列的长度

image-20240309164824945

The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that a later reattempt at connection succeeds.

  • 现象,服务端没有accept,客户端和服务端也能建立链接,建立连接的数量是n+1,如果超过这个数量,客户端链接显示ESTABLELISH,而服务端显示SYN_RECV,此时全连接队列满了(服务端收到了客户端的ACK,只是将他丢弃了),服务端将链接存放在半连接队列里,如果长时间没有处理这个半连接,服务端会丢弃该链接

  • n不能太长?

  • n不能没有?

TIME_WAIT

主动断开连接的一方,在四次挥手完成之后,进入TIME_WAIT状态,等待一段时间后自动释放;

因此当服务器主动断开,四次挥手之后,服务器无法立即重启

  • 使用链接复用
setsockopt
int opt = 1;
setsockopt(listensock, SOL_SOCKET, SO_REUSEPORT | SO_REUSEADDR, &opt, sizeof(opt));
  • 1
  • 2
  • 为什么要有TIME_WAIT?

    • 最后一次ACK丢弃的情况
    • 等待接收对方最后发来的数据,让之前的数据消散,防止影响后续的链接
  • TIME_WAIT等多久? —— 2MSL

    • MSL(maximum segment lifetime) :一个报文在网络存活的时间

    • 查看$ cat /proc/sys/net/ipv4/tcp_fin_timeout

服务端出现大量CLOSE__WAIT?

CLOSE_WAIT状态是被动关闭方收到FIN并进行ACK回复后进入的状态,接下来他会等待上层知道对方要关闭连接后做出处理,当自己也要关闭连接的时候给对方发送FIN,则 进入LAST_ACK状态,而一旦自己上层没有做出处理,则套接字状态会一直处于CLOSE_WAIT, 因此Server端出现大量的CLOSE_WAIT,是由于Server端没有及时的关闭连接导致的。

1.4.3 超时重传

image-20240306194247544

  • 超时重传的时间间隔?

动态的,和网络状况有关

Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时
时间都是500ms的整数倍。如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传。如果仍然得不到应答, 等待 4500ms 进行重传. 依次类推, 以指数形式递增。累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接

1.4.4 流量控制

  • TCP根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制(Flow Control)

  • 第一次怎么保证数据量的合理性:三次握手时交换了窗口大小,协商了双方的接收能力

image-20240309204128448

实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位;

1.4.5 滑动窗口

  • 滑动窗口的大小默认为对方的接收缓冲区剩余大小
  • 滑动窗口是发送缓冲区的一部分
  • 滑动窗口越大,代表着网络吞吐量越大

image-20240312000132989

  1. 如果丢包了,滑动窗口怎么变化?

    由于ack序号sqn表示sqn之前的全部报文都已经收到了,所以运行少量之前的ack丢失

    image-20240312001153618

  2. 但是如果发送过来的数据报丢了呢?比如上述3001-4000的报文丢失了,那么如果收到了4001-5000,服务端不能将确认序号设置成5001,而必须是3001

  3. 如果收到三次重复确认应答,则立即对丢失的报文补发 —— 快重传

    image-20240312001855256

  4. 已经有了快重传了,为什么还要又超时重传?

  5. 发送缓冲区逻辑上为环状结构,不用担心窗口越界问题

1.4.6 延迟应答

当接收方收到报文时,等待一段时间,让应用层取数据,此时缓冲区腾出更多剩余空间,发送ACK时携带窗口大小,下次发送报文时就能发送更多的数据(窗口越大,网络吞吐量越大,传输效率越高)

1.4.7 拥塞控制

  • 如果通信双方出现大量的数据包丢失,tcp会判断网络拥塞,此时不能立即对报文进行超时重传(会加重网络拥塞)

  • 如果一个网络发送拥塞,通过这个网络通信的主机都需要发生拥塞控制

  • 实际滑动窗口大小:min(窗口大小,拥塞窗口)

    • 窗口大小:考虑对方的接受能力
    • 拥塞窗口:考虑网络的接受能力

    image-20240312135608312

1.4.8 TCP粘包问题

  • 对于定长的包, 保证每次都按固定大小读取即可; 例如上面的Request结构, 是固定大小的, 那么就从缓冲
    区从头开始按sizeof(Request)依次读取即可;
  • 对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置;
  • 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔
    符不和正文冲突即可);

总结

image-20240312133341027

1.5 网络字节序

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址存放高字节

  • 网络字节序与主机字节序的转换函数

image-20240125123517540

1.6 查看所有http守护进程pid —— pidof

pidof httpd

  • 删除所有http守护进程 :pidof httpd | xargs kill -9

1.7 IP(网络层)

1.7.1 IP报头

  • ip = 目标网络 + 目标主机

image-20240318234728884

  • 和tcp一样首部长度20-(2^4-1) * 4 = 60
  • TTL:
  • 8位协议:将该报文交付给上层的指定协议
  • 16位标识:标识所有的分片
  • 3位标志:第二位为0表示允许分片,第三位用来表示分片结束标记
  • 13位片偏移:表示该报文在原始报文当中的位置 ,用于分片的排序组装,
IP分片
  1. ip数据报中任何一个分片丢失都要重新发送

image-20240319153147247

1.7.2 路由器

image-20240319003933596

image-20240319004225413

1.7.3 IP地址分类和CIDR

  • image-20240319005825319

  • image-20240319005910557

网络号 = IP地址 & 子网掩码

1.7.4 公网IP和私网IP

  • 10.* , 前8位是网络号,共16,777,216个地址
  • 172.16.* 到172.31.,前12位是网络号,共1,048,576个地址
  • 192.168.* , 前16位是网络号,共65,536个地址
    包含在这个范围中的, 都成为私有IP, 其余的则称为全局IP(或公网IP);

1.7.5 局域网/广域网

image-20240319153858666

  • 局域网按照传输介质使用的访问控制方法,可以分为以太网、FDDI网和令牌网,目前广泛使用的是以太网,它以集线器或交换机为中心构成

1.7.6 网关?网段?默认网关?

  • 网段:IP地址通过子网掩码的到的网络号

  • 网关:

    网关(Gateway)是在计算机网络中用作连接不同网络的设备或程序。它可以是物理设备,如路由器,也可以是运行网络协议的计算机,用于转发数据包到目标网络中的设备。网关的作用是在**不同网络之间传输数据**,并在需要时执行转换或处理数据的功能。

    在互联网中,ISP(Internet Service Provider,互联网服务提供商)通常会提供网关来连接用户的局域网与互联网。局域网中的设备通过网关访问互联网上的资源,网关负责将数据包从局域网传输到互联网,并将来自互联网的响应数据包传输回局域网。

1.8 MAC (数据链路层)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

区分同一个局域网中的不同主机

  1. 通过定长报文实现解包和封装
交换机

划分碰撞域——减少局域网内的碰撞

ARP协议

arp - a:查看arp缓存

image-20240322235324943

  1. 通过将数据链路层,加上mac报头(类型:0806),目的地址(全F:广播地址)
  2. 通过op分别,请求/响应ARP报文
ARP欺骗

一个中间人向同一子网的另一个主机发送ARP响应(IP:路由器IP, MAC: 中间人MAC),之后这台主机发送给路由器的报文会发送给中间人主机

1.9 DNS —— 域名解析

host文件:cat /etc/hosts

默认端口号:53

1.10 NAT/NAPT

  • NAT

    • 静态NAT:实现固定私网主机地址到公网地址的一对一转换,适用于上网用户少,且同时上网用户数量与公网地址数量相同的场景
    • 动态NAT:私网主机地址与公网地址的动态转换,并没有固定映射关系
  • NAPT路由器的转换表通常在TCP握手时生成,在FIN时删除

image-20240323181551313

1.11 内网穿透 frp/frps/frpc

image-20240323183643371

frp是一种用于将本地服务暴露到公共网络的工具,通常用于内网穿透。其全称为"Fast Reverse Proxy",是一个开源的、高性能的反向代理应用。通过frp,您可以在外部网络访问本地网络中的服务,而无需公网IP或端口映射。

frp工作原理是在公网服务器上部署一个frp服务器和一个frp客户端,然后在需要暴露的本地服务器上部署一个frp客户端。frp客户端会将本地服务的流量通过加密通道发送到frp服务器,frp服务器再将流量转发到公网访问者。这样,即使本地服务器位于内网或没有公网IP,也能实现被公网访问的效果。

frp可以用于很多场景,比如远程监控、内网穿透、局域网共享等,是一个非常实用的网络工具。

1.12 正向代理/反向代理

正向代理和反向代理是两种不同的代理服务器配置方式,它们在网络中的作用和工作方式有所不同。

  1. 正向代理:
    • 定义:正向代理(Forward Proxy)是代理服务器在客户端和目标服务器之间的中介,代表客户端向目标服务器发送请求。
    • 用途:用于隐藏客户端的真实IP地址,访问被墙的网站,加速访问等。
    • 工作方式:客户端向正向代理发送请求,代理服务器将请求转发给目标服务器,然后将目标服务器的响应返回给客户端。
  2. 反向代理:
    • 定义:反向代理(Reverse Proxy)是代理服务器位于目标服务器和客户端之间的中介,代表目标服务器向客户端发送响应。
    • 用途:用于负载均衡、安全性、缓存、SSL终结等。
    • 工作方式:客户端向反向代理发送请求,反向代理根据配置将请求转发给一个或多个目标服务器,然后将目标服务器的响应返回给客户端。

总的来说,正向代理隐藏了客户端,代表客户端向服务器发送请求;而反向代理隐藏了服务器,代表服务器向客户端发送响应。两者的主要区别在于代理服务器的位置和功能。

SOCKS5

SOCKS5(Socket Secure 5)是一种**网络协议**,用于在客户端和服务器之间进行数据传输。它是SOCKS协议的第五个版本,相比之前的版本,提供了更多的功能和安全性。

SOCKS5协议通常用于网络代理服务,允许客户端通过代理服务器访问互联网。与HTTP代理不同,SOCKS代理可以传输各种协议的数据,包括HTTP、FTP、SMTP等,更加灵活。此外,SOCKS5还支持身份验证和UDP转发,提供了更高的安全性和功能性。

使用SOCKS5代理可以帮助用户**隐藏真实IP地址绕过地区限制访问特定网站**,提高网络安全性等。许多网络工具和软件都支持SOCKS5代理,使其成为一个常用的网络工具。

VPN

  • 什么是VPN?
  • 定义:VPN是一种通过公共网络(如互联网)建立私密连接的技术,用于在不安全的网络上创建安全的通信管道。
  • 用途:用于保护用户的隐私和安全,突破网络封锁,访问受限制的内容等。
  • 工作方式:通过加密和隧道技术,在公共网络上创建一个安全的通信通道,使得通过该通道传输的数据在传输过程中不易被窃听或篡改。
  • VPN可以看作成正向代理吗?

在某种程度上,可以将VPN视为一种正向代理。正向代理(Forward Proxy)是代理服务器在客户端和目标服务器之间的中介,代表客户端向目标服务器发送请求。VPN也是一种将客户端的请求通过代理服务器转发到目标服务器的技术,因此在这个意义上,VPN可以被视为一种正向代理。

然而,VPN(Virtual Private Network,虚拟专用网络)通常具有更广泛的功能和用途,不仅仅局限于将客户端的请求转发到目标服务器,还包括加密通信、保护用户隐私、突破网络封锁等功能。因此,虽然VPN可以看作是一种正向代理,但它的功能和应用场景可能更加多样和复杂。

1.13 DHCP

  • 端口:客户端监听:68, 服务端监听:67

image-20240325162728664

  • 什么是BOOTP?

image-20240325162944816

2 socket相关函数

image-20240220231102427

image-20240220231540043

  • 套接字的分类:
    • 域间套接字:类似于管道,用于一个主机内的多个进程间通信,以文件的方式标识公共资源
    • 原始套接字:用于编写网络工具,绕过传输层
    • 网络套接字:用于用户间的网络通信

不同类型套接字接口同一参数类型 —— sockaddr* address

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

// 创建 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

image-20240125124436917

2.1 socket()

int socket(int domain, int type, int protocol);
  • 1
  • domain : 域,根据不同套接字类型,创建不同的套接字,网络套接字使用AF_INET(ipv4)或AF_INET5(ipv6)

image-20240125162401922

  • type : 套接字的类型
    • SOCK_STREAM:流式套接字 —— tcp
    • SOCK_DGRAM:数据包套接字 —— udp

image-20240125163314477

  • protocol : 目前填0
  • 返回值:网络文件描述符
int sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
  • 1
  • 分配fd
  • 分配tcp/udp控制块tcp control block/udp control block

2.2 bind

// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
  • 1
  • 2
  • 将字符串地址转为网络的四字节(小端)—— inet_addr
struct sockarr_in local;
bzero(&local, sizeof(local));	//填充字节
//初始化socket结构体的三个字段
local.sin_family = AF_INET;		//域
local.sin_port = htons(port_);	//本主机字节序转网络字节序 —— 端口号
local.sin_addr.s_addr = inet_addr(ip_.c_str());		//将ip地址风格的字符串转uint32_t,再转成网络字节序整型in_addr_t
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

image-20240329155644284

2.2.1 in_addr 与 字符串的转换函数

  1. IP地址字符串转in_addr
  • inet_addr
  • inet_aton

image-20240129173357514

  1. in_addr转IP地址字符串
  • inet_ntoa :函数返回一个动态开辟的字符指针,放在静态区,不需要手动释放,但是重复调用会覆盖之前的结果,该函数不是线程安全

image-20240129170156780

  • inet_ntop:用户自己传缓冲区

image-20240129170635001

inet_aton(ip_.c_str(), &(local.sin_addr));  	
//local.sin_addr.s_addr = inet_addr(ip_.c_str());
//local.sin_addr.s_addr = INADDR_ANY; 
  • 1
  • 2
  • 3
  • 上述socket结构体初始化在用户栈区,通过调用bind将结构体绑定到socket文件中
int ret = bind(sockfd_, (const struct sockaddr *)&local, sizeof(local));
  • 1

2.2.2 关于绑定IP地址

  • IP地址为0:任意地址绑定,收到发送给这台主机任意ip地址的信息(一台主机可能有多个网卡,多个ip地址)
#define INADDR_ANY ((in_addr_t)0x00000000)   //#include<in.h>

local.sin_addr.s_addr = 0;
local.sin_addr.s_addr = INADDR_ANY;    	//两者效果相同
  • 1
  • 2
  • 3
  • 4

2.3 从套接字里收消息 —— recvfrom

image-20240126142212464

char inbuffer[size];

struct sockaddr_in client;	//输出型参数
socklen_t len = sizeof(client);
sszie_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (const struct sockaddr*)&client, &len);
  • 1
  • 2
  • 3
  • 4
  • 5

2.4 发送消息 —— sendto

image-20240126142936051

std::string info(inbuffer);
std::string echo_string = "server echo# " + info;
sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);
  • 1
  • 2
  • 3

2.5 listen:服务器设置为监听状态

image-20240129183155173

  1. listen干了什么?
  • tcb->status = TCP_STATUS_LISTEN
  • 建立两个队列tcb->syn_queuetcb->accept_queue
  1. listen的第二个参数:防止syn泛洪
  • syn队列
  • syn + accept队列总长度,未分配fd的tcb数量
  • accept队列长度

2.6 accept:获取新连接

image-20240129184104924

  • addr,addrlen:获取客户端的socket
  • 返回值:文件描述符
struct sockadd_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);		//listensockfd_是服务端类的成员,由socket()初始化
  • 1
  • 2
  • 3
  • sockfd可能有多个,而listensockfd_只有一个,称为监听套接字

2.7 connect

image-20240130172351602

  • 返回值<0:连接失败

2.8 popen

  • 创建管道,创建子进程,程序替换,将命令执行结果写入文件

image-20240126180336999

3 UDP_Socket & TCP_Socket

  • udp:单线程echo服务器
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>   //htons
#include <string.h>
#include <stdlib.h> //atoi
#include <iostream>

int main(int argc, char* argv[]) {
    // int port = atoi(argv[1]);   //   a.out 8888

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(8888);   
    local.sin_addr.s_addr = INADDR_ANY;   //0.0.0.0

    bind(sockfd, (struct sockaddr*)&local, sizeof(local));

    char buffer[1024]{0};
    while(true) {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        ssize_t ret = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client, &len);
        buffer[ret] = 0;
        sendto(sockfd, buffer, ret + 1, 0, (struct sockaddr*)&client, len);        
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • tcp:单线程echo服务器
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>   //htons
#include <string.h>
#include <stdlib.h> //atoi
#include <iostream>
#include <unistd.h>

int main(int argc, char* argv[]) {
    // int port = atoi(argv[1]);   //   a.out 8888

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

    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(8888);   
    local.sin_addr.s_addr = INADDR_ANY;   //0.0.0.0

    bind(listenfd, (struct sockaddr*)&local, sizeof(local));

    listen(listenfd, 5);

    char buffer[1024]{0};
    while(true) {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
        int ret = read(clientfd, buffer, sizeof(buffer));
        buffer[ret] = 0;
        write(clientfd, buffer, ret);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

4 后台任务

  • 通过在命令末尾加上 &将一个进程启动成后台进程

  • jobs : 查看后台任务

  • fg: 将后台任务提到前台

  • ctrl + z: 将一个前台进程暂停,之后将这个暂停的进程提到后台,将bash进程自动提到前台

  • bg:将一个暂停的后台进程运行在后台

  • PGID:进程组id,任务和进程组的关系:一个任务被一个进程组执行

  • SID:会话id(session id) : bash进程的进程id

  • image-20240131143715888

  • TTY:控制终端

    TTY是指终端设备,通常是用户与计算机交互的界面。每个进程都可以有一个关联的TTY。TTY可以是物理终端(如键盘和显示器),也可以是虚拟终端(如SSH会话或终端仿真器)。TTY的相关信息可以通过/dev/tty/dev/pts/*来表示。

  • TPGID:终端进程组ID,TPGID 的主要作用之一是确定前台进程组。在一个终端会话中,只有一个进程组是前台进程组,它接收来自终端的输入,当用户在终端中启动一个新的命令时,该命令通常会成为前台进程组的一部分,从而接收来自终端的输入

    image-20240329185733458

4.1 终端、bash、前/后台进程、会话

image-20240329175724224

  • bash进程是一个会话的session leader

linux中一个会话和bash进程的关系:

在Linux中,一个会话(session)是一个用户与系统进行交互的一段时间。一个会话可以包含一个或多个进程,而这些进程通常与一个终端(terminal)相关联。终端是用户与系统进行文本输入和输出的界面,可以是物理终端、虚拟终端(比如终端仿真器,如xterm、gnome-terminal等)或者通过SSH等方式远程连接的终端。

每个进程都有一个唯一的进程ID(PID),而在一个会话中,有一个进程被认为是"控制终端"的拥有者。通常,这个进程是用户登录时启动的shell进程,比如bash。该shell进程成为会话的领头进程(session leader)。

与这个会话相关的进程,无论是直接子进程还是孙子进程,都共享同一个会话ID(SID)。这使得它们能够共享一些会话级的属性,比如控制终端、作业控制等。

当你启动一个终端并登录系统时,通常会话开始,一个shell进程成为领头进程,而你与该shell的交互都属于这个会话。当你在该终端中运行其他命令时,它们将成为这个会话的子进程。

在一个会话中,可以通过ps命令或者pstree命令查看进程树,这有助于理解会话与进程之间的关系。例如,你可以使用以下命令查看当前会话中的进程树:

ps -ejH
  • 1

或者使用pstree命令:

pstree
  • 1

总的来说,一个会话是一个用户与系统交互的时间段,而相关的进程树则包括会话领头进程和其衍生的子进程。 Bash进程通常是会话的领头进程,而其他在Bash中运行的命令则是该会话的子进程。

4.2 会话退了,后台进程会如何

当一个会话(session)终止时,会话中的所有进程都会收到SIGHUP信号(挂起信号)。这通常发生在用户从控制终端注销(logout)时。收到SIGHUP信号后,进程会有不同的行为取决于进程对该信号的处理方式:

  1. 默认情况下,大多数进程会终止(terminate)。
  2. 某些进程可能会忽略SIGHUP信号,继续运行。这通常是后台进程(daemon)所采取的策略,以便继续在后台执行。

因此,会话终止可能导致会话中的后台进程终止或继续运行,具体取决于这些进程的信号处理方式。

4.3 后台进程与守护进程的区别

后台进程和守护进程的主要区别在于其目的和特性:

  1. 目的:
    • 后台进程:后台进程是指不与终端直接关联的进程,它们可以在后台运行而不影响用户终端的操作。这种设计使得用户可以在终端上执行其他任务而不受后台进程的影响。
    • 守护进程:守护进程是一种特殊的后台进程,其设计目的是在系统启动时启动,并在系统关闭时关闭。它们通常用于执行系统级任务,如网络服务、日志记录等。
  2. 控制终端:
    • 后台进程:后台进程不与任何控制终端直接关联。
    • 守护进程:守护进程也不与任何控制终端直接关联,它们通常在启动时会调用setsid函数创建一个新的会话,从而使守护进程脱离终端控制。
  3. 生命周期:
    • 后台进程:后台进程的生命周期与其父进程(通常是一个终端会话)相关联。
    • 守护进程:守护进程的生命周期通常与系统的启动和关闭相关联,它们在系统启动时启动,在系统关闭时关闭。

4.4 守护进程

image-20240329184515944

  • **自成进程组自成会话**的进程 —— 在后台运行,不依赖于任何特定的终端或用户会话

创建守护进程 ——将自己变成一个独立的会话

image-20240131145843477

  • 一般,单独的一个进程即进程组只有一个,则它自己就是process group leader,那么如何让进程变成守护进程呢?
int main() {
    //忽略其他异常信号
    signal(SIGCLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    
    if (fork() > 0) exit(0);	//父进程退出
	setsid();	//创建新的会话
    
    //该守护进程可能服务于其他进程,需要更改进程的工作目录
    chdir(cur_cwd);
    
    //由该进程创建的会话不因该占用IO,所以需要“关闭”,即将stdin、stdout、stderr重定向到/dev/null
    int fd = open("/dev/null", O_RDWR);   //O_RDWR:以读写方式打开
    if (fd > 0) {
        dup2(fd, 0);
        dup2(fd, 1);
        dip2(fd, 2);
        close(fd);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.4 daemon —— 创建守护进程

image-20240131160259667

5 HTTP

image-20240224213541177

image-20240224213550736

1 请求方法

1.1 GET 与 POST的区别

  • 数据传输方式

    • GET:通过 URL 参数传输数据,数据在 URL 中可见,适用于传输少量数据。
    • POST:通过请求体传输数据,数据不在 URL 中可见,适用于传输大量数据和敏感信息。
  • 安全性

    • GET:数据在 URL 中可见,不适合传输敏感信息,如密码等。
    • POST:数据在请求体中,相对更安全,适合传输敏感信息。
  • 数据长度限制

    • GET:受浏览器和服务器的限制,通常不能超过 2048 个字符。
    • POST:通常没有固定长度限制,但受服务器配置和网络传输等因素影响。
  • 数据类型

    • GET:仅能传输 ASCII 字符。
    • POST:可以传输二进制数据。
  • 幂等性

    • GET:幂等,多次请求返回的结果应该是相同的。
    • POST:非幂等,多次请求可能会产生不同的结果,如提交订单。
  • 缓存处理

    • GET:可以被缓存,可以被收藏为书签,可被历史记录保存。

    • POST:不能被缓存,不应该被收藏为书签,不会保存在历史记录中。(即,我们为什么无法从浏览器历史记录中查找到POST请求)

      POST 请求通常用于提交表单、执行操作等,这些请求可能会改变服务器状态,因此不适合保存在历史记录中,以免用户在未经意的情况下重复执行这些操作。

      另外,POST 请求中的数据通常是通过请求体传输的,而不是像 GET 请求那样在 URL 中可见,所以即使保存了请求的 URL,也无法完整地还原出 POST 请求所携带的数据。因此,为了避免用户的隐私泄露和意外操作,浏览器通常不会将 POST 请求保存在历史记录中。

  • 使用场景

    • GET:适用于获取资源、查询操作,不应该用于改变服务器状态。
    • POST:适用于提交表单、上传文件、执行操作等,可以改变服务器状态。

1.2 其他常见方法

  1. GET:从服务器获取资源。GET 方法应该只用于获取数据,不应该对服务器上的资源产生其他影响。
  2. POST:向服务器提交数据,用于创建新资源。POST 方法通常用于提交表单数据或上传文件
  3. PUT:向服务器上传更新资源的表示。PUT 方法通常用于更新已经存在的资源,客户端需要提供完整的资源表示。(可以上传数据)
  4. DELETE:从服务器删除资源。DELETE 方法用于删除服务器上的资源。
  5. HEAD:与 GET 方法类似,但服务器只返回头部信息,不返回实际数据。HEAD 方法通常用于获取资源的元数据,如大小或修改日期。
  6. PATCH:对资源进行部分修改。PATCH 方法用于对资源进行局部更新,客户端提供要修改的部分及其新值。
  7. OPTIONS:获取目标资源支持的通信选项。OPTIONS 方法用于查询服务器支持的方法和其他功能,常用于跨域请求中的预检请求。
  8. TRACE:追踪请求-响应的传输路径。TRACE 方法用于在目的服务器端发起一个测试,以查看客户端发送的请求在传输过程中是否被修改。
  9. CONNECT:建立用于代理服务器的隧道连接。CONNECT 方法用于建立与目标资源的双向连接,通常用于加密连接的代理服务器。

1.3 HTTP1.0 与 HTTP1.1 关于请求方法的区别

  • http1.0,三种:post,get,head
  • http1.1,八种:post,get,head,options,put,delete,trace,connect

2 状态码

image-20240224213823056

  • 200 OK」是最常见的成功状态码,表示一切正常。如果是非 HEAD 请求,服务器返回的响应头都会有 body 数据。
  • 204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
  • 206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。

3xx 类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向

  • 301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
  • 302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。

301 和 302 都会在响应头里使用字段 Location,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。

  • 304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,也就是告诉客户端可以继续使用缓存资源,用于缓存控制。

4xx 类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。

  • 400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误。
  • 403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。
  • 404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。

5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。

  • 500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
  • 501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
  • 502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
  • 503 Service Unavailable」表示服务器当前很忙,暂时无法响应客户端,类似“网络服务正忙,请稍后重试”的意思。

3 消息头(字段)

  • Content-Type: 数据类型(text/htmlapplication/jsonimage/jpg

    • 例如当请求图片资源时,服务器需要在html文件中将图片路径带上,并且需要填写图片对应格式的Content-Type
  • Content-Length:Body部分长度

  • Host:客户端告知服务器请求的资源是在哪个主机的的哪个端口上

  • User-Agent:用户的操作系统和浏览器版本信息

  • referer:当前页面是从哪个页面跳转过来的

  • Location:重定向的地址

  • Connection:

    • 短链接(http1.0)
    • 长连接(http1.1)——keep-alive,客户端和服务器协商

3.1 Cookie

  • http协议本身是无状态的,Set-Cookie:服务器向客户端发送,浏览器收到将cookie写入文件,之后客户端向服务器发送http报文会自动携带cookie;
  • cookie数据实在http协议头部字段中传输的

image-20240225190754508

cookie文件:内存级:cookie存放在浏览器进程的虚拟内存中;文件级:cookie存放在浏览器的的某个路径下(磁盘中)

image-20240225192835426

cookie的基础属性

domain:可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”。

path:Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”。

httponly:如果cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击,窃取cookie内容,这样就增加了cookie的安全性,但不是绝对防止了攻击

secure:该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。

expires:指定了coolie的生存期,默认情况下cookie是暂时存在的,他们存储的值只在浏览器会话期间存在,当用户退出浏览器后这些值也会丢失,如果想让cookie存在一段时间,就要为expires属性设置为未来的一个过期日期。现在已经被max-age属性所取代,max-age用秒来设置cookie的生存期

  • cookie安全问题,个人信息泄露?ip地址修改,cookie失效?
session

通过session得到session id,存放再服务端,有服务端管理,避免了cookie内容被窃取

image-20240228185125183

  • 解决cookie信息被盗取,但是别人拿到session id在特定站点,同样能访问到用户在该页面的数据,如何避免:当用户ip地址发生改变时,服务器要求重新客户端认证

6 HTTPS

image-20240227234113624

6.1 对称加密

  • 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对
    称加密,也称为单密钥加密,特征:加密和解密所用的密钥是相同的

  • 常见对称加密算法:DES、3DES、AES、TDEA、Blowfish、RC2等

  • 特点:算法公开、计算量小、加密速度快、加密效率高

6.2 非对称加密

  • 公钥加密-私钥解密;私钥解密-公钥解密

  • 需要两个密钥来进行加密和解密,这两个密钥是公开密钥(public key,简称公钥)和私有密钥
    (private key,简称私钥)

  • 常见非对称加密算法(了解):RSA,DSA,ECDSA

  • 特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对
    称加密解密的速度快。

6.3 非对称 + 对称加密

image-20240228202723199

此时当其他方拿到公钥,也无法会信息解密

6.3.1 中间人攻击

image-20240228203302743

  • 客户端无法知道公钥是否可信(例如图上的公钥M,客户端将其认为是服务器的公钥)

6.4 数字证书

服务端在使用HTTPS前,需要向CA机构申领一份数字证书,数字证书里含有证书申请者信息、公钥信
息等。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,证书就如身份证,证明服务端
公钥的权威性

image-20240303191959817

  1. 如何保证证书的完整性与合法性? CA机构用自己的私钥对证书进行加密,形成数字签名

6.5 数字签名

CA机构签名,客户端验证,防止证书被篡改

image-20240303193040915

6.6 对称 + 非对称 + 证书 —— SSL/TLS协议

23-HTTPS工作流程

  • 特点:算法公开、计算量小、加密速度快、加密效率高

6.2 非对称加密

  • 公钥加密-私钥解密;私钥解密-公钥解密

  • 需要两个密钥来进行加密和解密,这两个密钥是公开密钥(public key,简称公钥)和私有密钥
    (private key,简称私钥)

  • 常见非对称加密算法(了解):RSA,DSA,ECDSA

  • 特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对
    称加密解密的速度快。

6.3 非对称 + 对称加密

[外链图片转存中…(img-8i5waVtf-1711710069811)]

此时当其他方拿到公钥,也无法会信息解密

6.3.1 中间人攻击

[外链图片转存中…(img-3ybQ0IhQ-1711710069812)]

  • 客户端无法知道公钥是否可信(例如图上的公钥M,客户端将其认为是服务器的公钥)

6.4 数字证书

服务端在使用HTTPS前,需要向CA机构申领一份数字证书,数字证书里含有证书申请者信息、公钥信
息等。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,证书就如身份证,证明服务端
公钥的权威性

[外链图片转存中…(img-COzrVvED-1711710069812)]

  1. 如何保证证书的完整性与合法性? CA机构用自己的私钥对证书进行加密,形成数字签名

6.5 数字签名

CA机构签名,客户端验证,防止证书被篡改

[外链图片转存中…(img-mi65httk-1711710069812)]

6.6 对称 + 非对称 + 证书 —— SSL/TLS协议

[外链图片转存中…(img-O90fWJ72-1711710069812)]

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号