Linux--网络编程

news/2023/12/5 23:34:36

一、网络编程概述
1.进程间通信:

1)进程间通信的方式有**:管道,消息队列,共享内存,信号,信号量这么集中

2)特点:依赖于linux内核,基本是通过内核来实现应用层的两个进程间的通信

3)缺陷:无法多机通讯

2.网络编程:

1)网络编程适用去不同的pc间的通信,可以实现多机运行

2)它关心的是:地址和数据
地址是指:IP地址和端口号。每台PC机连网够都有一个IP地址,那么每台联网的PC机可能跑多个服务器,然后每个服务器中对应的有很多进程。当客户端接入的时候,不知道去对接哪个服务器。那么这时端口号对应的就是每个服务器的端口。客户就可以通过端口号连接到对应的服务器了。

数据是指:协议(HTTP/TCP/UDP)
它是一种数据格式

3.TCP/UDP对比

1)TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大的努力交付,即不保证可靠交付

3)TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没用拥塞控制,因此网络出现拥塞不会使源主机的发送速率减低(对实时应用很有用,如IP电话,实时视频会议等)

4)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5)TCP首部开销字节;UDP的首部开销小,只有8字节

6)TCP的逻辑通信信道是全双工的可靠信道;UDP则是不可靠信道

4.端口号的作用

一台拥有IP地址的主机可以提供许多服务,比如Web服务,FTP服务,SMTP服务
这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务的呢?显然不能只靠IP地址,因为IP地址和网络服务的关系是一对多的关系。
实际上是通过“IP地址+端接口”来区分不同服务的。

端口提供了一种访问通道,
服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69

二、字节序
1.定义

字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。

2.常见序

1)Little endian(小端字节序):将低序字节存储在起始地址

LE little-endian

最符合人的思维的字节序

地址低位存储值的低位

地址高位存储值的高位

怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说

低位值小,就应该放在内存地址小的地方,也即内存地址低位

反之,高位值就应该放在内存地址大的地方,也即内存地址高位

2) Big endian(大端字节序):将高序字节存储在起始地址

BE big-endian

最直观的字节序

地址低位存储值的高位

地址高位存储值的低位

为什么说直观,不要考虑对应关系

只需要把内存地址从左到右按照由低到高的顺序写出

把值按照通常的高位到低位的顺序写出

两者对照,一个字节一个字节的填充进去

三、socket编程步骤

1.创建套接字

函数原型:

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

domain:
指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族
AF_INET IPv4 因特网域
AF_INET6 IPv6 因特网域
AF_UNIX Uinx 域
AF_ROUTE 路由套接字
AF_KEY 密匙套接字
AF_UNSPEC 未指定

type参数指定socket的类型:
SOCK_STREAM:
流式套接字提供可靠的,面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性

SOCK_DGRAM:
数据报套接字定义了一种无连接的服,数据通过相互独立的报文传输,是无序了,并且不保证是可靠,无差错的。它使用数据包协议UDP

SOCK_RAW:
允许程序使用底层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发

protocol:
通常赋值:0
0选择type类型对应的默认协议
IPPROTO_TCP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTPC传输协议
IPPROTO_TIPC TIPC传输协议
 

2.bind()函数:IP端口号与相应描述字赋值函数
函数原型:

 #include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能:

用于绑定IP地址和端口号到socketfd

参数:
sockfd
是一个socket描述符

addr
是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同
addrlen

addr指向的结构体的大小,以字节为单位

struct sockaddr *addr:一般用替换的:

