C语言网络编程基础(linux)

news2024/11/24 5:24:40

目录

  • 文件描述符与套接字
  • 网络编程的基本流程
  • 基础的函数和结构体(持续更新)
    • socket函数
    • sockaddr和sockaddr_in结构体
    • bind函数
    • listen函数
    • accept函数
    • recv函数
    • writev函数
    • readv函数
    • connect函数
    • fcntl函数
  • epoll相关函数
    • epoll_create
    • epoll_ctl函数
    • epoll_wait函数

文件描述符与套接字

在linux操作系统下,有万物皆文件的概念,当一个进程想要打开/创建一个文件时,内核会给进程返回一个文件描述符,文件描述符是一个非负数,常用int类型表示,起到索引的作用,是为了高效管理进程打开/创建的文件的,指向的是被打开的文件。所有I/O的系统操作也都是通过文件描述符来的;每一个进程都有一个文件描述符表,里面记录的就是进程打开/创建文件的记录

套接字是一种特殊的文件描述符,用于进程和进程之间的网络通信,常用在网络编程中

进程和进程之间通信主要有六种方式,分别是:
1.管道
2.消息队列
3.共享内存
4.信号
5.信号量
6.套接字.

套接字便是其中的一种.

网络编程的基本流程

在这里插入图片描述
这个流程很经典,就不过多赘述了.

基础的函数和结构体(持续更新)

函数太多了,这里只记录一些常用的函数

socket函数

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

其中
domain表示指定套接字的地址族或协议族。常见的值包括:

AF_INET:用于IPv4 地址族。
AF_INET6:用于IPv6 地址族。
AF_UNIX 或 AF_LOCAL:用于本地(Unix 域)套接字通信。

type表示指定套接字的类型,常见的值包括:

SOCK_STREAM:用于基于流的 TCP 套接字。
SOCK_DGRAM:用于基于数据报的 UDP 套接字。
SOCK_RAW:用于原始套接字,允许更底层的数据包处理。

protocol 参数通常为 0,表示选择默认的协议。在大多数情况下,操作系统会自动选择正确的协议,例如,对于 IPv4 TCP 套接字,它会选择 TCP 协议。

返回值:socket函数的返回值是一个文件描述符(fd),经常作为网络编程中其他函数的参数.

常见的使用方式

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

sockaddr和sockaddr_in结构体

sockaddr

#include <sys/socket.h>
struct sockaddr {  
     sa_family_t sin_family;//地址族
    char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
   }; 

sockaddr已经被sockaddr_in取代了,这里就不详细说了。

sockaddr_in

#include<netinet/in.h>或#include <arpa/inet.h>

struct sockaddr_in {
    short int sin_family;      // 地址族(Address Family),通常为 AF_INET
    unsigned short int sin_port;  // 端口号(Port Number)
    struct in_addr sin_addr;     // IPv4 地址(32 位的 IPv4 地址)
    unsigned char sin_zero[8];   // 不使用,填充字节
};

sockaddr_in 是用于表示 IPv4 地址的 C 语言结构体,通常在网络编程中与套接字套接字相关的函数一起使用

常见的使用方式:

struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;//绑定地址族,使用ipv4
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // 127.0.0.1 //绑定地址
    addr.sin_port = htons(8000); //绑定端口

bind函数

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

1.sockfd参数表示要进行绑定的套接字文件描述符(是socket函数的返回值)

2.sockaddr 结构体是刚才上述所说的结构体,但是sockaddr不如sockaddr_in好用,所以一般情况下是定义一个sockaddr_in结构体,然后使用强制转换成sockaddr类型

3.addrlen参数表示结构体的长度

常用的使用方式:

 struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);  // 端口号 8080
    server_addr.sin_addr.s_addr = INADDR_ANY;  // 任意地址
    memset(server_addr.sin_zero, 0, sizeof(server_addr.sin_zero));
    
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("Bind failed");
        exit(1);
    }

listen函数

