【Linux网络编程】网络编程套接字(1)

【Linux网络编程】网络编程套接字(1)

目录

  • 【Linux网络编程】网络编程套接字(1)
      • 源IP地址和目的IP地址
      • 端口号
        • 端口号和进程ID的关系
      • 网络通信
      • TCP协议
      • UDP协议
      • 网络字节序
      • socket编程接口
      • 简单的UDP网络程序

作者:爱写代码的刚子

时间:2024.1.29

前言:先提前写网络编程的博客,管道以及多线程的博客之后补上。

源IP地址和目的IP地址

IP数据包头部中,有两个IP地址,分别叫做源IP地址,和目的IP地址

  1. 源IP地址(Source IP Address):
    • 源IP地址是发送数据包的设备(或主机)的IP地址。
    • 在一个网络通信过程中,源IP地址标识了消息的来源。
    • 在IPv4中,源IP地址通常以四个十进制数字的形式表示,如 “192.168.0.1”。
    • 在IPv6中,源IP地址以一种更长的形式表示,如 “2001:0db8:85a3:0000:0000:8a2e:0370:7334”。
  2. 目的IP地址(Destination IP Address):
    • 目的IP地址是接收数据包的设备(或主机)的IP地址。
    • 在一个网络通信过程中,目的IP地址标识了消息的目标。
    • 同样,目的IP地址可以以IPv4或IPv6的形式表示。

端口号

端口号(port)是传输层协议的内容.

  • 端口号是一个2字节16位的整数;

  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;

  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;

  • 一个端口号只能被一个进程占用

端口号和进程ID的关系
  • 当网络服务启动时,它会监听一个特定的端口号,等待客户端连接。
  • 当客户端尝试连接到服务时,客户端和服务之间的通信将使用该端口号进行标识。
  • 当连接建立后,服务的进程ID与该连接相关联。这使得操作系统可以跟踪网络连接与哪个进程相关。

【问题】:我们之前在学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程。那么这两者之间是怎样的关系?

  • 网络模块和进程管理模块进行解耦合。进程pid在技术上是可以标定当前主机上某一个唯一的进程,但是实际上不会用进程pid做,进程pid属于进程管理范畴,而端口号属于网络范畴。如果非要用进程pid做两用(既做调度进程管理,又在网络上标定主机的一个唯一进程),无疑是将进程管理和网络强耦合起来了。它可以但不合理。

  • 在我们的系统中,并不是所有的进程都要进行网络通信的。而端口号是一种数字,标定当前主机上某一个唯一的进程,它更加的是一种证明,证明对应的进程是要进行网络通信的。没有端口号,这个进程只是本地间跑某些业务。而有端口号,一定是要对外的。

【问题】:底层如何通过port找到对应进程的?

  • 实际底层采用哈希的方式建立了端口号和进程PID或PCB之间的映射关系,当底层拿到端口号时就可以直接执行对应的哈希算法,然后就能够找到该端口号对应的进程。

【问题】:一个进程可以绑定多个端口号吗?

  • 可以的。未来一个进程在进行网络通信的时候,它可能既和客户端A通信,也和客户端A的子模块通信,所以此进程就会绑定两个端口号。只要能够通过端口号找到同一个进程即可。但是一个端口号不能被多个进程绑定。因为端口号到进程具有唯一性。

网络通信

我们在网络通信的时候,只要让两台主机能够通信就可以了吗?

  • 实际上,在进行通信的时候,不仅仅要考虑两台主机间互相交互数据。

  • 本质上讲,进行数据交互的时候,是用户和用户在进行交互。用户的身份,通常是用程序体现的。程序一定是在运行中的 ---- 进程。

    所以主机间通信的目的本质是:在各自的主机上的两个进程在互相交互数据。IP地址可以完成主机和主机的通信,而主机上各自的通信进程,才是发送和接受数据的一方。