ipv4对应的是:
struct sockaddr {sa_family_t sa_family;//协议族char        sa_data[14];//IP+端口号}同等替换:
struct sockaddr_in{sa_family_t  sin_family;//协议族in_port_t sin_port;//端口号struct in_addr sin_addr;//IP地址结构体unsigned char sin_zero[8];//填充,没用实际意义,只是为sockaddr结构在内存中对齐,这样两者才能相互转换}

地址转换API

//把字符串形式“127.0.0.1”转化为网络能识别的格式
int inet_aton(const char* straddr,struct in_addr *addrp);//把网络格式的IP地址转化为字符串形式
char* inet_ntoa(struct in_addr sin_addr);

将主机字节顺序转换为网络字节顺序

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。

为了进行转换 bsd socket提供了转换的函数 有下面四个
htons 把unsigned short类型从主机序转换到网络序
htonl 把unsigned long类型从主机序转换到网络序
ntohs 把unsigned short类型从网络序转换到主机序
ntohl 把unsigned long类型从网络序转换到主机序

在使用little endian的系统中 这些函数会把字节序进行转换 
在使用big endian类型的系统中 这些函数会定义成空宏

同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug.

htonl()函数

头文件
#include <arpa/inet.h> 
htonl()函数
函数原型是:uint32_t htonl(uint32_t hostlong)

1.hostlong是主机字节顺序表达的32位数,htonl中的h–host主机地址,to–to,n–net网络,l–unsigned long无符号的长整型(32位的系统是4字节);
2.函数返回值是一个32位的网络字节顺序;
3.函数的作用是将一个32位数从主机字节顺序转换成网络字节顺序。
htons()函数
函数原型是:uint16_t htons(uint16_t hostlong)

1.hostlong是主机字节顺序表达的16位数,htons中的h–host主机地址,to–to,n–net网络,s–short long无符号的短整型(32位的系统是2字节);
2.函数返回值是一个16位的网络字节顺序;
3.函数的作用是将一个16位数从主机字节顺序转换成网络字节顺序,简单的说就是把一个16位数高低位呼唤。
ntohs()函数
函数原型是:uint16_t ntohs(uint16_t hostlong)

1.hostlong是网络字节顺序表达的16位数,ntohs中的,n–net网络,to–toh–host主机地址,s–short long有符号的短整型(32位的系统是2字节);
2.函数返回值是一个16位的主机字节顺序;
3.函数的作用是将一个16位数由网络字节顺序转换为主机字节顺序,简单的说就是把一个16位数高低位互换。
ntohl()函数
函数原型是:uint32_t ntohs(uint32_t hostlong)

1.hostlong是网络字节顺序表达的32位数,ntohs中的,n–net网络,to–toh–host主机地址,s–unsigned long无符号的短整型(32位的系统是4字节);
2.函数返回值是一个32位的主机字节顺序;
3.函数的作用是将一个32位数由网络字节顺序转换为主机字节顺序。
大小端
比如: unsigned long hostlong = 0xa2b4c6d8;

大端顺序存放:
偏移地址 存放内容
0x00000000 0xa2
0x00000001 0xb4
0x00000002 0xc6
0x00000003 0xd8

小端顺序存放:
偏移地址 存放内容
0x00000000 0xd8
0x00000001 0xc6
0x00000002 0xb4
0x00000003 0xa2

网络字节顺序一定是大端顺序,主机字节不一定
 

3.监听
listen()函数:监听设置函数