listen函数作用:让套接字变成可以被动连接的状态,等待客户端的连接

int listen(int sockfd, int backlog);

sockfd参数表示文件描述符

backlog参数表示等待连接队列的最大长度,即在调用 accept 函数之前可以排队等待的最大连接数。通常,这个值为一个正整数,决定了同时等待的连接数量。

常用的使用方法:

int backlog = 5; // 最大等待连接数
if (listen(sockfd, backlog) == -1) {
        perror("Listen failed");
        exit(1);
    }

accept函数

accept 函数用于接受传入的连接请求,通常在服务器端用于接受客户端的连接

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

1.sockfd是文件描述符
2.addr是 sockaddr结构体,用于接收客户端的地址,端口等信息,所以跟Bind函数调用时的sockaddr要区分开来

3.addrlen是结构体的大小

常见的使用方式:

 struct sockaddr_in new_addr;
 int new_sock;
 
 addr_size = sizeof(new_addr);
 new_sock = accept(sockfd, (struct sockaddr*)&new_addr, &addr_size);

返回值:在成功接受连接请求时返回一个新的套接字,该套接字用于与客户端进行通信。这个新套接字是已连接套接字,它是服务器与客户端之间的通信通道。

这里要重点强调一下,我们后续进行客户端和服务端之间的通信时,使用的是accept函数返回的新套接字,而之前用socket函数创建的旧套接字仍然在监听新的连接请求(用于接收连接请求,而不是直接用来通信)

recv函数

recv 函数用于从已连接套接字(或者数据报套接字)接收数据
注意是已连接的套接字

int recv(int sockfd, void *buf, size_t len, int flags);

1.sockfd是文件描述符
2.buf是接收数据的缓冲区指针
3.len是缓冲区的大小
4.flags通常设置为0

返回值是recv函数读到的字节数,如果返回值为 -1,表示读取失败,失败的原因会存储在errno里面
recv函数的返回值总结

常见的使用方式:

int bytes_read=recv(sockfd,buffer,sizeof(buffer),0);

recv函数是一个阻塞函数,如果在读取时,发现并没有数据可以读,就会被阻塞住,如果不想被阻塞住,可以用fcntl函数将文件描述符设置为非阻塞模式,具体操作请看fcntl函数.

recv 和 read 函数在某些方面类似,因为它们都用于从文件描述符中读取数据。然而,它们有一些区别:

来源:
recv 是套接字库函数,用于在网络编程中接收数据。它可以用于套接字(sockets)等网络通信相关的操作。
read 是标准C I/O 函数,通常用于文件描述符,但也可以用于套接字等。它更一般化,可用于读取任何可读的文件描述符。

参数:
recv 在最后一个参数中可以指定额外的选项(flags),允许对接收操作进行控制。
read 没有额外的选项参数,它只接受文件描述符、缓冲区和长度。

错误处理:
recv 返回的错误值可能包含更多关于套接字通信的信息,如连接已断开等。因此,错误代码可能更详细。
read 的错误码可能相对简单,不会提供关于底层通信的额外信息,但它可用于读取多种文件类型。

用法:
recv 主要用于网络编程,特别是在套接字通信中,用于接收数据。
read 主要用于文件和通用文件描述符的读取,可用于从文件、管道、套接字等读取数据。

writev函数

writev 函数用于将多个分散的数据写入文件描述符(通常是文件或套接字)
也被称为集中写,与write函数的最大区别就是writev函数可以一次性写出多个缓冲区,而write函数一次性只能写出一个缓冲区

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

1.fd参数表示文件描述符
2.iov参数表示指向iovec结构体数组的结构体指针
3.iovcnt表示数组中结构体的数量

iovec数组

struct iovec {
    void *iov_base;    // 缓冲区的起始地址
    size_t iov_len;    // 缓冲区的长度
};