所以:

  • IP —— 确保主机的唯一性

  • 端口号(port)—— 确保该主机上的进程的唯一性

  • IP + PORT = 标识互联网中唯一的一个进程。—— socket

  • 网络通信的本质:也是进程间通信


    socket:

  • socket在英文上有“插座”的意思,插座上有不同规格的插孔,我们将插头插入到对应的插孔当中就能够实现电流的传输。

  • 在进行网络通信时,客户端就相当于插头,服务端就相当于一个插座,但服务端上可能会有多个不同的服务进程(多个插孔),因此当我们在访问服务时需要指明服务进程的端口号(对应规格的插孔),才能享受对应服务进程的服务。

TCP协议

先简单介绍一下,之后再详细介绍

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

UDP协议

先简单介绍一下,之后再详细介绍

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

总结:由于我们不能保证通信双方存储数据的方式是一样的,因此网络当中传输的数据必须考虑大小端问题。因此TCP/IP协议规定如下:网络数据流采用大端字节序,即低地址高字节。无论是大端机还是小端机,都必须按照TCP/IP协议规定的网络字节序来发送和接收数据。

  • 大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
  • 小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。
  • 需要注意的是,所有的大小端的转化工作是由操作系统来完成的,因为该操作属于通信细节,不过也有部分的信息需要我们自行进行处理,比如端口号和IP地址。

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络 字节序和主机字节序的转换。

#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。

  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。

  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

socket编程接口

几个重要的函数接口

  • socket套接字

在这里插入图片描述

  • htons通常用于将主机字节顺序(host byte order)的16位整数转换为网络字节顺序(network byte order)

在这里插入图片描述

  • inet_addr 通常用于将点分十进制的 IPv4 地址转换为网络字节顺序的 32 位整数

在这里插入图片描述

  • sendto用于在UDP协议中发送数据的函数

在这里插入图片描述

  • recvfrom用于在UDP协议中接收数据的函数

在这里插入图片描述

  • 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 connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同。
在这里插入图片描述

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.

  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.

  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好 处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为 参数;

  • sockaddr结构

struct sockaddr
{_SOCKADDR_COMMON(sa_);/*Common data: address family and length. */char sa_data[14];  /*Address data*/
}
  • sockaddr_in结构
/* Structure describing an Internet socket address. */
struct sockaddr_in
{_SOCKADDR_COMMON(sin_);in_port_t sin_port;		/* Port number. */struct in_addr sin_addr;	/* Internet address. *//* Pad to size of 'struct sockaddr.' */ussigned char sin_zero[sizeof(struct sockaddr) -__SOCKADDR_COMMON_SIZE - sizeof(in_port_t) - sizeof(struct in_addr)];
}
  • in_addr结构
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
}

in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;

简洁版:

#include <netinet/in.h>struct sockaddr_in {sa_family_t sin_family;     // 地址家族 AF_INETin_port_t sin_port;         // 16位端口号,网络字节顺序struct in_addr sin_addr;    // 32位IPv4地址,网络字节顺序char sin_zero[8];           // 不使用,填充0以使结构体大小与 struct sockaddr 相同
};

sockaddr_in 结构体包含以下字段:

  • sin_family: 地址家族,通常为 AF_INET,表示IPv4地址。
  • sin_port: 16位端口号,以网络字节顺序存储,即大端字节序。
  • sin_addr: 32位IPv4地址,以网络字节顺序存储,即大端字节序。它是一个结构体 struct in_addr,其中包含一个字段 s_addr,表示IPv4地址。
  • sin_zero: 用于填充,以使 sockaddr_in 的大小与通用地址结构 struct sockaddr 相同。

在网络编程中,当使用套接字 API 中的函数时,sockaddr_in 结构体通常需要进行类型转换,以便与通用的 struct sockaddr 结构体一起使用。这是因为套接字函数(如 bindconnectrecvfromsendto)通常使用通用地址结构 struct sockaddr 来表示地址信息。在使用时,可以使用类型强制转换sockaddr_in 转换为 struct sockaddr

套接字编程的种类:

  1. 域间套接字编程——同一个机器内
  2. 原始套接字编程——网络工具
  3. 网络套接字编程——用户间的网络通信

网络接口统一抽象化:参数的类型必须是统一的。