 #include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int listen(int sockfd, int backlog);

功能:
设置能处理的最大连接数,listen() 并未开始接收连接,只是设置socket 的 listen 模式,listen 函数只用于服务端,服务器进程不知道要与谁连接,因此,它不会主动要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对他做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换为一个被动的套接字(监听),规定内核为相应套接字排队的最大连接数。

内核为任何一个给定监听套接字维护两个队列:
未完成连接队列,每个这样的SYN 报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的TCP 三次握手过程。这些套接字处于SYN_REVD 状态;

已完成连接队列,每个已完成TCP 三次握手过程的客户端对应其中一项。这些套接字处于ESTABLISHED 状态;

参数
sockfd

sockfd 是socket 系统调用返回的服务器端socket 描述符
backlog
backlog 指定在请求队列中允许的最大请求数

4.接受连接
accept()函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能:
accept 函数由TCP 服务器调用,用于从已完成连接队列对头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。

参数:
sockfd
sockfd 是socket 系统调用返回的服务器端(s_addr)socket 描述符

addr
用来返回已连接的对端(客户端)的协议地址

addrled
客户端大小(c_addr)

返回值:
该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接收的客户连接创建一个已连接套接字(表示TCP 三次握手已完成),当服务器完成对某个给定客户的服务器时,相应的已连接套接字就会被关闭。

5.数据的收发:
字节流读取函数

在套接字通信中进行字节读取函数:read(); write(); 与I/O 中的读取读取函数略有区别,因为它们输入或输出的字节数比可能比请求的要少。

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
 ssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t recv(int sockfd, void *buf, size_t len, int flags);

注意:这里fd文字描述符就是套接字。。

6.客户端的connect函数:
connect()函数:客户连接主机

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能:
该函数用于绑定之后的client 端(客户端),与服务器建立连接

参数:
sockfd:是目的服务器的socket 描述符
addr:是服务器端的IP 地址和端口号的地址结构指针
addrlen:地址长度,常被设置为sizeof(struct sockaddr)

返回值:
成功返回0,遇到错误时返回 -1,并且error中包含相应的错误码

7.网络消息代码:

1.服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main()
{int s_fd;char readbuf[128] = {0};int nread;char *msg = "I get your message,QINGYuan is handsome.\n";struct sockaddr_in s_addr;struct sockaddr_in c_addr;memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);
}//2.binds_addr.sin_family = AF_INET;s_addr.sin_port = htons(8989);inet_aton("192.168.1.71",&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listenlisten(s_fd,10);//4.acceptint clen = sizeof(struct sockaddr_in);int c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&clen);if(c_fd == -1){perror("accept");exit(-1);}printf("start conneting...\n");sleep(2);printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//5.readnread = read(c_fd,readbuf,128);if(nread == -1){perror("read");}else{printf("get message :%d,%s\n",nread,readbuf);
}//6.writeif(write(c_fd,msg,strlen(msg))<0){perror("write");exit(-1);
}return 0;}

运行结果:

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main()
{int c_fd;char readbuf[128];int nread;char *msg="msg from client\n";struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));//1.socketc_fd=socket(AF_INET,SOCK_STREAM,0);if(c_fd==-1){perror("socket");exit(-1);}//2.connectc_addr.sin_family=AF_INET;c_addr.sin_port=htons(8989);inet_aton("192.168.1.5",&c_addr.sin_addr);if(connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr))==-1){perror("connect");exit(-1);}//3.writewrite(c_fd,msg,strlen(msg));//4.readnread=read(c_fd,readbuf,128);if(nread==-1){perror("read");}else{printf("get message from sever:%d,%s\n",nread,readbuf);}return 0;
}

2.多个客户端连接代码:

服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main(int argc,char **argv)
{int s_fd;char readbuf[128];int nread;int mark=0;char msg[128]={0};struct sockaddr_in s_addr;struct sockaddr_in c_addr;if(argc!=3){printf("param is not good\n");exit(-1);}memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1.sockets_fd=socket(AF_INET,SOCK_STREAM,0);if(s_fd==-1){perror("socket");exit(-1);}//2.binds_addr.sin_family=AF_INET;s_addr.sin_port=htons(atoi(argv[2]));inet_aton(argv[1],&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listenlisten(s_fd,10);//4.acceptint clen=sizeof(struct sockaddr_in);while(1){int c_fd=accept(s_fd,(struct sockaddr*)&c_addr,&clen);if(c_fd==-1){perror("accept");}printf("connect\n");mark++;sleep(2);printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));if(fork()==0){if(fork()==0){while(1){memset(msg,0,sizeof(msg));sprintf(msg,"welcome No.%d client",mark);//6.writewrite(c_fd,msg,strlen(msg));sleep(3);}}while(1){//5.readmemset(readbuf,0,sizeof(readbuf));nread=read(c_fd,readbuf,128);if(nread==-1){perror("read");}else{printf("get message:%d,%s\n",nread,readbuf);}}break;}	}return 0;
}

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main()
{int s_fd;char readbuf[128] = {0};int nread;char *msg = "I get your message,QINGYuan is handsome.\n";struct sockaddr_in s_addr;struct sockaddr_in c_addr;memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);
}//2.binds_addr.sin_family = AF_INET;s_addr.sin_port = htons(8989);inet_aton("192.168.1.71",&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listenlisten(s_fd,10);//4.acceptint clen = sizeof(struct sockaddr_in);int c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&clen);if(c_fd == -1){perror("accept");exit(-1);}printf("start conneting...\n");sleep(2);printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//5.readnread = read(c_fd,readbuf,128);if(nread == -1){perror("read");}else{printf("get message :%d,%s\n",nread,readbuf);
}//6.writeif(write(c_fd,msg,strlen(msg))<0){perror("write");exit(-1);
}return 0;}