常见的使用方式:

    iov[0].iov_base = buf1; //缓冲区的起始地址
    iov[0].iov_len = strlen(buf1);//缓冲区的长度!
    iov[1].iov_base = buf2;
    iov[1].iov_len = strlen(buf2);

    int fd = 1;  
    ssize_t bytes_written = writev(fd, iov, 2);//将这两个缓冲区的内容全部
    //                                           写入文件描述符

readv函数

用于把文件描述符中的数据一次性读到多个缓冲区中,也叫作分散读

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

使用方法和writev类似

iov[0].iov_base= buf1;
iov[0].iov_len=sizeof(buf1);
iov[1].iov_base = buf2;
iov[1].iov_len = sizeof(buf2);
ssize_t bytes_read = readv(fd,iov,2);

connect函数

connect 函数用于建立一个客户端套接字与服务端套接字之间的连接。它在客户端套接字上调用,指示客户端要连接到指定的服务器地址和端口。

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

1.sockfd表示要连接的客户端文件描述符
2.sockaddr表示连接时的地址及端口等信息
3.addrlen表示结构体的大小

常见使用方式:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }

    // 准备服务器地址信息
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);  // 服务器端口
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  // 服务器IP地址

    // 连接到服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect");
        exit(1);
    }

fcntl函数

fcntl 函数是一个在 Unix 和类 Unix 操作系统中使用的函数,主要用于控制文件描述符(file descriptor)的属性和执行各种操作。这包括修改文件状态标志、获取或设置文件描述符的属性、以及执行非阻塞操作等。具体来说,fcntl 函数的一些常见用途包括:

1.修改文件状态标志:通过 fcntl 函数,你可以修改文件描述符的状态标志,例如将文件设置为非阻塞模式,以便在读写操作时不会被阻塞。这是通过设置 O_NONBLOCK 标志实现的。

2.获取或设置文件描述符属性:你可以使用 fcntl 函数获取或设置文件描述符的各种属性,如获取或设置文件的访问模式、文件的拥有者、或文件的屏蔽字(file mode creation mask)等。

3.复制文件描述符:你可以使用 F_DUPFD 命令来复制一个文件描述符,这会创建一个新的文件描述符,指向与原始文件描述符相同的文件。

4.获取或设置文件锁:fcntl 函数还可用于获取或设置文件锁,以确保多个进程可以安全地访问共享文件。你可以使用 F_GETLK 命令来获取文件锁信息,或使用 F_SETLK 和 F_SETLKW 命令来设置或阻塞文件锁。

5.取消文件锁:通过 F_SETLK 命令,你还可以用来取消现有的文件锁。

参考链接:fcntl

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */);

1.fd是要操作的文件描述符
2.cmd是对应的操作命令,如下:

F_DUPFD:创建一个新的文件描述符,指向与原始文件描述符相同的文件。

F_GETFD:获取文件描述符的标志。

F_SETFD:设置文件描述符的标志。

F_GETFL:获取文件的状态标志(如 O_RDONLY、O_WRONLY、O_NONBLOCK 等)。

F_SETFL:设置文件的状态标志。

F_GETOWN:获取文件描述符的所有权(如进程 ID 或进程组 ID)。

F_SETOWN:设置文件描述符的所有权。

F_GETLK:获取文件锁的信息。

F_SETLK:设置文件锁,如果锁已存在则返回错误。

F_SETLKW:设置文件锁,如果锁已存在则等待。

使用例子:

//对文件描述符设置非阻塞
int setnonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;// O_NONBOLOCK为非阻塞标志.
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

因为文件描述符的标志是一个位掩码,所以必须要先获取原来的状态,再跟新状态或运算,才可以修改文件描述符的状态.


epoll相关函数

epoll是linux操作系统,内核提供给用户态专门用于多路复用的系统调用函数,其作用是可以让一个进程维护多个socket.

epoll的流程
1.使用epoll_create函数创建一个指向内核事件表的文件描述符

2.使用epoll_ctl函数将想要监听的socket和想要监听的事件类型注册到epoll上

3.使用epoll_wait函数等待事件到达,进程/线程通过对应的事件处理方式处理事件

