# issue 8 TCP内部原理和UDP编程
TCP 通信三大步骤:
1 三次握手建立连接;
2 开始通信,进行数据交换;
3 四次挥手断开连接;
一、TCP内部原理--三次握手
【第一次握手】套接字A∶"你好,套接字B。我这儿有数据要传给你,建立连接吧。"
【第二次握手】套接字B∶"好的,我这边已就绪。"
【第三次握手】套接字A∶"谢谢你受理我的请求。"
首先,请求连接的主机A 向主机B 传递如下信息∶
[SYN] SEQ:1000, ACK: -
该消息中SEQ 为1000,ACK 为空,而SEQ 为1000 的含义如下∶
"现传递的数据包序号为1000,如果接收无误,请通知我向您传递1001 号数据包。"这是首
次请求连接时使用的消息,又称SYN。SYN 是Synchronization 的简写,表示收发数据前传输
的同步消息。
接下来主机B 向A 传递如下消息∶
[SYN+ACK]SEQ:2000, ACK:1001
此时SEQ 为2000,ACK 为1001,而SEQ 为2000 的含义如下∶
"现传递的数据包序号为2000 如果接收无误,请通知我向您传递2001 号数据包。"
而ACK1001 的含义如下∶
"刚才传输的SEQ 为1000 的数据包接收无误,现在请传递SEQ 为1001 的数据包。"
对主机A 首次传输的数据包的确认消息(ACK1001)和为主机B 传输数据做准备的同步消息
(SEQ2000)拥绑发送,因此,此种类型的消息又称SYN+ACK。
收发数据前向数据包分配序号,并向对方通报此序号,这都是为防止数据丢失所做的准备。
通过向数据包分配序号并确认,可以在数据丢失时马上查看并重传丢失的数据包。因此,TCP
可以保证可靠的数据传输。最后观察主机A 向主机B 传输的消息∶
[ACK]SEQ:1001, ACK:2001
TCP 连接过程中发送数据包时需分配序号。
在之前的序号1000 的基础上加1,也就是分配1001。此时该数据包传递如下消息∶
"已正确收到传输的SEQ 为2000 的数据包,现在可以传输SEQ 为2001 的数据包。"
这样就传输了添加ACK2001 的ACK 消息。至此,主机A 和主机B 确认了彼此均就绪。
特么的,文字太复杂了,来个通俗易懂的。
TCP 三次握手好比在一个夜高风黑的夜晚,你一个人在小区里散步,不远处看见小区里的
一位漂亮妹子迎面而来,但是因为路灯有点暗等原因不能100%确认,所以要通过招手的方
式来确定对方是否认识自己。
你首先向妹子招手(syn),妹子看到你向自己招手后,向你点了点头挤出了一个微笑
(ack)。你看到妹子微笑后确认了妹子成功辨认出了自己(进入established 状态)。
但是妹子有点不好意思,向四周看了一看,有没有可能你是在看别人呢,她也需要确认一下。
妹子也向你招了招手(syn),你看到妹子向自己招手后知道对方是在寻求自己的确认,于是
也点了点头挤出了微笑(ack),妹子看到对方的微笑后确认了你就是在向自己打招呼(进入
established 状态)。
二、TCP内部原理--四次挥手
三、UDP编程--UDP基本原理
在4 层TCP/IP 模型中,第二层传输(Transport)层分为TCP 和UDP 这2 种。数据交换
过程可以分为通过TCP 套接字完成的TCP 方式和通过UDP 套接字完成的UDP 方式。
UDP 套接字的特点:
我们可以通过信件说明UDP 的工作原理,这是讲解UDP 时使用的传统示例,它与UDP
特性完全相符。寄信前应先在信封上填好寄信人和收信人的地址,之后贴上邮票放进邮筒
即可。当然,信件的特点使我们无法确认对方是否收到。另外,邮寄过程中也可能发生信件
丢失的情况。也就是说,信件是一种不可靠的传输方式。与之类似,UDP 提供的同样是不可
靠的数据传输服务。
"既然如此,TCP 应该是更优质的协议吧?"
如果只考虑可靠性,TCP 的确比UDP 好。但UDP 在结构上比TCP 更简洁。UDP 不会发
送类似ACK 的应答消息,也不会像SEQ 那样给数据包分配序号。因此,UDP 的性能有时比
TCP 高出很多。编程中实现UDP 也比TCP 简单。另外,UDP 的可靠性虽比不上TCP,但也不
会像想象中那么频繁地发生数据损毁。因此,在更重视性能而非可靠性的情况下,UDP 是一
种很好的选择。
既然如此,UDP 的作用到底是什么呢?为了提供可靠的数据传输服务,TCP 在不可靠的
IP 层进行流控制,而UDP 就缺少这种流控制机制。
流控制是区分UDP 和TCP 的最重要的标志。但若从TCP 中除去流控制,所剩内容也屈
指可数。也就是说,TCP 的生命在于流控制。
如果把TCP 比喻为电话,把UDP 比喻为信件。但这只是形容协议工作方式,并没有包
含数据交换速率。请不要误认为"电话的速度比信件快,因此TCP 的数据收发速率也比UDP
快"。实际上正好相反。TCP 的速度无法超过UDP,但在收发某些类型的数据时有可能接近
UDP。例如,每次交换的数据量越大,TCP 的传输速率就越接近UDP 的传输速率。
从上图可以看出,IP 的作用就是让离开主机B 的UDP 数据包准确传递到主机A。但把
UDP 包最终交给主机A 的某一UDP 套接字的过程则是由UDP 完成的。UDP 最重要的作用就
是根据端口号将传到主机的数据包交付给最终的UDP 套接字。
其实在实际的应用场景中,UDP 也具有一定的可靠性。网络传输特性导致信息丢失频发,
可若要传递压缩文件(发送1 万个数据包时,只要丢失1 个就会产生问题),则必须使用
TCP,因为压缩文件只要丢失一部分就很难解压。但 通过网络实时传输视频或音频时的情况
有所不同。对于多媒体数据而言,丢失一部分也没有太大问题,这只会引起短暂的画面抖动,
或出现细微的杂音。但因为需要提供实时服务,速度就成为非常重要的因素,此时需要考虑
使用UDP。但UDP 并非每次都快于TCP,TCP 比UDP 慢的原因通常有以下两点。
1 收发数据前后进行的连接设置及清除过程。
2 收发数据过程中为保证可靠性而添加的流控制。
尤其是收发的数据量小但需要频繁连接时,UDP 比TCP 更高效。
四、UDP服务端(上)
UDP 中的服务器端和客户端没有连接
UDP 服务器端/客户端不像TCP 那样在连接状态下交换数据,因此与TCP 不同,无需经
过连接过程。也就是说,不必调用TCP 连接过程中调用的listen 函数和accept 函数。UDP 中
只有创建套接字的过程和数据交换过程。
UDP 服务器端和客户端均只需1 个套接字
TCP 中,套接字之间应该是一对一的关系。若要向10 个客户端提供服务,则除了守门
的服务器套接字外,还需要10 个服务器端套接字。但在UDP 中,不管是服务器端还是客户
端都只需要1 个套接字。之前解释UDP 原理时举了信件的例子,收发信件时使用的邮筒可
以比喻为UDP 套接字。只要附近有1 个邮筒,就可以通过它向任意地址寄出信件。同样,
只需1 个UDP 套接字就可以向任意主机传输数据
上图展示了1 个UDP 套接字与2 个不同主机交换数据的过程。也就是说,只需1 个UDP
套接字就能和多台主机通信。
创建好TCP 套接字后,传输数据时无需再添加地址信息。因为TCP 套接字将保持与对方
套接字的连接。换言之,TCP 套接字知道目标地址信息。但UDP 套接字不会保持连接状态(UDP
套接字只有简单的邮筒功能),因此每次传输数据都要添加目标地址信息。这相当于寄信前
在信件中填写地址。以下为:填写地址并传输数据时调用的UDP 相关函数。
发送:
#include<sys/socket.h>
ssize_t sendto(int sock,void*buff,size_t nbytes,int flags,struct sockaddr *to, socklen_t addrlen);
→成功时返回传输的字节数,失败时返回-1。
● sock
用于传输数据的UDP 套接字文件描述符。
● buff
保存待传输数据的缓冲地址值。
● nbytes
待传输的数据长度,以字节为单位。
● flags
可选项参数,若没有则传递0。
● to
存有目标地址信息的sockaddr 结构体变量的地址值。
● addrlen
传递给参数to 的地址值结构体变量长度。
上述函数与之前的TCP 输出函数最大的区别在于,此函数需要向它传递目标地址信息。接
下来介绍接收UDP 数据的函数。UDP 数据的发送端并不固定,因此该函数定义为可接收发
送端信息的形式,也就是将同时返回UDP 数据包中的发送端信息。
接收:
#include<sys/socket.h>
ssize_t recvfrom(int sock, void *buff,size_t nbytes, int flags,struct sockaddr*from, socklen_t*addrlen);
→成功时返回接收的字节数,失败时返回-1。
●sock 用于接收数据的UDP 套接字文件描述符。
●buff 保存接收数据的缓存地址值
●nbytes 可接收的最大字节数,故无法超过参数buf 所指的缓冲大小。
●flags 可选项参数,若没有则传入0。
●from 存有发送端地址信息的sockaddr 结构体变量的地址值。
●addrlen 保存参数from 的结构体变量长度的变量地址值。
编写UDP 程序时最核心的部分就在于上述两个函数,这也说明二者在UDP 数据传输中
的地位。
注意:UDP是DDOS攻击的一个最主要的形式,因为他是无法拒收的。
五、UDP服务端(下)
int lession73(int argc, char* argv[])
{printf("server start");system("echo 'server start' > /output.txt");int ser_sock = -1;char message[1024] = "";struct sockaddr_in servaddr, clientaddr;socklen_t clientlen = 0;if (argc != 2) {printf("usage:% <port>\n", argv[0]);handle_error("argement is error:");}ser_sock = socket(PF_INET, SOCK_DGRAM, 0); //UDPif (ser_sock == -1) {handle_error("create socket failed:");}servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //127.0.0.1servaddr.sin_port = htons((short)atoi(argv[1]));if (bind(ser_sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {handle_error("bind failed:");}for (int i = 0; i < 50; i++) {system("echo 'server start 0' > /output.txt");clientlen = sizeof(clientaddr);ssize_t len = recvfrom(ser_sock, message, sizeof(message), 0, (struct sockaddr*)&clientaddr, &clientlen);system("echo 'server start 2' > /output.txt");sendto(ser_sock, message, len, 0, (struct sockaddr*)&clientaddr, clientlen);}printf("server ok");close(ser_sock);return 0;
}
六、UDP客户端
int lession74(int argc, char* argv[]) {int client_sock;struct sockaddr_in serv_addr;socklen_t serv_len = sizeof(serv_addr);char massege[1024] = "";//校验参数if (argc != 3) {printf("usge:%s ip port\n", argv[0]);handle_error("argement error!");}client_sock = socket(AF_INET, SOCK_DGRAM, 0);if (client_sock == -1) {handle_error("soket create failed!");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port = htons((short)atoi(argv[2]));while (1) {printf("input massege(Q to quit):");scanf("%s", massege);if ((strcmp(massege, "Q") == 0) || (strcmp(massege, "q") == 0)) {break;}printf("Sending message: '%s' with length: %zu\n", massege, strlen(massege));//printf("Sending message: '%s' with length: %zu\n", massege, strlen(massege));printf("debug:%s", massege);ssize_t len=sendto(client_sock, massege, strlen(massege), 0, (sockaddr*)&serv_addr, serv_len);memset(massege, 0, (unsigned int)len);recvfrom(client_sock, massege, sizeof(massege), 0, (sockaddr*)&serv_addr, &serv_len);printf("recv:%s\n", massege);}close(client_sock);return 0;}
七、UDP的传输特性和调用
前面讲解了UDP 服务器端/客户端的实现方法。但如果仔细观察UDP 客户端会发现,它
缺少把IP 和端口分配给套接字的过程。TCP 客户端调用connect 函数自动完成此过程,而
UDP 中连能承担相同功能的函数调用语句都没有。究竟在何时分配IP 和端口号呢?
UDP 程序中,调用sendto 函数传输数据前应完成对套接字的地址分配工作,因此调用bind
函数。当然,bind 函数在TCP 程序中出现过,但bind 函数不区分TCP 和UDP,也就是说,
在UDP 程序中同样可以调用。另外,如果调用sendto 函数时发现尚未分配地址信息,则在
首次调用sendto 函数时给相应套接字自动分配IP 和端口。而且此时分配的地址一直保留到
程序结束为止,因此也可用来与其他UDP 套接字进行数据交换。当然,IP 用主机IP,端口
号选尚未使用的任意端口号。
综上所述,调用sendto 函数时自动分配IP 和端口号,因此,UDP 客户端中通常无需额
外的地址分配过程。
八、SO_REUSEADDR
void client78() {int client = socket(PF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr)); //清零 防止意外servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");servaddr.sin_port = htons(33005);int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));while (ret == 0) {printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);char buffer[256] = "";fputs("input message(Q to quit):", stdout);//输出提示符fgets(buffer, sizeof(buffer), stdin/*标准输入流*/);//读入一行if ((strcmp(buffer, "Q\n") == 0) || (strcmp(buffer, "q\n") == 0)) {break;}size_t len = strlen(buffer);size_t send_len = 0;while (send_len < len) {ssize_t ret = write(client, buffer + send_len, len - send_len);//发给服务器if (ret <= 0){fputs("write failed!\n", stdout);//close(client);std::cout << "client done!" << std::endl;return;}send_len += (size_t)ret;}memset(buffer, 0, sizeof(buffer));size_t read_len = 0;while (read_len < len){ssize_t ret = read(client, buffer + read_len, len - read_len);if (ret <= 0){fputs("read failed!\n", stdout);//close(client);std::cout << "client done!" << std::endl;return;}send_len += (size_t)ret;}std::cout << "from server:" << buffer;}//close(client);std::cout << "client done!" << std::endl;
}
void server78() {printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);int sock,client,optval=0;struct sockaddr_in addr,cli;socklen_t addrlen=sizeof(addr);char message[256] = "";sock = socket(PF_INET, SOCK_STREAM, 0);getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);printf("SO_REUSEADDR=%d\n", optval);optval = 1;setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);printf("SO_REUSEADDR=%d\n", optval);memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(33005);addrlen = sizeof(addr);if (bind(sock, (struct sockaddr*)&addr, addrlen) == -1) {handle_error("bind failed!");}listen(sock, 3);client = accept(sock, (struct sockaddr*)&cli, &addrlen);read(client, message, sizeof(message));close(client);close(sock);return;
}
void lession78(char*option){if (strcmp(option, "1") == 0) {//服务器server78();server78();server78();server78();server78();server78();server78();}else {//客户端client78();}}
九、TCP_NODELAY
"什么是Nagle 算法?使用该算法能够获得哪些数据通信特性?"
Nagle 算法是以他的发明人John Nagle 的名字命名的,它用于自动连接许多的小缓冲
器消息;这一过程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效率。
从上图中可以得到如下结论:
"只有收到前一数据的ACK 消息时,Nagle 算法才发送下一数据。"
TCP 套接字默认使用Nagle 算法交换数据,因此最大限度地进行缓冲,直到收到ACK。
上图左侧正是这种情况。为了发送字符串"Nagle",将其传递到输出缓冲。这时头字符"N"之
前没有其他数据(没有需接收的ACK),因此立即传输。之后开始等待字符"N"的ACK 消息,
等待过程中,剩下的"agle"填入输出缓冲。接下来,收到字符"N"的ACK 消息后,将输出缓冲
的"agle"装入一个数据包发送。也就是说,共需传递4 个数据包以传输1 个字符串。
接下来分析未使用Nagle 算法时发送字符串"Nagle"的过程。假设字符"N"到"e"依序传到
输出缓冲。此时的发送过程与ACK 接收与否无关,因此数据到达输出缓冲后将立即被发送
出去。从上图右侧可以看到,发送字符串"Nagle"时共需10 个数据包。由此可知,不使用Nagle
算法将对网络流量产生负面影响。即使只传输1 个字节的数据,其头信息都有可能是几十个
字节。因此,为了提高网络传输效率,必须使用Nagle 算法。
在程序中将字符串传给输出缓冲时并不是逐字传递的,故发送字符串"Nagle"的实际情
况并非如上图所示。但如果隔一段时间再把构成字符串的字符传到输出缓冲(如果存在此
类数据传递)的话,则有可能产生类似上图的情况。上图中就是隔一段时间向输出缓冲传递
待发送数据的。
但Nagle 算法并不是什么时候都适用。根据传输数据的特性,网络流量未受太大影响时,
不使用Nagle 算法要比使用它时传输速度快。最典型的是"传输大文件数据"。将文件数据传
入输出缓冲不会花太多时间,因此,即便不使用Nagle 算法,也会在装满输出缓冲时传输数
据包。这不仅不会增加数据包的数量,反而会在无需等待ACK 的前提下连续传输,因此可
以大大提高传输速度。
一般情况下,不适用Nagle 算法可以提高传输速度。但如果无条件放弃使用Nagle 算法,
就会增加过多的网络流量,反而会影响传输。因此,未准确判断数据特性时不应禁用Nagle
算法。
刚才说过的"大文件数据"应禁用Nagle 算法。换言之,如果有必要,就应禁用Nagle 算
法。"Nagle 算法使用与否在网络流量上差别不大,使用Nagle 算法的传输速度更慢"禁用方
法非常简单。
十、IO缓存大小
我们进行套接字编程时往往只关注数据通信,而忽略了套接字具有的不同特性。但是,理解
这些特性并根据实际需要进行更改也十分重要。
从上表可以看出,套接字可选项是分层的。IPPROTOIP 层可选项是IP 协议相关事项,
IPPROTO_TCP 层可选项是TCP 协议相关的事项,SOL_SOCKET 层是套接字相关的通用可选项。
也许有人看到表格会产生畏惧感,但我们真的无需全部背下来或理解,因此不必有负担。实
际能够设置的可选项数量是上表的好几倍,也无需一下子理解所有可选项,实际开发中逐一
掌握即可。接触的可选项多了,自然会掌握大部分重要的。
getsockopt & setsockopt
我们几乎可以针对上表中的所有可选项进行读取(Get)和设置(Set)(当然,有些可选项
只能进行一种操作)。可选项的读取和设置通过如下2 个函数完成。
#include<sys/socket.h>
int getsockopt(int sock, int level,int optname, void *optval, socklen_t *optlen);
→成功时返回0,失败时返回-1。
●sock :用于查看选项套接字文件描述符。
●level 要查看的可选项的协议层。
●optname 要查看的可选项名。
●optval 保存查看结果的缓冲地址值。
●optlen 向第四个参数optval 传递的缓冲大小。调用函数后,该变量中保存通过第四个参数
返回的可选项信息的字节数。
#include<sys/socket.h>
int setsockopt(int sock, int level, int optname, const void*optval, socklen_t optlen);
→成功时返回0,失败时返回-1。
●sock 用于更改可选项的套接字文件描述符。
●level 要更改的可选项协议层。
●optname 要更改的可选项名。
●optval 保存要更改的选项信息的缓冲地址值。
●optlen 向第四个参数optval 传递的可选项信息的字节数。
相关文章:

# issue 8 TCP内部原理和UDP编程
TCP 通信三大步骤: 1 三次握手建立连接; 2 开始通信,进行数据交换; 3 四次挥手断开连接; 一、TCP内部原理--三次握手 【第一次握手】套接字A∶"你好,套接字B。我这儿有数据要传给你,建立连接吧。" 【第二次…...

力扣100题--移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […...

Spring 邮件发送
Spring 邮件发送 1. 主要内容(了解) 2. JavaMail 概述(了解) JavaMail,顾名思义,提供给开发者处理电⼦邮件相关的编程接⼝。JavaMail 是由 Sun 定义的⼀套收发电⼦邮件的 API,它可以⽅便地执⾏⼀…...

利用 360 安全卫士极速版关闭电脑开机自启动软件教程
在使用电脑的过程中,过多的开机自启动软件会严重拖慢电脑的开机速度,影响我们的使用体验。本教程中简鹿办公将详细介绍如何使用 360 安全卫士极速版关闭电脑开机自启动软件,让您的电脑开机更加迅速流畅。 一、打开 360 安全卫士极速版 在电…...

楼房销售系统
文末获取源码和万字论文,制作不易,感谢点赞支持。 毕 业 设 计(论 文) 题目:楼房销售系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储…...

UML箭线图的理解和实践
在软件开发的世界里,UML(统一建模语言)作为一种标准化的建模语言,扮演着举足轻重的角色。UML类图更是软件开发设计和架构过程中的核心工具,它不仅能帮助开发者明确系统中的类及其关系,还能为后续的代码实现…...

Qt入门8——Qt文件
1. Qt文件概述 文件操作是应用程序必不可少的部分。Qt作为⼀个通用开发库,提供了跨平台的文件操作能力。Qt 提供了很多关于文件的类,通过这些类能够对文件系统进行操作,如文件读写、文件信息获取、文件复制或重命名等。 2. 输入输出设备类 在…...

鸿翼受邀出席2024海峡两岸档案暨缩微学术交流会
近日,由中国档案学会、中国文献影像技术协会共同主办,中华档案暨资讯微缩管理学会参加的2024年海峡两岸档案暨缩微学术交流会在乌鲁木齐召开。鸿翼联合创始人兼CTO罗永秀受邀出席本次交流会并作主题分享。 自1992年以来,该学术交流会已连续举…...

支持win7系统的onnxruntime
在win7 X86系统上,使用了onnxruntime.dll库做AI识别,但是在win7上运行报0xc0000005的错误 经查,ONNX Runtime从v1.15.0版本开始不再支持Windows 7及其之前的操作系统,即便尝试重新编译源代码亦无法在这些老系统上运行,…...

如何利用内链策略提升网站的整体权重?
内链是谷歌SEO中常常被低估的部分,实际上,合理的内链策略不仅能帮助提升页面间的关联性,还可以增强网站的整体权重。通过正确的内链布局,用户可以更流畅地浏览你的网站,谷歌爬虫也能更快地抓取到更多页面,有…...

鸿蒙分享(二):引入zrouter路由跳转+封装
码仓库:https://gitee.com/linguanzhong/share_harmonyos 鸿蒙api:12 鸿蒙第三方库地址:OpenHarmony三方库中心仓 zrouter地址:OpenHarmony三方库中心仓 1.引入zrouter 1.打开终端界面:输入 ohpm install hzw/zrouter 2.在项目…...

【计算机网络】实验11:边界网关协议BGP
实验11 边界网关协议BGP 一、实验目的 本次实验旨在验证边界网关协议(BGP)的实际作用,并深入学习在路由器上配置和使用BGP协议的方法。通过实验,我将探索BGP在不同自治系统之间的路由选择和信息交换的功能,理解其在互…...

leetcode 1853 转换日期格式(postgresql)
需求 表: Days ----------------- | Column Name | Type | ----------------- | day | date | ----------------- day 是这个表的主键。 给定一个Days表,请你编写SQL查询语句,将Days表中的每一个日期转化为"day_name, month_name day, year"…...

掌握时间,从`datetime`开始
文章目录 掌握时间,从datetime开始第一部分:背景介绍第二部分:datetime库是什么?第三部分:如何安装这个库?第四部分:简单库函数使用方法1. 获取当前日期和时间2. 创建特定的日期3. 计算两个日期…...

剖析千益畅行,共享旅游-卡,合规运营与技术赋能双驱下的旅游新篇
在数字化浪潮席卷各行各业的当下,旅游产业与共享经济模式深度融合,催生出旅游卡这类新兴产品。然而,市场乱象丛生,诸多打着 “共享” 幌子的旅游卡弊病百出,让从业者与消费者都深陷困扰。今天,咱们聚焦技术…...

集合框架(2)List
Collection的子接口:List、Set 1、List接口 鉴于Java中数组用来存储数据的局限性,我们通常使用java.util.List替代数组List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。JDK API中List接口的实现类常用的有ÿ…...

【子查询】.NET开源 ORM 框架 SqlSugar 系列
.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...

西安理工大学丨ChatGPT助力学术论文写作训练营-助力发表SCI一区、二区
在当今学术研究中,科研人员在撰写论文时面临诸多挑战。首先是信息量的剧增,科研人员需要快速消化新知识,筛选相关信息并清晰表达。但论文写作不仅是信息的罗列,还需要条理清晰、逻辑严密、语言精准,特别是在竞争激烈的…...

go get依赖包失败,502 Bad gateway
问题描述 go get 依赖包失败,502 Bad gateway 解决办法 # 临时 export GOPROXY"https://goproxy.cn" go get -u xxxx # 或者直接永久生效 go env -w GOPROXY"https://goproxy.cn"...

71、docker镜像制作上传/下载到阿里云
基本思想:简单学习一下如何制作镜像和上传下载到私有阿里云,然后构建一个gpu的训练/推理环境,以备后续使用 一、配置环境 ubuntu@ubuntu:~$ sudo apt-get install docker.ioubuntu@ubuntu:~$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS P…...

ZLMediaKit+wvp (ffmpeg+obs)推拉流测试
这里使用了两种方式: ffmpeg命令和 OBS OBS推流在网上找了些基本没有说明白的, 在ZLMediaKit的issues中看到了一个好大哥的提问在此记录一下 使用OBS推流,rtmp,报鉴权失败 推流 1. ffmpeg命令推流 官方说明文档地址: 推流规则 rtsp://192.168.1.4:10554…...

POSTGRESQL跟ORACLE语法区别和相同之处
跟ORACLE语法区别之处 1. Update和delete语法区别 Pg 和MySQL Update和delete的时候表名不能加别名 2. 插入数字类型不一样 ORACLE 对number类型的数据可以用’’ 字符串标记插入,但是PG不行,必须要进行正确的数据类型 3. SEQ使用不同 ORACEL的SEQ…...

【知识点】图与图论入门
何为图论 见名知意,图论 (Graph Theory) 就是研究 图 (Graph) 的数学理论和方法。图是一种抽象的数据结构,由 节点 (Node) 和 连接这些节点的 边 (Edge) 组成。图论在计算机科学、网络分析、物流、社会网络分析等领域有广泛的应用。 如下,这…...

FPGA系列,文章目录
前言 FPGA(Field-Programmable Gate Array,现场可编程门阵列)是一种集成电路,其内部结构可以通过软件重新配置来实现不同的逻辑功能。与传统的ASIC(Application-Specific Integrated Circuit,专用集成电路…...

PAT乙级1003我要通过的做题笔记
分析题意 得到“答案正确”的条件是: 字符串中必须仅有 P、 A、 T这三种字符,不可以包含其它字符; 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串࿱…...

【React】React常用开发工具
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、React DevTools二、Redux DevTools三、Create React App 前言 React 是一种用于构建用户界面的流行 JavaScript 库,由于其灵活性、性能和可重用…...

Ubuntu20.04编译安装Carla全过程
前言 Carla的安装是我现阶段解决的第一个问题,现记录一下我安装Carla的过程以及我在安装过程中遇到的一些问题。 一、安装前准备 1、硬件环境 carla是一款基于UE4开发的模拟仿真软件,本身对硬件的要求比较高。 我是windows与ubuntu双系统࿰…...

Dijkstra 算法 是什么?
Dijkstra 算法 Dijkstra 算法是一种经典的最短路径算法,用于在图(有向或无向图)中找到从起点到其他所有节点的最短路径。它以广度优先搜索的方式,逐步扩展到目标节点,确保计算出的路径是最短的。 1. Dijkstra 算法的基…...

英文输入法---华为OD机试2024年E卷
题解: 代码:...

理解 package.json 中版本号符号
今天,聊一聊在前端开发中, package.json 中怎么看版本号符号。 版本号符号的解释 版本号通常由三部分组成:主版本号、次版本号、补丁版本号,格式为 major.minor.patch。常见的符号有: ^:更新时允许自动…...