结果:


http://www.ppmy.cn/news/1230278.html

相关文章

代码随想录 Day50 单调栈 LeetCodeT503 下一个最大元素II T42接雨水

前言 前面我们说到了单调栈的第一题,下一个最大元素I,其实今天的两道题都是对他的变种,知道第一个单调栈的思想能够想清楚,其实这道题是很简单的 考虑好三个状态,大于等于小于,其实对于前面这些题目只要细心的小伙伴就会发现其实小于和等于的处理是一样的都是直接入栈,只有大于…

C/C++最大质因子 2021年12月电子学会中小学生软件编程(C/C++)等级考试一级真题答案解析

目录 C/C最大质因子 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C最大质因子 一、题目要求 1、编程实现 质因子是指能整除给定正整数的质数。而最大质因子是指一个整数的所有质因子中最大的那个。…

PLC梯形图实操——风扇正反转

文章目录 1.项目内创建函数块&#xff08;FB&#xff09;2.项目内创建数据块&#xff08;DB&#xff09;2.1去除优化块访问2.2去除优化块的访问后对数据块进行编译 3.在函数块&#xff08;FB&#xff09;内实现正转反转的自锁与互锁3.1在函数块内实现电机正反转的梯形图 4.主函…

基于单片机PM2.5监测系统仿真设计

**单片机设计介绍&#xff0c; 基于单片机PM2.5监测系统仿真设计 文章目录 一 概要简介设计目标系统组成工作流程仿真设计结论 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于单片机PM2.5监测系统仿真设计介绍 简介 PM2.5&#xff08;可吸…

el-tree结合el-switch实现状态切换

<template><div><el-col :span"24"><el-card class"tree-card"><div class"sketch_content selectFile"><span class"span_title">组织列表 </span><div style"display: flex; jus…

HarmonyOS开发(四):UIAbility组件

1、UIAbility概述 UIAbility 一种包含用户界面的应用组件用于与用户进行交互系统调度的单元为应用提供窗口在其中绘制界同 注&#xff1a;每一个UIAbility实例&#xff0c;都对应一个最近任务列表中的任务。 一个应用可以有一个UIAbility也可以有多个UIAbility。 如一般的…

python爬虫SHA案例:某直播大数据分析平台

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、找出需要加密的参数 js运行 atob(‘aHR0cDovL3d3dy5oaDEwMjQuY29tLyMvc2VhcmNoL3NlYXJjaA’) 拿到网址&#xff0c;F12打…

k8s的高可用集群搭建,详细过程实战版

kubernetes高可用集群的搭建 前面介绍过了k8s单master节点的安装部署 今天介绍一下k8s高可用集群搭建 环境准备&#xff1a; vip &#xff1a;192.168.121.99 keeplive master01&#xff1a;192.168.121.153 centos7 master02&#xff1a;192.168.121.154 centos7 master03&a…

SSD主控