epoll_create

#include <sys/epoll.h>
int epoll_create(int size)

作用:创建一个指向epoll内核事件表的文件描述符,返回值用于epoll其他函数的第一个参数

epoll_ctl函数

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

用于将文件描述符注册到epoll上,或者对已经注册好的文件描述符修改和删除

1.第一个参数是epoll_create函数的句柄
2.第二个参数是一个命令,分别用三个宏表示注册,修改,删除

EPOLL_CTL_ADD (注册新的fd到epfd),
EPOLL_CTL_MOD (修改已经注册的fd的监听事件),
EPOLL_CTL_DEL (从epfd删除一个fd);

3.event参数表示要监听的事件

epoll_event结构体

struct epoll_event {
__uint32_t events; //表示事件的类型
epoll_data_t data; //
};

events对应的事件类型有如下几种:
EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭)

EPOLLOUT:表示对应的文件描述符可以写

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)

EPOLLERR:表示对应的文件描述符发生错误

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里


epoll_data_t是一个共用体(联合体)表示用户数据,用来存储额外的信息

typedef union epoll_data {
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

ptr:一个指向 void 类型的指针,通常用于关联一个任意类型的指针。
fd:一个整数,通常用于关联一个文件描述符(比如套接字描述符)。
u32:一个32位的无符号整数。
u64:一个64位的无符号整数。

epoll_ctl常见的使用方式:(这里如果看不太懂events下面还有详解)

注册:

     epoll_event event;
     event.data.fd = fd;//设置文件描述符!
 #ifdef ET
     event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
 8#endif
 
#ifdef LT
    event.events = EPOLLIN | EPOLLRDHUP;
#endif

    if (one_shot)
        event.events |= EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);

删除:

epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0);
   close(fd);

修改