在这里插入图片描述

简单的UDP网络程序

Log.hpp

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};

Main.cc

#include "UdpServer.hpp"
#include <memory>
#include <cstdio>void Usage(std::string proc)
{std::cout << "\n\rUsage: "<< proc << " port[1024+]\n" << std::endl;
}std::string Handler(const std::string &str)
{std::string res = "Server get a message: ";res += str;std::cout << res << std::endl;return res;
}
std::string ExcuteCommand(const std::string &cmd)
{//SafeCheck(cmd);FILE *fp = popen(cmd.c_str(),"r");//文件中存放命令的执行结果if(nullptr == fp){perror("popen");return "error";}std::string result;char buffer[4096];while(true){char* p = fgets(buffer,sizeof(buffer),fp);if(p == nullptr)break;result += buffer;}pclose(fp);return result;
}//运行时的指令 ./udpserver port
int main(int argc,char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->Init();svr->Run(ExcuteCommand);//传递一个func指针;return 0;}

Makefile

.PHONY:all
all:udpserver udpclientudpserver:Main.ccg++ -o $@ $^ -std=c++11udpclient:UdpClient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f udpserver udpclient

UdpClient.cc

#include <iostream>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>using namespace std;void Usage(std::string proc)
{std::cout << "\n\rUsage: "<< proc << " serverip serverport\n"<<std::endl;
}
int main(int argc,char *argv[])
{if(argc != 3){Usage(argv[0]);exit(0);}std::string server_ip = argv[1];uint16_t server_port = std::stoi(argv[2]);struct sockaddr_in server;bzero(&server,sizeof(server));server.sin_family= AF_INET;server.sin_port = htons(server_port);server.sin_addr.s_addr = inet_addr(server_ip.c_str());socklen_t len = sizeof(server);int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){cout<< "socker error!!!" <<endl;return 1;}// client 也要bind,只不过不需要用户显示的bind!一般有OS自由随机选择!// 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此!// 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以!// 系统什么时候给我bind呢?首次发送数据的时候string message;char buffer[1024];while(true){cout<<"Please Enter@";getline(cin, message);//给某某发数据sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr *)&server,len);struct sockaddr_in temp;socklen_t len = sizeof(temp);size_t s = recvfrom(sockfd,buffer,1023,0,(struct sockaddr*)&temp,&len);if(s > 0){buffer[s] = 0;cout<<buffer<<endl;}}close(sockfd);return 0;
}

UdpServer.hpp