《深入浅出SSD》学习中… 文章目录 《深入浅出SSD》学习中.....一、SSD主控二、PCIe和NVMe控制器前端子系统1.PCIe控制器2.NVMe控制器 一、SSD主控 就是类似电脑CPU的东西&#xff0c;在SSD中收取处理Host端的命令&#xff0c;管理NAND闪存 二、PCIe和NVMe控制器前端子系统 主…

RuntimeError: PyPI no longer supports ‘pip search‘ (or XML-RPC search).

RuntimeError: PyPI no longer supports ‘pip search’ (or XML-RPC search). 1. ERROR: XMLRPC request failed Deprecated Methods https://warehouse.pypa.io/api-reference/xml-rpc.html#deprecated-methods PyPI XMLRPC Search Disabled https://status.python.org/inc…

【金融分析】Python:病人预约安排政策 | 金融模拟分析

目录: 说明(Instructions) 问题描述(Problem Description) 仿真设置(Simulation Setting) 仿真过程的 Python 代码

五、程序员指南:数据平面开发套件

服务质量 (QoS) 框架 本章介绍 DPDK 服务质量 (QoS) 框架。 21.1 带有 QoS 支持的数据包流水线 下图显示了一个具有 QoS 支持的复杂数据包处理流水线的示例 表21.1&#xff1a;带有 QoS 支持的复杂数据包处理流水线 这个流水线可以使用可重用的 DPDK 软件库构建。在这个流…

LangChain 4用向量数据库Faiss存储,读取YouTube的视频文本搜索Indexes for information retrieve

接着前面的Langchain&#xff0c;继续实现读取YouTube的视频脚本来问答Indexes for information retrieve LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗…

OpenVPN Connect使用连接公网VPN服务器实现内网穿透

安装并运行OpenVPN Connect 点击AGREE 添加配置.OVPN文件 点击连接 连接成功 两个内网主机通过公网VPN穿透

Python-----PyInstaller的简单使用

PyInstaller简介 PyInstaller是一个Python库&#xff0c;可以将Python应用程序转换为独立的可执行文件。PyInstaller支持跨平台&#xff0c;可以在Windows、Linux和MacOS上生成可执行文件。 PyInstaller会分析Python程序&#xff0c;并将程序打包成一个完整的可执行文件&…

[ruby on rails]rack-cors, rack-attack

gem rack-attack gem rack-cors1. rack-attack 可以根据ip、域名等设置黑名单、设置访问频率 设置黑名单 # 新增 config/initializers/rack_attack.rb # 请求referer如果匹配不上设置的allowed_origins&#xff0c;返回403 forbidden Rack::Attack.blocklist(block bad domai…

力扣:172. 阶乘后的零(Python3)

题目&#xff1a; 给定一个整数 n &#xff0c;返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * ... * 3 * 2 * 1 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长…

FFmpeg常用命令行讲解及实战一

文章目录 前言一、学习资料参考二、FFmpeg 选项1、主要选项①、主要命令选项②、举例 2、视频选项①、主要命令选项②、举例1&#xff09;提取固定帧2&#xff09;禁止输出视频3&#xff09;指定视频的纵横比 3、音频选项①、主要命令选项②、举例 4、字幕选项①、主要命令选项…

猫12分类:使用多线程爬取图片的Python程序

本文目标 对于猫12目标检测部分的数据集&#xff0c;采用网络爬虫来制作数据集。 在网络爬虫中&#xff0c;经常需要下载大量的图片。为了提高下载效率&#xff0c;可以使用多线程来并发地下载图片。本文将介绍如何使用Python编写一个多线程爬虫程序&#xff0c;用于爬取图片…

01背包 P1507 NASA的食物计划

P1507 NASA的食物计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 普通01背包状态表示&#xff1a;f(i, j)表示前i件物品放入一个容量为j的背包可以获得的最大价值。 本题类似&#xff0c;f(i, j, k)表示前i件物品放入一个限制为j&#xff0c;且另一个限制为k的背包中可以…
最新文章