void modfd(int epollfd, int fd, int ev)
 {
     epoll_event event;
     event.data.fd = fd;
 
 #ifdef ET
     event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
 #endif
 
#ifdef LT
    event.events = ev | EPOLLONESHOT | EPOLLRDHUP;
#endif

    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

epoll_wait函数

用于等待事件的发生,当监控的文件描述符上有事件发生时,返回有事件发生的文件描述符的个数,通知进程处理事件

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

1.epfd是epoll_wait函数创建的句柄
2.events表示内核得到的事件的集合
3.maxevents表示events的大小,即能够处理的最大事件数
4.timeout表示超时时间:
-1:阻塞
0:非阻塞
大于0:指定毫秒数

常见的使用方式:

int epfd = epoll_create(1); // 创建 epoll 实例
struct epoll_event events[MaxEvents]; // 用于存储事件的数组

// 将需要监听的文件描述符添加到 epoll 实例(epfd)中,使用 epoll_ctl 函数。

int num_events = epoll_wait(epfd, events, MaxEvents, timeout);

epoll_ctl函数和epoll_wait函数中的events详解:

epoll_ctl 函数:
events 参数用于指定你希望监听的事件,这个参数是用于告诉 epoll 实例需要监听哪些事件的。在调用 epoll_ctl 函数时,你需要为 events 参数赋值,指定感兴趣的事件类型,如 EPOLLIN(可读事件)或 EPOLLOUT(可写事件)等。
events 参数通常是一个位掩码,可以使用位运算来指定多个事件,例如 EPOLLIN | EPOLLOUT 表示同时监听可读和可写事件。
events 参数的角色是告诉 epoll 实例你关心的事件类型以及要监听的文件描述符。

epoll_wait 函数:
events 参数用于接收 epoll_wait 函数返回的已发生事件的信息。在调用 epoll_wait 之前,你不需要为 events 参数赋值,因为它将由 epoll_wait 函数填充。
当 epoll_wait 函数返回时,它会将已发生的事件信息填充到 events 数组中。你可以检查每个事件的类型和相关的文件描述符,以确定发生了什么事件。

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

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

相关文章

竞赛 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

生物标志物发现中的无偏数据分析策略

目录 0. 导论基本概念 1. 生物标志物发现的注意事项2. 数据预处理2.1 高质量原始数据和缺失值处理2.2 数据过滤2.3 数据归一化 3. 数据质量评估3.1 混杂因素3.2 类别分离3.3 功效分析3.4 批次效应 4. 生物标志物发现4.1 策略4.2 数据分析工具4.3 模型优化策略 0. 导论 组学技术…

Pytorch之MobileViT图像分类

文章目录 前言一、Transformer存在的问题二、MobileViT1.MobileViT网络结构&#x1f353; Vision Transformer结构&#x1f349;MobileViT结构 2.MV2(MobileNet v2 block)3.MobileViT block&#x1f947;Local representations&#x1f948;Transformers as Convolutions (glob…

三十四、【进阶】MySQL索引的操作

1、创建索引 &#xff08;1&#xff09;基础语法 &#xff08;2&#xff09;唯一索引 唯一索引与普通索引不同的是&#xff0c;索引列的数值必须唯一&#xff0c;但允许有空值null&#xff1b; 唯一索引与主键索引不同的是&#xff0c;主键索引不允许出现空值null&#xff0…

Webmin 远程命令执行漏洞_CVE-2019-15107

Webmin 远程命令执行漏洞_CVE-2019-15107 文章目录 Webmin 远程命令执行漏洞_CVE-2019-15107在线漏洞详情&#xff1a;漏洞描述&#xff1a;版本影响&#xff1a;环境搭建漏洞复现使用BP进行抓包POC发送以下POST请求来执行命令id:复现成功 漏洞利用-shell反弹执行反弹指令bp的i…

【Flutter Widget】AppBar 和 PopupMenu

App Bar 可以视为页面的标题栏&#xff0c;在 Flutter 中用AppBar组件实现&#xff1b;Popup Menu 是弹出菜单&#xff0c;用PopupMenuButton实现。下面结合这两个组件说明其用法。 1. 代码实现 一个简单的AppBar实现代码如下&#xff1a; import package:flutter/material.…

同源策略和跨域问题

1.跨域问题产生的原因 浏览器的同源策略影响&#xff0c;同源策略是一种安全机制&#xff0c;它限制了一个网页中的脚本只能访问同源的资源。 跨源网络访问的三种方式&#xff1a;跨域写操作&#xff0c;跨域资源嵌入&#xff0c;跨域读操作 2.跨域问题案例 ip和域名不一致…

使用 Certbot 为 Nginx 自动配置 SSL 证书

发布于 2023-07-13 on https://chenhaotian.top/linux/certbot-nginx/ 使用 Certbot 为 Nginx 自动配置 SSL 证书 配置步骤 以 Debian 11 为例 1. 安装Certbot和Nginx插件 sudo apt-get update sudo apt-get install certbot python3-certbot-nginx2. 获取和安装证书 运行…

nginx的location的优先级和匹配方式和nginx的重定向

在http模块有server,在server模块才有location,location匹配的是uri /test /image 在一个server当中有多个location,如何来确定匹配哪个location。 nginx的正则表达式&#xff1a; ^&#xff1a;字符串的起始位置 $&#xff1a;字符串的结束位置 *&#xff1a;匹配所有 &am…

【宏offsetof详解和模拟实现】

文章目录 一. 什么是offsetof&#xff1f;二. 用宏模拟实现offsetof三. 代码重点讲解四. 结束语 一. 什么是offsetof&#xff1f; 1.** 注意&#xff1a;offsetof不是函数而是一个宏** 2. 返回值是size_t无符号整形&#xff0c;头文件是<stddef.h>&#xff0c;参数是&…

智慧工地解决方案,智慧工地平台源码

一、现状描述 建筑工程建设具有明显的生产规模大宗性与生产场所固定性的特点。建筑企业70%左右的工作都发生在施工现场&#xff0c;施工阶段的现场管理对工程成本、进度、质量及安全等至关重要。同时随着工程建设规模不断扩大&#xff0c;工艺流程纷繁复杂&#xff0c;如何搞好…

腾讯云我的世界mc服务器多少钱一年?

腾讯云我的世界mc服务器多少钱&#xff1f;95元一年2核2G3M轻量应用服务器、2核4G5M带宽优惠价218元一年、4核8G12M带宽轻量服务器446元一年&#xff0c;云服务器CVM标准型S5实例2核2G优惠价280元一年、2核4G配置服务器748元一年&#xff0c;腾讯云百科txybk.com分享腾讯云我的…

Spring MVC 和Spring JDBC

目录 Spring MVC MVC模式 核心组件 工作流程 Spring JDBC Spring JDBC功能和优势 Spring JDBC的关键组件 Spring MVC Spring MVC&#xff08;Model-View-Controller&#xff09;是Spring框架的一个模块&#xff0c;用于构建Web应用程序。它的主要目标是将Web应用程序的不…

【Python】Python语言基础(上)

第一章 前言 1. Python简介 Python语言并不是新的语言&#xff0c;它早于HTTP 1.0协议5年&#xff0c;早于Java语言 4年。 ​ Python是由荷兰人Guido van Rossum&#xff08;吉多范罗苏姆&#xff09;于1989年圣诞节期间在阿姆斯特丹休假时为了打发无聊的假期而编写的一个脚本…

利用 Amazon CodeWhisperer 激发孩子的编程兴趣

我是一个程序员&#xff0c;也是一个父亲。工作之余我会经常和儿子聊他们小学信息技术课学习的 Scratch 和 Kitten 这两款图形化的少儿编程工具。 我儿子有一次指着书房里显示器上显示的 Visual Studio Code 问我&#xff0c;“为什么我们上课用的开发界面&#xff0c;和爸爸你…

巴以冲突中暴露的摄像头正对安全构成威胁

巴以冲突爆发后&#xff0c;许多配置不当的安全摄像头正暴露给黑客活动分子&#xff0c;使其周遭人员面临巨大安全风险。 Cyber​​news 研究人员发现&#xff0c;在以色列至少有165 个暴露的联网 RTSP 摄像头&#xff0c;在巴勒斯坦有 29 个暴露的 RTSP 摄像头。在巴勒斯坦&am…

CCPlotR | 轻松拿捏单细胞分析之细胞交互!~

1写在前面 周末了各位&#xff0c;昨天去看了奥本海默&#xff0c;不得不说&#xff0c;大神就是大神。&#x1f618; 比起我们的电影&#xff0c;似乎诺兰更好地还原了奥本海默的真实。&#x1f9d0; 言归正传&#xff0c;今天分享的是CCPlotR包&#xff0c;用于基于scRNAseq数…

SwiftUI Swift CoreData 计算某实体某属性总和

有一个名为 Item 的实体&#xff0c;它有一个名为 amount 的 Double 属性&#xff0c;向你的 View 添加一个计算属性&#xff1a; Code: struct ContentView: View {Environment(\.managedObjectContext) private var viewContextFetchRequest(sortDescriptors: [NSSortDescri…

Codeforces Round 903 (Div. 3) C(矩形旋转之后对应的坐标)

题目链接&#xff1a;Codeforces Round 903 (Div. 3) C 题目&#xff1a; 思想&#xff1a; 旋转之后对应的坐标&#xff1a; &#xff08;i&#xff0c;j&#xff09;&#xff08;n1-j&#xff0c;i&#xff09;&#xff08;n1-i&#xff0c;n1-j&#xff09;&#xff08;j…

CSS的布局 Day03

一、显示模式&#xff1a; 网页中HTML的标签多种多样&#xff0c;具有不同的特征。而我们学习盒子模型、使用定位和弹性布局把内容分块&#xff0c;利用CSS布局使内容脱离文本流&#xff0c;使用定位或弹性布局让每块内容摆放在想摆放的位置&#xff0c;让网站页面布局更合理、…