【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序
- 认识端口号
- 网络字节序
- 处理字节序函数 htonl、htons、ntohl、ntohs
- socket
- socket编程接口
- sockaddr结构
- 结尾实现UDP程序的socket接口使用解析
- socket
- 处理 IP 地址的函数
- 初始化sockaddr_in
- bind
- recvfrom
- sendto
- 实现一个简单的UDP网络程序
- 封装服务器相关代码
- 封装客户端相关代码
- 实验结果
认识端口号
我们把数据从A主机发送到B主机,是目的吗?不是,真正通信的不是这两个机器!其实是这两台机器上面的软件(人)
数据有IP(公网)
标识一台唯一的主机,用谁来标识各自主机上客户或者服务进程的唯一性呢?
为了更好的表示一台主机上服务进程的唯一性,我们采用端口号port
,标识服务器进程,客户端进程的唯一性!
端口号(port)是传输层协议的内容:
- 端口号是一个2字节16位的整数;
- 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理;
- IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
- 一个端口号只能被一个进程占用
ip地址(主机全网唯一性) + 该主机上的端口号,标识该服务器上进程的唯一性
IP保证全网唯一,port保证在主机内部的唯一性
主机上对应的服务进程,在全网中是唯一的一个进程。
网络通信的本质:其实就是进程间通信
- 需要让不同的进程,先看到同一份资源—网络
- 通信就是在做IO,所以我们所有的上网行为,无外乎两种:我要把我的数据发出去、我要收到别人给我发的数据
进程已经有pid,为什么要有port呢?
- 系统是系统,网络是网络,单独设置—系统与网络解耦
- 需要客户端每次都能找到服务器进程—服务器的唯一性不能做任何改变— IP+port不能随意改变不会轻易改变。
- 不是所有的进程都要提供网络服务或者请求,但是所有的进程都需要pid。
进程+port–>网络服务进程
底层OS如何根据port找到指定的进程:OS内部采用hash方案,在OS内部维护了一个基于端口号的哈希表,key就是端口号,value就是task_struct的地址。有这个端口号就可以找到PCB,继而找到文件描述符表,文件描述符对象,对象找到了那么这个文件的缓冲区也就能找到,然后就可以将数据拷贝到缓冲区,最后就相当于我们将网络数据放到了文件中,如同读文件一样就将数据读上去了。
一个进程可以绑定多个端口号;但是一个端口号不能被多个进程绑定;
理解源端口号和目的端口号:
传输层协议(TCP和UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号。 就是在描述 “数据是谁发的,要发给谁”;
认识TCP(Transmission Control Protocol 传输控制协议)协议:
- 传输层协议
- 有连接(相当于打电话必须先接通才能通话)
- 可靠传输
- 面向字节流
认识UDP协议
认识UDP(User Datagram Protocol 用户数据报协议)协议:
- 传输层协议
- 无连接(相当于发送邮件只需要知道你的邮箱地址不需要你同意直接就能发给你)
- 不可靠传输
- 面向数据报
网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢?
- 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
- 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
- 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。
- TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
- 如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可;
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换
- 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
- 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
- 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
处理字节序函数 htonl、htons、ntohl、ntohs
函数原型:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
函数作用:
将数据在不同字节序之间进行转换。
函数的详细介绍:
htonl()
函数:将一个 32 位无符号整数(unsigned int)从本地字节序转换为网络字节序(大端字节序)。htons()
函数:将一个 16 位无符号整数(unsigned short)从本地字节序转换为网络字节序(大端字节序)。ntohl()
函数:将一个 32 位无符号整数(unsigned int)从网络字节序(大端字节序)转换为本地字节序。ntohs()
函数:将一个 16 位无符号整数(unsigned short)从网络字节序(大端字节序)转换为本地字节序。
socket
socket编程接口
// 创建 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);
- 网络套接字编程(多应用于:网络跨主机之间多主机通信、支持本地通信)
- 原始套接字(可以跨过传输层向下访问更底层的接口)
- unix或间套接字(仅本地通信)
按道理要实现上述三种套接字应该要三套不同的接口,但是设计者只设计了一套接口,通过不同的参数解决所有网络或其他场景下的通信问题
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'. */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};
虽然socket
api的接口是sockaddr
,但是我们真正在基于IPv4编程
时,使用的数据结构是sockaddr_in
;这个结构里主要有三部分信息:地址类型、端口号、IP地址。
in_addr结构:
typedef uint32_t in_addr_t;
struct in_addr{in_addr_t s_addr;};
in_addr
用来表示一个IPv4
的IP
地址。其实就是一个32位的整数。
显示当前户籍UDP连接状况与端口号的使用情况:
sudo netstat -nuap
结尾实现UDP程序的socket接口使用解析
socket
int socket(int domain, int type, int protocol);
函数作用:
用于创建一个新的网络套接字的系统调用。
函数参数:
domain
参数指定了网络协议族:
AF_INET
表示 IPv4 协议AF_INET6
表示 IPv6 协议AF_UNIX
表示 Unix 域协议
type
参数指定了套接字的类型
SOCK_STREAM
表示面向连接的流套接字SOCK_DGRAM
表示无连接的数据报套接字SOCK_RAW
表示原始套接字。
protocol
参数指定了使用的协议
IPPROTO_TCP
表示 TCP 协议IPPROTO_UDP
表示 UDP 协议- 参数设置为
0
时,系统会根据指定的 domain 和 type 参数选择一个默认的协议。这通常是最常用的协议,例如对于 AF_INET 和 SOCK_STREAM 的组合,通常使用的协议是 TCP(即 IPPROTO_TCP)。
使用 socket() 函数的一般流程如下:
- 创建一个套接字:调用 socket() 函数,指定 domain、type 和 protocol 参数,返回一个新的套接字描述符。
- 绑定套接字到本地地址:调用 bind() 函数,将套接字和一个本地地址绑定,以便其他程序可以通过该地址找到该套接字。
- 监听连接请求:如果创建的是面向连接的流套接字,可以调用 listen() 函数,开始监听连接请求。
- 接受连接请求:如果创建的是面向连接的流套接字,可以调用 accept() 函数,接受一个连接请求,返回一个新的套接字描述符,用于与客户端通信。
- 发送和接收数据:调用 send() 和 recv() 函数,向对方发送数据或接收对方发送的数据。
- 关闭套接字:调用 close() 函数,关闭套接字描述符,释放相关资源。
处理 IP 地址的函数
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
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);
inet_aton()
函数:将一个字符串形式的 IP 地址转换为一个二进制形式的 IP 地址。如果转换成功,函数返回非零值,否则返回零。inet_addr()
函数:将一个字符串形式的 IP 地址转换为一个 32 位的整数,该整数表示为网络字节序。如果转换成功,函数返回一个非零值(即返回一个网络字节序表示的 IP 地址),否则返回 INADDR_NONE。inet_network()
函数:将一个字符串形式的 IP 地址的网络部分转换为一个 32 位的整数,该整数表示为网络字节序。inet_ntoa()
函数:将一个二进制形式的 IP 地址转换为一个字符串形式的 IP 地址。注意,该函数返回的是一个指向静态缓冲区的指针,因此不要将其作为返回值传递给其他函数。inet_makeaddr()
函数:根据网络号和主机号创建一个 IP 地址。inet_lnaof()
函数:从一个二进制形式的 IP 地址中提取主机号部分。inet_netof()
函数:从一个二进制形式的 IP 地址中提取网络号部分。
初始化sockaddr_in
sockaddr_in
是一个 IPv4 地址结构体,用于存储 IP 地址和端口号信息。在使用套接字函数时,通常需要将地址信息存储在 sockaddr_in 结构体中,并将其作为参数传递给函数
struct sockaddr_in local; // 定义了一个变量,栈,用户bzero(&local, sizeof(local));//用于将指定的内存区域清零local.sin_family = AF_INET;//将结构体成员 sin_family 设置为 AF_INET,表示使用 IPv4 地址族local.sin_port = htons(_port); //结构体成员 sin_port 设置为要使用的端口号,使用 htons() 函数将端口号转换为网络字节序(大端字节序)local.sin_addr.s_addr = inet_addr(_ip.c_str());//将结构体成员 sin_addr 设置为要使用的 IP 地址,使用 inet_addr() 函数将 IP 地址转换为网络字节序(大端字节序)//在实际开发中,可以使用 inet_pton() 函数将字符串形式的 IP 地址转换为一个 struct in_addr 类型的结构体,该结构体包含了 IP 地址的二进制表示
bind
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
函数作用:
用于将一个本地地址(IP 地址和端口号)与一个套接字关联起来的函数
函数参数:
socket
参数是一个指定了套接字的文件描述符。addr
参数是一个指向struct sockaddr
类型的结构体的指针,该结构体包含了要绑定的本地地址信息。addrlen
参数是 addr 结构体的长度
函数返回值:
返回值为 0 表示绑定成功,-1 表示绑定失败,错误码保存在 errno 变量中。
recvfrom
ssize_t recvfrom(int socket, void *restrict buffer, size_t length,int flags, struct sockaddr *restrict address,socklen_t *restrict address_len);
函数作用:
用于从已连接或未连接的套接字接收数据的函数
函数参数:
socket
参数是指定了要接收数据的套接字的文件描述符。buf
参数是一个指向接收数据的缓冲区的指针。len
参数是缓冲区的大小。flags
参数是一组标志位,可以用来指定接收数据的行为。为0表示默认address
参数是一个指向 struct sockaddr 类型的结构体的指针,用于存储发送数据的远程地址。addrlen
参数是 src_addr 结构体的长度。
函数返回值:
函数返回值为接收到的数据的字节数,如果没有数据可用,则返回 0。如果发生错误,则返回 -1,错误码保存在 errno 变量中
sendto
ssize_t sendto(int socket, const void *message, size_t length,int flags, const struct sockaddr *dest_addr,socklen_t dest_len);
函数作用:
用于向已连接或未连接的套接字发送数据的函数
函数参数:
socket
参数是指定了要发送数据的套接字的文件描述符。buf
参数是一个指向要发送数据的缓冲区的指针。len
参数是要发送数据的字节数。flags
参数是一组标志位,可以用来指定发送数据的行为。dest_addr
参数是一个指向 struct sockaddr 类型的结构体的指针,用于指定接收数据的远程地址。dest_len
参数是 dest_addr 结构体的长度。
函数返回值:
函数返回值为发送数据的字节数,如果发生错误,则返回 -1,错误码保存在 errno 变量中。
实现一个简单的UDP网络程序
封装服务器相关代码
udpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>namespace Server
{using namespace std;static const string defaultIp = "0.0.0.0"; //TODOstatic const int gnum = 1024;enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};class udpServer{public:udpServer(const uint16_t &port, const string &ip = defaultIp):_port(port), _ip(ip), _sockfd(-1){}void initServer(){// 1. 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){cerr << "socket error: " << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERR);}cout << "socket success: " << " : " << _sockfd << endl;// 2. 绑定port,ip(TODO)// 未来服务器要明确的port,不能随意改变struct sockaddr_in local; // 定义了一个变量,栈,用户bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str());//local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind,服务器的真实写法int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));if(n == -1){cerr << "bind error: " << errno << " : " << strerror(errno) << endl;exit(BIND_ERR);}// UDP Server 的预备工作完成}void start(){// 服务器的本质其实就是一个死循环char buffer[gnum];for(;;){// 读取数据struct sockaddr_in peer;socklen_t len = sizeof(peer); //必填ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);// 1. 数据是什么 2. 谁发的?if(s > 0){buffer[s] = 0;string clientip = inet_ntoa(peer.sin_addr); //1. 网络序列 2. int->点分十进制IPuint16_t clientport = ntohs(peer.sin_port);string message = buffer;cout << clientip <<"[" << clientport << "]# " << message << endl;}}}~udpServer(){}private:uint16_t _port;string _ip; // 实际上,一款网络服务器,不建议指明一个IPint _sockfd;// func_t _callback; //回调};
}
udpServer.cc
#include "udpServer.hpp"
#include <memory>using namespace std;
using namespace Server;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}// ./udpServer port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::unique_ptr<udpServer> usvr(new udpServer(port));usvr->initServer();usvr->start();return 0;
}
封装客户端相关代码
udpClient.hpp
#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>namespace Client
{using namespace std;class udpClient{public:udpClient(const string &serverip, const uint16_t &serverport) : _serverip(serverip),_serverport(serverport), _sockfd(-1), _quit(false){}void initClient(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error: " << errno << " : " << strerror(errno) << endl;exit(2);}cout << "socket success: " << " : " << _sockfd << endl;// 2. client要不要bind[必须要的],client要不要显示的bind,需不需程序员自己bind?不需要// 写服务器的是一家公司,写client是无数家公司 -- 由OS自动形成端口进行bind!-- OS在什么时候,如何bind}void run(){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverip.c_str());server.sin_port = htons(_serverport);string message;while(!_quit){cout << "Please Enter# ";cin >> message;sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));}}~udpClient(){}private:int _sockfd;string _serverip;uint16_t _serverport;bool _quit;};
} // namespace Client
udpClient.cc
#include "udpClient.hpp"
#include <memory>using namespace Client;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<udpClient> ucli(new udpClient(serverip, serverport));ucli->initClient();ucli->run();return 0;
}
实验结果
如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀
相关文章:
【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序
认识端口号网络字节序处理字节序函数 htonl、htons、ntohl、ntohs socketsocket编程接口sockaddr结构结尾实现UDP程序的socket接口使用解析socket处理 IP 地址的函数初始化sockaddr_inbindrecvfromsendto 实现一个简单的UDP网络程序封装服务器相关代码封装客户端相关代码实验结…...
Python学习笔记第六十四天(Matplotlib 网格线)
Python学习笔记第六十四天 Matplotlib 网格线普通网格线样式网格线 后记 Matplotlib 网格线 我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。 grid() 方法语法格式如下: matplotlib.pyplot.grid(bNone, whichmajor, axisboth, )参数说明:…...
机器学习与模式识别3(线性回归与逻辑回归)
一、线性回归与逻辑回归简介 线性回归主要功能是拟合数据,常用平方误差函数。 逻辑回归主要功能是区分数据,找到决策边界,常用交叉熵。 二、线性回归与逻辑回归的实现 1.线性回归 利用回归方程对一个或多个特征值和目标值之间的关系进行建模…...
vue启动配置npm run serve,动态环境变量,根据不同环境访问不同域名
首先创建不同环境的配置文件,比如域名和一些常量,创建一个env文件,先看看文件目录 env.dev就是dev环境的域名,.test就是test环境域名,其他同理,然后配置package.json文件 {"name": "require-admin&qu…...
HTML <strike> 标签
HTML5 中不支持 <strike> 标签在 HTML 4 中用于定义删除线文本。 定义和用法 <strike> 标签可定义加删除线文本定义。 浏览器支持 元素ChromeIEFirefoxSafariOpera<strike>YesYesYesYesYes 所有浏览器都支持 <strike> 标签。 HTML 与 XHTML 之间…...
数学建模-模型详解(1)
规划模型 线性规划模型: 当涉及到线性规划模型实例时,以下是一个简单的示例: 假设我们有两个变量 x 和 y,并且我们希望最大化目标函数 Z 5x 3y,同时满足以下约束条件: x > 0y > 02x y < 10…...
MySQL 数据库表的基本操作
一、数据库表概述 在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。 二、数…...
企业微信电脑端开启chrome调试
首先: Mac端调试开启的快捷键:control shift command d Window端调试开启的快捷键: control shift alt d 这边以Mac为例,我们可以在电脑顶部看到调试的入口: 然后我们点击 『浏览器、webView相关』菜单,勾选上…...
Maven官网下载配置新仓库
1.Maven的下载 Maven的官网地址:Maven – Download Apache Maven 点击Download,查找 Files下的版本并下载如下图: 2.Maven的配置 自己在D盘或者E盘创建一个文件夹,作为本地仓库,存放项目依赖。 将下载好的zip文件进行解…...
银河麒麟V10 达梦安装教程
安装前先准备要安装包,包需要需要区分X86和arm架构。 版本为:dm8_20230419_FTarm_kylin10_sp1_64.iso 达梦数据库下载地址: https://www.aliyundrive.com/s/Qm7Es5BQM5U 第一步创建用户 su - root 1. 创建安装用户组 dminstall。 groupad…...
Python操作MongoDB数据库
安装MongoDB库 pip install pymongopython 代码 Author: tkhywang 2810248865qq.com Date: 2023-08-21 10:22:30 LastEditors: tkhywang 2810248865qq.com LastEditTime: 2023-08-21 11:17:45 FilePath: \PythonProject02\MongoDB 数据库.py Description: 这是默认设置,请设置…...
《HeadFirst设计模式(第二版)》第十一章代码——代理模式
代码文件目录: RMI: MyRemote package Chapter11_ProxyPattern.RMI;import java.rmi.Remote; import java.rmi.RemoteException;public interface MyRemote extends Remote {public String sayHello() throws RemoteException; }MyRemoteClient packa…...
QT的工程文件认识
目录 1、QT介绍 2、QT的特点 3、QT模块 3.1基本模块 3.2扩展模块 4、QT工程创建 1.选择应用的窗体格式 2.设置工程的名称与路径 3.设置类名 4.选择编译器 5、QT 工程解析 xxx.pro 工程配置 xxx.h 头文件 main.cpp 主函数 xxx.cpp 文件 6、纯手工创建一个QT 工程…...
typeScript安装及TypeScript tsc 不是内部或外部命令,也不是可运行的程序或批处理文件解决办法
一、typeScript安装: 1、首先确定系统中已安装node, winr 输入cmd 打开命令行,得到版本号证明系统中已经安装node node -v //v18.17.0 2、使用npm 全局安装typeScript # 全局安装 TypeScript npm i -g typescript 二、检查是否安装成功ts #检查t…...
SWUST 派森练习题:P111. 摩斯密码翻译器
描述 摩斯密码(morse code),又称摩斯电码、摩尔斯电码(莫尔斯电码),是一种时通时断的信号代码,通过不同的信号排列顺序来表达不同的英文字母、数字和标点符号;通信时,将英文字母等内…...
如何在控制台查看excel内容
背景 最近发现打开电脑的excel很慢,而且使用到的场景很少,也因为mac自带了预览的功能。但是shigen就是闲不住,想自己搞一个excel预览软件,于是在一番技术选型之后,我决定使用python在控制台显示excel的内容。 具体的需…...
Echarts、js编写“中国主要城市空气质量对比”散点图 【亲测】
本次实验通过可视化工具Echarts来对全国主要城市的PM2.5的值进行直观的展示,使人们可以快速的发现信息的关键点,从而对各个城市的空气质量情况有直观的了解。 先看效果 上代码: <!DOCTYPE html> <html>&…...
linux不分区直接在文件系统根上开swap
root下,直接创swapfile dd if/dev/zero of/swapfile bs1M count8192然后 mkswap swapfile swapon swapfile修改fstab # /etc/fstab: static file system information. # # Use blkid to print the universally unique identifier for a # device; this may be us…...
React请求机制优化思路 | 京东云技术团队
说起数据加载的机制,有一个绕不开的话题就是前端性能,很多电商门户的首页其实都会做一些垂直的定制优化,比如让请求在页面最早加载,或者在前一个页面就进行预加载等等。随着react18的发布,请求机制这一块也是被不断谈起…...
CompletableFuture总结和实践
CompletableFuture被设计在Java中进行异步编程。异步编程意味着在主线程之外创建一个独立的线程,与主线程分隔开,并在上面运行一个非阻塞的任务,然后通知主线程进展,成功或者失败。 一、概述 1.CompletableFuture和Future的区别&…...
使用Nginx调用网关,然后网关调用其他微服务
问题前提:目前我的项目是已经搭建了网关根据访问路径路由到微服务,然后现在我使用了Nginx将静态资源都放在了Nginx中,然后我后端定义了一个接口访问一个html页面,但是html页面要用到静态资源,这个静态资源在我的后端是…...
windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】
windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】 文章目录 windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】1. 安装IIS必要WebDav组件2. 客户端测试3. cpolar内网穿透3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表3.4 浏览器访…...
PAT 1097 Deduplication on a Linked List
个人学习记录,代码难免不尽人意 Given a singly linked list L with integer keys, you are supposed to remove the nodes with duplicated absolute values of the keys. That is, for each value K, only the first node of which the value or absolute value o…...
Flink 数据集成服务在小红书的降本增效实践
摘要:本文整理自实时引擎研发工程师袁奎,在 Flink Forward Asia 2022 数据集成专场的分享。本篇内容主要分为四个部分: 小红书实时服务降本增效背景Flink 与在离线混部实践实践过程中遇到的问题及解决方案未来展望 点击查看原文视频 & 演…...
jellyfin使用ipv6+DDNS实现外网访问
前言 原本使用frp的方案进行外网访问jellyfin,但是阿里云的轻量服务器的带宽只有5M,只能支持看1080p的视频,看4K有点吃力,为了有更好的观影体验,选择ipv6DDNS的方式实现外网访问,此方案能跑满群晖的上行带宽…...
Codeforces EDU 151 Div.2
文章目录 A. Forbidden IntegerB. Come TogetherC. Strong PasswordD. Rating SystemE. Boxes and Balls A. Forbidden Integer Problem - A - Codeforces 给定整数n,从1~k中选择除了x的数,使这些数之和为n,每个数可以选择无限次 爆搜&…...
V2board缓存投毒漏洞复现
1.什么是缓存投毒 缓存投毒(Cache poisoning),通常也称为域名系统投毒(domain name system poisoning),或DNS缓存投毒(DNS cache poisoning)。它是利用虚假Internet地址替换掉域名系…...
2023面试八股文 ——Java基础知识
Java基础知识 一.Java概述何为编程什么是Javajdk1.5之后的三大版本JVM、JRE和JDK的关系什么是跨平台性?原理是什么Java语言有哪些特点什么是字节码?采用字节码的大好处是什么什么是Java程序的主类?应用程序和小程序的主类有何不同?…...
在linux系统中修改mysql数据目录
目录 1.查看mysql默认存储路径2.停止mysql服务3.移动或复制原数据目录4.修改配置文件5.修改启动文件6.配置AppArmor访问控制规则7.重启apparmor服务8.启动mysql 1.查看mysql默认存储路径 在/etc/mysql/mysql.conf.d/mysqld.cnf中的datadir配置项。 datadir /var/lib/mysql2…...
ORB-SLAM2学习笔记9之图像帧Frame
先占坑,明天再完善… 文章目录 0 引言1 Frame类1.1 成员函数1.2 成员变量 2 Frame类的用途 0 引言 ORB-SLAM2学习笔记8详细了解了图像特征点提取和描述子的生成,本文在此基础上,继续学习ORB-SLAM2中的图像帧,也就是Frame类&#…...
国外购物网站系统/网站制作免费
https://www.zhihu.com/question/62277180/answer/196715976 从最高层看,G1的collector一侧其实就是两个大部分: * 全局并发标记(global concurrent marking) * 拷贝存活对象(evacuation) 而这两部分可以相…...
给网站整一个客服 怎么做/百度搜索开放平台
2019独角兽企业重金招聘Python工程师标准>>> MySQL索引?这玩意儿还能简单聊?明显是在挖坑,幸好老夫早有准备,切听我一一道来。 一、索引是什么? 索引是帮助MySQL高效获取数据的数据结构。 二、索能干什么? 索引非常…...
免费网站制作手机软件的app/seo运营推广
文章目录写在前面投光物平行光点光源衰减选择正确的值实现衰减聚光手电筒平滑/软化边缘总结练习写在前面 原文链接。原文应该是github上的一个项目,本文主要用来记录一些知识点和自己遇到的问题。 投光物 我们目前使用的光照都来自于空间中的一个点。它能给我们不…...
网站备案到期/简单的seo
php加图片水印 最近项目中有功能需求要加图片水印,下边分享给你大家。 直接上代码: <?php //要加水印的图片 $dst_path ./timg.jpeg; //水印图片 $src_path ./logo.png;createWater($dst_path,$src_path);/*** dst_path 图片路径* src_path 水印…...
python 做网站缺点/网站优化推广seo
前言 本篇文章继续我们的微软挖掘系列算法总结,前几篇文章已经将相关的主要算法做了详细的介绍,我为了展示方便,特地的整理了一个目录提纲篇:大数据时代:深入浅出微软数据挖掘算法总结连载, 有兴趣的童鞋可…...
汕头网站建设公司有哪些/做网站的步骤
⭐️这篇博客是对上一篇博客中进行扩展,我们应该知道,位图的效率确实很高,但是有一个致命的缺点——只能对整形数据进行处理。今天要介绍的布隆过滤器就是来解决这个缺点的,看完下面的内容,相信你会了解这个数据结构。…...