#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"//以下两种写法都可以
// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string&)> func_t;Log lg;enum{SOCKET_ERR=1,BIND_ERR
};uint16_t defaultport = 8080;
std::string default_ip = "0.0.0.0";
const int size = 1024;class UdpServer{
public:UdpServer(const uint16_t &port = defaultport, const std::string &ip = default_ip):sockfd_(0), port_(port), ip_(ip),isrunning_(false){}void Init(){// 第一步 创建udp socketsockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INETif(sockfd_ < 0){lg(Fatal, "socket create error, sockfd: %d", sockfd_);exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 第二步 bind socketstruct sockaddr_in local;bzero(&local, sizeof(local));//Linux里面的函数local.sin_family = AF_INET;local.sin_port = htons(port_); //需要保证我的端口号是网络字节序列,因为该端口号是要给对方发送的,htons函数用于将主机字节序(host byte order)的16位整数转换为网络字节序(network byte order)local.sin_addr.s_addr = inet_addr(ip_.c_str()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 //local.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0)//bind函数用于将一个套接字(socket)与特定的地址(IP地址和端口号)关联起来{lg(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));}void Run(func_t func) // 对代码进行分层{isrunning_ = true;char inbuffer[size];while(isrunning_){struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);//recvfrom是一个用于从网络套接字接收数据的函数if(n < 0){lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}inbuffer[n] = 0;std::string info = inbuffer;std::string echo_string = func(info);//func为处理函数sendto(sockfd_,echo_string.c_str(),echo_string.size(),0,(const sockaddr*)&client,len);}}~UdpServer(){if(sockfd_>0) close(sockfd_);}
private:int sockfd_;     // 网路文件描述符std::string ip_; // 任意地址bind 0uint16_t port_;  // 表明服务器进程的端口号bool isrunning_;
};

运行结果:

在这里插入图片描述

(不能使用设置了别名的命令)

window下网络编程套接字代码与Linux类似,添加对应的头文件,更改部分代码,比如:bzero函数要换成memset函数。

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

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

相关文章

树--二叉树(C语言纯手凹)

目录 目录 1.什么是树&#xff1f;&#xff08;不深入&#xff0c;仅做了解&#xff09; 2.树的表示方式 2.1孩子兄弟表示法&#xff08;左孩子右兄弟&#xff09; 2.2孩子表示法 2.3双亲表示法 3.什么是二叉树 4.二叉树分类 4.1满二叉树 4.2完全二叉树 4.3二叉搜索树…

基础小白快速学习c语言----变量的仔细介绍

变量&#xff1a; 表面理解&#xff1a;在程序运行期间&#xff0c;可以改变数值的数据&#xff0c; 深层次含义&#xff1a;变量实质上代表了一块儿内存区域&#xff0c;我们可以将变量理解为一块儿内存区域的标识&#xff0c;当我们操作变量时&#xff0c;相当于操作了变量…

qemu搭建arm64 linux kernel环境

一、环境准备 ubuntu 22.04 内核源码&#xff1a;linux-6.6.1 &#xff08;直接上最新版&#xff09; 下载链接&#xff1a;The Linux Kernel Archives 交叉编译工具链&#xff1a; sudo apt-get install gcc-12-aarch64-linux-gnu 具体能用的版本gcc-XX-arch64-linux-gnu…

如何使用IaC Scan Runner扫描IaC中的常见安全漏洞

关于IaC Scan Runner IaC Scan Runner是一款针对IaC&#xff08;基础设施即代码&#xff09;的安全漏洞扫描工具&#xff0c;在该工具的帮助下&#xff0c;广大安全开发人员可以轻松扫描IaC&#xff08;基础设施即代码&#xff09;中的常见漏洞。 IaC Scan Runner本质上是一个…

正则表达式 文本三剑客

一 正则表达式&#xff1a; 由一类特殊字符及文本字符所编写的模式&#xff0c;其中有些字符&#xff08;元字符&#xff09;不表示字符字面意义&#xff0c;而表示控制或通配的功能&#xff0c;类似于增强版的通配符功能&#xff0c;但与通配符不同&#xff0c;通配符功能是用…

2023年算法GWCA -CNN-BiLSTM-ATTENTION回归预测(matlab)

2023年算法GWCA -CNN-BiLSTM-ATTENTION回归预测&#xff08;matlab&#xff09; GWCA -CNN-BiLSTM-Attention长城建造算法优化卷积-长短期记忆神经网络结合注意力机制的数据回归预测 Matlab语言。 长城建造算法&#xff08;Great Wall Construction Algorithm&#xff0c;GWC…

防御保护第四次作业

防火墙的智能选路 就近选路 --- 我们希望在访问不同运营商的服务器是&#xff0c;通过对应运营商的链路。这样可以高 通信效率&#xff0c;避免绕路。 策略路由 -- PBR 传统的路由&#xff0c;仅基于数据包中的目标IP地址查找路由表。仅关心其目标&#xff0c;所以&#…

排序【数据结构】

文章目录 一、 稳定性二、排序1. 插入排序(1) 直接插入排序(2) 希尔排序 2. 选择排序(1) 直接选择排序(2) 堆排序 3. 交换排序(1) 冒泡排序(2) 快速排序① 普通版快排② 关于优化快排③ 快速排序的非递归方式 4. 归并排序5. 计数排序 三、 总结 一、 稳定性 在计算机科学中&am…

CHS_03.2.3.2_2+进程互斥的硬件实现方法

CHS_03.2.3.2_2进程互斥的硬件实现方法 知识总览中断屏蔽方法TestAndSet指令Swap指令 知识回顾 进程互斥的四种软件实现方法 知识总览 这个小节我们会介绍另外的三种进程互斥的硬件实现方法 那么 这个小节的学习过程当中 大家需要注意理解各个方法的原理 并且要稍微的了解各个…

【Uni-App】Vue3如何使用pinia状态管理库与持久化

安装插件 pinia-plugin-unistorage 引入 // main.js import { createSSRApp } from "vue"; import * as Pinia from "pinia"; import { createUnistorage } from "pinia-plugin-unistorage";export function createApp() {const app create…

SpringBoot不同的@Mapping使用

文章目录 一、介绍二、使用 一、介绍 一般Mapping类注解在Spring框架中用于将HTTP请求映射到对应的处理器方法。它们各自对应于不同类型的HTTP方法&#xff0c;主要用于RESTful Web服务中。以下是每个注解的作用&#xff1a; GetMapping: 用于映射HTTP GET请求到处理器方法。通…

Life is Strange 奇异人生汉化指南

奇异人生汉化指南 引言&#xff1a;在搜索引擎上看了许多的攻略&#xff0c;都无法得到指向性明确的安装步骤&#xff0c;其中最令人不解的分别为汉化包与汉化包的安装地址&#xff0c;以下会以汉化包获取与汉化包安装地址两个维度来确保汉化的正确&#xff0c;以及在最终附上…

爬虫学习笔记-get请求获取豆瓣电影排名多页数据★★★★★

1. 导入爬虫需要使用的包 import urllib.request import urllib.parse 2.创建请求函数 def create_request(page): # 定义不变的url部分 base_url https://movie.douban.com/j/chart/top_list?type5&interval_id100%3A90&action& # 根据规律定义data拼接url …

算法沉淀——二分查找(leetcode真题剖析)

算法沉淀——二分查找 01.二分查找02.在排序数组中查找元素的第一个和最后一个位置03.搜索插入位置04.x 的平方根05.山脉数组的峰顶索引06.寻找峰值07.寻找旋转排序数组中的最小值08.LCR 173. 点名 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元…

【算法专题】二分查找(入门)

&#x1f4d1;前言 本文主要是二分查找&#xff08;入门&#xff09;的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日…

幻兽帕鲁服务器怎么收费?4核16G配置

幻兽帕鲁服务器价格多少钱&#xff1f;4核16G服务器Palworld官方推荐配置&#xff0c;阿里云4核16G服务器32元1个月、96元3个月&#xff0c;腾讯云换手帕服务器服务器4核16G14M带宽66元一个月、277元3个月&#xff0c;8核32G22M配置115元1个月、345元3个月&#xff0c;16核64G3…

前言:穿越迷雾,探索C语言指针的奇幻之旅

各位少年&#xff0c;大家好&#xff0c;我是博主那一脸阳光&#xff0c;今天给大家分享指针&#xff0c;内存和地址的使用&#xff0c;以及使用。 前言&#xff1a; 在编程的世界中&#xff0c;若论灵活多变、深邃神秘的角色&#xff0c;非“指针”莫属。如同哈利波特手中的魔…

深度学习快速入门--7天做项目

深度学习快速入门--7天做项目 0. 引言1. 本文内容2. 深度学习是什么3. 项目是一个很好的切入点4. 7天做项目4.1 第一天&#xff1a;数据整理4.2 第二天&#xff1a;数据处理4.3 第三天&#xff1a;简单神经网络设计4.4 第四天&#xff1a;分析效果与原因4.5 第五天&#xff1a;…

基于SpringBoot的玩具租赁系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

【原创课程】KUKA机器人与S7-1200进行Profinet通讯

一、KUKA机器人与S7-1200进行Profinet通讯 1、硬件配置 ①硬件配置 名称 型号 数量 PLC S7_1217C 1个 机器人 KUKA_KR-210 1台 2、机器人一侧参数配置 ①添加备选软件包 首先&#xff0c;从KUKA机器人控制柜中将KOP备选软件包拷贝出来&#xff0c;然后在”WorkVi…