Socket编程(TCP/UDP详解)

前言:之前因为做项目和找实习没得空,计算机网络模块并没有写成博客,最近得闲了,把计算机网络模块博客补上。

目录

一,UDP编程

1)创建套接字

2)绑定端口号

3)发送与接收数据

4)UDP简单的发送数据和接收数据服务器

二,TCP编程

1)创建套接字

2)绑定端口号

3)使套接字进入监听状态

4)获取成功建立连接的的文件描述符和主机信息

5)发送与接收数据

6)连接其他主机

7)TCP简单的发送数据和接收数据服务器


scoket编程即套接字编程,是网络编程的基础,它允许两台或者多台计算机进行网络通信,这篇文章主要讲socket编程利用里面的TCP和UDP相关接口实现网络通信。

一,UDP编程

在udp编程里面,我们首先要创建一个套接字,也就是文件描述符。用来接收数据与发送数据,但注意,UDP为每一个套接字维护一个缓冲区,但是发送缓冲区是临时的、不可见的。这是为什么呢?UDP是面向无连接的,每次发送数据都是相对独立的,这允许我们可以使用临时的缓冲区,UCP数据发送完就不管任何事了,不会像TCP一样要确认对方收到,没收到还要进行重传等操作。不维护一个长久的缓冲区,也可以节省空间资源,使UDP变得轻量与高效。如果接收缓冲区设置成临时的那么数据到达后,如果应用程序没有及时读取可能出现丢失,那么如果一直等到读取完再销毁,一个套接字缓冲区可能接收很多主机的信息,可能接收缓冲区会频繁的创建,销毁,这会有很多不必要的开销。

1)创建套接字

第一个参数是网络通信协议,如IPV4或者IPV6等,具体参考下图

第二个参数是套接字的类型,使用什么方式通信,如数据报(UDP)或者字节流(TCP)等

返回值为-1代表创建失败,并设置错误码,大于0代表成功创建。

使用例子:

        //AF_INET代表IPV4协议格式,SOCK_DGRAM代表以UDP数据报形式发送,0代表选择IPV4和UDP的默认协议
        int fd=socket(AF_INET,SOCK_DGRAM,0);
        if(fd<0){
            cout<<"创建套接字失败"<<endl;
        }

2)绑定端口号

在我的上一篇文章,我们以及明白绑定端口号加上IP才能确定互联网内的唯一一台主机,客户端可以不绑定端口号,这样子操作系统就会随机分配端口号,但是服务端不能这样,不然其他人无法主动连接服务端,因为其他人根本无法发现它,需要被别人第一次主动发现需要绑定端口号。现在我们来学习绑定端口号的接口。

scokfd就是我们前面使用socket接口创建的文件描述符。我们重点介绍接下来第二个参数,第三个参数是第二个参数的长度。

addr是结构体强转后得到的,它可以由IPV4结构体格式强转得到,也可以由IPV6格式强转得到,socketaddr_in是IPV4协议,socketaddr_un是IPV6协议。可以看下图理解

struct socketaddr里面的内容

struct socketaddr_in里面的内容

上图struct in_addr里面的内容

具体初始化和使用例子:

//IPV4结构体
        struct sockaddr_in _addr; 
        //设置为IPV4协议
        _addr.sin_family=AF_INET;
        //端口号网络字节序
        _addr.sin_port=htons(PORT);
        //IP地址网络字节序,inet_addr函数将C风格字符串的IP地址形式转化成uint32_t的网络字节序类型
        _addr.sin_addr.s_addr=inet_addr(IP);
        //成功返回0,失败返回-1设置错误码,设置成功只能接收来自IP主机发送给PORT的信息
        int result=bind(fd,(struct sockaddr*)&_addr,(socklen_t)sizeof(_addr));
        if(result!=0){
            cout<<"绑定端口号失败"<<endl;
        }

3)发送与接收数据

发送数据,UDP协议使用的是sendto接口

socketfd就是套接字文件描述符,buf是发送的数据地址,len是发送数据的长度,flag是位图,使用|可以实现对发送的方法控制

  • 发送标志,可以是一个或多个标志的组合,用于修改 sendto 的行为。常见的标志包括:
    • MSG_CONFIRM:请求确认消息已被接收(某些实现可能不支持)。
    • MSG_DONTROUTE:避免路由,直接发送到本地接口。
    • MSG_DONTWAIT:非阻塞发送,如果操作会阻塞,则立即返回错误。
    • MSG_EOR:表示记录结束(对某些协议有意义)。
    • MSG_MORE:指示发送的数据是更大消息的一部分。

后面两个参数就不必多少,目标地址的信息和长度强转得来。最后成功返回发送数据的长度,失败返回-1,并设置错误码。

接收数据,UDP协议用的是recvfrom函数,

socketfd就是套接字文件描述符,buf是接收数据存放的地址,len是接收数据的最大长度,flag是位图,使用|可以实现对接收数据的方法控制

  • 接收标志,可以是一个或多个标志的组合,用于修改 recvfrom 的行为。常见的标志包括:
    • MSG_PEEK:查看数据而不从队列中删除它。
    • MSG_WAITALL:请求接收完整的消息(对于某些协议可能不适用)。
    • MSG_DONTWAIT:非阻塞接收,如果操作会阻塞,则立即返回错误。
    • MSG_TRUNC:即使数据被截断也继续接收(通常与 MSG_PEEK 一起使用)。
    • MSG_CTRUNC:如果控制消息被截断,则设置 msg_flags 的 MSG_CTRUNC 标志。

src_addr会返回发送数据的信息,如端口号,IP地址,addrlen是src_addr的长度。成功返回收到数据的长度,失败返回-1。

4)UDP简单的发送数据和接收数据服务器

中间可能有一个地方没讲清楚,bind函数不论接收,数据还是发送数据都最好设置,设置成功能接收你设置的主机发过来的特点端口号消息,sendto函数里面设置的是要发送给的人的IP和端口号。recvfrom函数里面的struct sockeaddr是接收消息的发送主机信息,方便你回信息和处理。

发送端

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       #include<unistd.h>
       #include <arpa/inet.h>
       #include <netinet/in.h>
       #include<iostream>
       using namespace std;

       #define PORT 8081
       //本地环回通信测试
       #define IP "127.0.0.1"
       int main(){
        //AF_INET代表IPV4协议格式,SOCK_DGRAM代表以UDP数据报形式发送,0代表选择IPV4和UDP的默认协议
        int fd=socket(AF_INET,SOCK_DGRAM,0);
        if(fd<0){
            cout<<"创建套接字失败"<<endl;
            return -1;
        }
        //不绑定端口号,操作系统随机分配
        char msg[13]="hello world!";
        struct sockaddr_in _send; 
        _send.sin_family=AF_INET;
        _send.sin_port=htons(8080);
        _send.sin_addr.s_addr=inet_addr(IP);
        //给地址为IP主机8080端口号发送消息
        int result=sendto(fd,(void*)msg,13,0,(struct sockaddr*)&_send,(socklen_t)sizeof(_send));
        if(result<0){
            cout<<"发送数据失败"<<endl;
            return -1;
        }
        close(fd);
        return 0;
       }

接收端

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       #include<unistd.h>
       #include <arpa/inet.h>
       #include <netinet/in.h>
       #include<iostream>
       using namespace std;
        #define PORT 8080
       int main(){
        //AF_INET代表IPV4协议格式,SOCK_DGRAM代表以UDP数据报形式发送,0代表选择IPV4和UDP的默认协议
        int fd=socket(AF_INET,SOCK_DGRAM,0);
        if(fd<0){
            cout<<"创建套接字失败"<<endl;
            return -1;
        }
        //IPV4结构体
        struct sockaddr_in _addr; 
        //设置为IPV4协议
        _addr.sin_family=AF_INET;
        //端口号网络字节序
        _addr.sin_port=htons(PORT);
        //接收所有主机的信息
        _addr.sin_addr.s_addr=INADDR_ANY;
        //成功返回0,失败返回-1设置错误码
        int result=bind(fd,(struct sockaddr*)&_addr,(socklen_t)sizeof(_addr));
        if(result!=0){
            cout<<"绑定端口号失败"<<endl;
            return -1;
        }
        char msg[20];
        struct sockaddr_in recv;
        //必须写,不能为空。
        socklen_t len=sizeof(recv);
        result=recvfrom(fd,(void*)msg,20,0,(struct sockaddr*)&recv,&len);
        if(result<0){
            cout<<"接收数据数据失败"<<endl;
            return -1;
        }
        for(int i=0;i<result;i++){
            cout<<msg[i];
        }
        close(fd);
        return 0;
       }

二,TCP编程

1)创建套接字

创建套接字,与UDP创建套接字相似,只要把SOCK_DGRAM改为SOCK_STREAM

        //SOCK_STREAM代表字节流,适用于TCP
        int fd=socket(AF_INET,SOCK_STREAM,0);
        if(fd<0){
            cout<<"创建套接字失败"<<endl;
            return -1;
        }

2)绑定端口号

绑定端口号与UDP没有差别,就是接收来自指定的主机的连接请求,UDP是没有连接,需要发送消息时指定目的地址的。暂时简单理解就行。

struct sockaddr_in _addr; 
        //设置为IPV4协议
        _addr.sin_family=AF_INET;
        //端口号网络字节序
        _addr.sin_port=htons(PORT);
        //IP地址网络字节序,inet_addr函数将C风格字符串的IP地址形式转化成uint32_t的网络字节序类型
        _addr.sin_addr.s_addr=inet_addr(IP);
        //成功返回0,失败返回-1设置错误码
        int result=bind(fd,(struct sockaddr*)&_addr,(socklen_t)sizeof(_addr));
        if(result!=0){
            cout<<"绑定端口号失败"<<endl;
            return -1;
        }

3)使套接字进入监听状态

在TCP编程里面,创建套接字后并不能直接使用,TCP套接字只用来接收来自其他主机的连接请求,UDP发送完数据就不管了,是无连接的,TCP是面向连接的,双方会建立一个连接,也就是会为两台主机间创建单独的文件描述符,并且进行管理,这个文件描述符只能用来双方通信,而UDP可以实现一个文件描述符也就是socket就向所有主机发送消息。只有将套接字变成监听状态才会接收来自其他主机的连接。

第一个参数无需多言,就是我们使用socket函数创建的套接字,backlog是允许同时与多少台主机建立连接,也就是同时创建多少个通信的文件描述符,成功返回0,失败返回-1,并设置错误码。

        //允许同时最大与三个主机建立连接
        result=listen(fd,3);
        if(result!=0){
            cout<<"套接字启动监听失败"<<endl;
            return -1;
        }

4)获取成功建立连接的的文件描述符和主机信息

套接字进入监听状态后,我们需要获得建立连接的文件描述符,这样基于文件描述符才能和建立连接的主机通信,我们使用accept函数获取建立连接的消息,一般使用一个while循环来获取得到的多个连接信息。

第一个参数是套接字,第二个参数是连接主机的信息,第三个是第二个参数的长度,方便区分类型。成功返回建立连接的文件描述符,失败返回-1,并设置错误码。

        while(1){
            //这里不对对方主机信息进行处理,设置为空
            int fd_net=accept(fd,NULL,NULL);
            if(fd_net==-1){
            cout<<"TCP连接失败"<<endl;
            return -1;
            }
            //进行处理,发送或者接收数据
        }

5)发送与接收数据

TCP可以使用UDP的sendto和recvfrom函数发送与接收数据,但一般不这么做,因为TCP以及建立连接了,每个连接文件描述符都只和一台主机通信,被唯一的四元组来标识的,这个四元组包括源IP地址、源端口号、目的IP地址和目的端口号。没必要使用这两个函数,这两个函数里面还需要包括目的主机地。一般使用send和write,read与recv。

flag常用标志

  1. MSG_DONTWAIT(或MSG_NONBLOCK)
    • 作用:允许非阻塞操作。如果套接字被设置为非阻塞模式,并且发送缓冲区已满,则send函数会立即返回,而不是阻塞等待缓冲区空间可用。
    • 返回值:在非阻塞模式下,如果发送缓冲区已满,send函数可能返回-1,并设置errnoEAGAINEWOULDBLOCK,表示资源暂时不可用。
  2. MSG_OOB(Out-of-Band Data)
    • 作用:发送带外数据。带外数据通常用于发送紧急数据,这些数据会被接收方优先处理。然而,并非所有协议都支持带外数据,且其使用方式可能因协议而异。
    • 限制MSG_OOB标志通常仅适用于流式套接字(如SOCK_STREAM),而不适用于数据报套接字(如SOCK_DGRAM)。
  3. MSG_DONTROUTE
    • 作用:勿将数据路由出本地网络。这个标志告诉系统不要通过网关或路由器发送数据,而只在本地网络上发送。然而,并非所有系统都支持这个标志,且其效果可能因系统而异。

成功返回发送数据大小,失败返回-1,设置错误码。

fd是文件描述符,也就是accept函数的返回值,buf被发送的数据,count是发送的大小。

flag常用标志

  1. MSG_PEEK
    • 作用:查看接收队列中的数据,但不从队列中移除它们。这允许调用者在不实际消耗数据的情况下检查是否有数据可读。
    • 使用场景:在需要多次读取同一份数据或检查数据是否到达时非常有用。
  2. MSG_WAITALL
    • 作用:阻塞调用,直到接收到指定长度的数据或连接关闭。然而,需要注意的是,并非所有系统都支持这个标志,且其行为可能因系统而异。
    • 使用场景:在需要确保接收到完整消息时非常有用,但应谨慎使用,因为它可能导致程序在数据不足时长时间阻塞。
  3. MSG_DONTWAIT(或MSG_NONBLOCK
    • 作用:在非阻塞模式下接收数据。如果当前没有数据可读,则立即返回,而不是阻塞等待。
    • 使用场景:在需要避免阻塞等待数据到达时非常有用,例如在非阻塞I/O或事件驱动的编程模型中。
  4. MSG_OOB
    • 作用:接收带外数据(Out-of-Band Data)。带外数据通常用于发送紧急数据,这些数据会被接收方优先处理。然而,并非所有协议都支持带外数据。
    • 使用场景:在需要处理紧急数据或优先级较高的消息时非常有用,但应确保所使用的协议支持带外数据。
  5. MSG_TRUNC
    • 作用:如果接收到的数据长度超过了缓冲区长度,则只返回缓冲区长度的数据,并截断多余的数据。然而,需要注意的是,并非所有系统都支持这个标志。
    • 使用场景:在需要限制接收数据的大小或处理不完整数据时可能有用。
  6. MSG_CTRUNC
    • 作用:类似于MSG_TRUNC,但用于控制信息的截断。如果接收到的控制信息长度超过了缓冲区长度,则只返回缓冲区长度的控制信息。
    • 使用场景:在处理带有控制信息的套接字时可能有用。
  7. MSG_ERRQUEUE
    • 作用:接收错误信息。如果接收到的数据包出现错误,则会将错误信息放入错误队列中,可以通过此标志来接收这些错误信息。
    • 使用场景:在需要处理套接字错误或诊断网络问题时非常有用。

fd是文件描述符,也就是accept函数的返回值,buf存放数据,count是接收数据的最大大小,防止越界。

6)连接其他主机

上面我们只说了如何被动连接其他主机,但我们该如何主动连接其他主机呢?使用connect函数我们主动连接其他主机,是需要设置协议和IP,端口号信息的。注意connect连接成功之后这个scokfd就被占用了,用来后续的通信,需要继续使用socket函数创建与多台主机建立连接。这是与accept函数不同的地方,accept函数是创建了新的文件描述符,sockfd还可以继续监听。

成功返回0,失败返回-1,其他这些前面都讲过,老生常谈了,无需多言。

7)TCP简单的发送数据和接收数据服务器

服务端

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       #include<unistd.h>
       #include <arpa/inet.h>
       #include <netinet/in.h>
       #include<iostream>
       using namespace std;
       #define PORT 8080
       #define IP "127.0.0.1"
       int main(){
        //SOCK_STREAM代表字节流,适用于TCP
        int fd=socket(AF_INET,SOCK_STREAM,0);
        if(fd<0){
            cout<<"创建套接字失败"<<endl;
            return -1;
        }
        //IPV4结构体
        struct sockaddr_in _addr; 
        //设置为IPV4协议
        _addr.sin_family=AF_INET;
        //端口号网络字节序
        _addr.sin_port=htons(PORT);
        //IP地址网络字节序,inet_addr函数将C风格字符串的IP地址形式转化成uint32_t的网络字节序类型
        _addr.sin_addr.s_addr=inet_addr(IP);
        //成功返回0,失败返回-1设置错误码
        int result=bind(fd,(struct sockaddr*)&_addr,(socklen_t)sizeof(_addr));
        if(result!=0){
            cout<<"绑定端口号失败"<<endl;
            return -1;
        }
        //允许同时最大与三个主机建立连接
        result=listen(fd,3);
        if(result!=0){
            cout<<"套接字启动监听失败"<<endl;
            return -1;
        }
        while(1){
            //这里不对对方主机信息进行处理,设置为空
            int fd_net=accept(fd,NULL,NULL);
            if(fd_net==-1){
            cout<<"TCP连接失败"<<endl;
            return -1;
            }
            char msg[13]="hello world!";
            //进行处理,发送或者接收数据
            result=send(fd_net,(void*)msg,13,0);
            close(fd);
        }
        return 0;
       }

客户端

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       #include<unistd.h>
       #include <arpa/inet.h>
       #include <netinet/in.h>
       #include<iostream>
       using namespace std;
       #define PORT 8080
       #define IP "127.0.0.1"
       int main(){
        //SOCK_STREAM代表字节流,适用于TCP
        int fd=socket(AF_INET,SOCK_STREAM,0);
        if(fd<0){
            cout<<"创建套接字失败"<<endl;
            return -1;
        }
        //IPV4结构体
        struct sockaddr_in _addr; 
        //设置为IPV4协议
        _addr.sin_family=AF_INET;
        //端口号网络字节序
        _addr.sin_port=htons(PORT);
        _addr.sin_addr.s_addr=inet_addr(IP);
        int result=connect(fd,(struct sockaddr*)&_addr,(socklen_t)sizeof(_addr));
        if(result==-1){
            cout<<"连接主机失败"<<endl;
            return -1;
        }
        char msg[20];
        result=recv(fd,msg,20,0);
        for(int i=0;i<result;i++){
            cout<<msg[i];
        }
        close(fd);
        return 0;
       }

创造不易,我为人人,人人为我,如果大家有所收获的话可以点赞加关注,下一篇文章将会着重讲TCP与UDP的特性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

求平均年龄

求平均年龄 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 班上有学生若干名&#xff0c;给出每名学生的年龄&#xff08;整数&#xff09;&#xff0c;求班上所有学生的平均年龄&#xff0c;保留到小数…

40分钟学 Go 语言高并发:GC原理与优化

GC原理与优化 一、GC基础知识概览 方面核心概念重要性优化目标GC算法三色标记法、并发GC⭐⭐⭐⭐⭐理解GC工作原理垃圾回收策略触发条件、回收步骤⭐⭐⭐⭐⭐掌握GC过程GC调优参数设置、性能监控⭐⭐⭐⭐优化GC效果内存管理内存分配、内存逃逸⭐⭐⭐⭐⭐减少内存压力 让我们…

论文笔记 SliceGPT: Compress Large Language Models By Deleting Rows And Columns

欲买桂花同载酒&#xff0c;终不似&#xff0c;少年游。 数学知识 秩&#xff1a; 矩阵中最大线性无关的行/列向量数。行秩与列秩相等。 线性无关&#xff1a;对于N个向量而言&#xff0c;如果任取一个向量 v \textbf{v} v&#xff0c;不能被剩下的N-1个向量通过线性组合的方式…

vscode的项目给gitlab上传

目录 一.创建gitlab帐号 二.在gitlab创建项目仓库 三.Windows电脑安装Git 四.vscode项目git上传 一.创建gitlab帐号 二.在gitlab创建项目仓库 图来自:Git-Gitlab中如何创建项目、创建Repository、以及如何删除项目_gitlab新建项目-CSDN博客&#xff09; 三.Windows电脑安…

电阻可靠性的内容

一、影响电阻可靠性的因素&#xff1a; 影响电阻可靠性的因素有温度系数、额定功率&#xff0c;最大工作电压、固有噪声和电压系数 &#xff08;一&#xff09;温度系数 电阻的温度系数表示当温度改变1摄氏度时&#xff0c;电阻阻值的相对变化&#xff0c;单位为ppm/℃.电阻温…

(计算机网络)期末

计算机网络概述 物理层 信源就是发送方 信宿就是接收方 串行通信--一次只发一个单位的数据&#xff08;串行输入&#xff09; 并行通信--一次可以传输多个单位的数据 光纤--利用光的反射进行传输 传输之前&#xff0c;要对信源进行一个编码&#xff0c;收到信息之后要进行一个…

【K230 CanMV】machine.FPIOA、Pin 与 GPIO 全解析

引言&#xff1a;在嵌入式开发领域&#xff0c;GPIO&#xff08;通用输入输出&#xff09;引脚的功能配置和复用能力对设备的灵活性和功能实现起到了至关重要的作用。FPIOA&#xff08;Field Programmable IO Array&#xff0c;现场可编程 IO 数组&#xff09;是现代嵌入式芯片…

Observability:如何在 Kubernetes pod 中轻松添加应用程序监控

作者&#xff1a;来自 Elastic Jack Shirazi•Sylvain Juge•Alexander Wert Elastic APM K8s Attacher 允许将 Elastic APM 应用程序代理&#xff08;例如 Elastic APM Java 代理&#xff09;自动安装到 Kubernetes 集群中运行的应用程序中。该机制使用变异 webhook&#xff0…

【QT入门到晋级】QT项目打生产环境包--(Linux和window)

前言 使用QTcreator完成正常编译后&#xff0c;在构建目录中有可执行程序生成&#xff0c;如果直接把可执行程序拷贝到干净的生产环境上是无法运行成功的&#xff0c;使用ldd&#xff08;查看程序依赖包&#xff09;会发现缺失很多QT的特性包&#xff0c;以及将介绍国产Linux桌…

Flutter:页面滚动

1、单一页面&#xff0c;没有列表没分页的&#xff0c;推荐使用&#xff1a;SingleChildScrollView() return Scaffold(backgroundColor: Color(0xffF6F6F6),body: SingleChildScrollView(child: _buildView()) );2、列表没分页&#xff0c;如购物车页&#xff0c;每个item之间…

Windsurf可以上传图片开发UI了

背景 曾经羡慕Cursor的“画图”开发功能&#xff0c;这不Windsurf安排上了。 Upload Images to Cascade Cascade now supports uploading images on premium models Ask Cascade to build or tweak UI from on image upload New keybindings Keybindings to navigate betwe…

单片机-- 松瀚sonix学习过程

硬件&#xff1a;松瀚sn8f5701sg、SN-LINK 3 Adapter模拟器、sn-link转接板 软件&#xff1a; keil-c51&#xff08;v9.60&#xff09;&#xff1a;建立工程&#xff0c;编辑&#xff0c;烧录程序 SN-Link_Driver for Keil C51_V3.00.005&#xff1a;安装sonix设备包和snlin…

CSAPP Cache Lab(缓存模拟器)

前言 理解高速缓存对 C 程序性能的影响&#xff0c;通过两部分实验达成&#xff1a;编写高速缓存模拟器&#xff1b;优化矩阵转置函数以减少高速缓存未命中次数。Part A一开始根本不知道要做什么&#xff0c;慢慢看官方文档&#xff0c;以及一些博客&#xff0c;和B站视频&…

⽂件操作详解

⽬录 一 文件操作的引入 1 为什么使⽤⽂件&#xff1f; 2 什么是⽂件&#xff1f; 3 文件分类&#xff08;1 从⽂件功能的⻆度来分类&#xff1a;程序⽂件/数据⽂件 2根据数据的组织形式&#xff1a;为⽂本⽂件/⼆进制⽂件&#xff09; 二 ⽂件的打开和关闭 1 …

如何构建一个可扩展、全球可访问的 GenAI 架构?

你有没有尝试过使用人工智能生成图像&#xff1f; 如果你尝试过&#xff0c;你就会知道&#xff0c;一张好的图像的关键在于一个详细具体的提示。 我不擅长这种详细的视觉提示&#xff0c;所以我依赖大型语言模型来生成详细的提示&#xff0c;然后使用这些提示来生成出色的图像…

Python知识点精汇:列表篇精汇!

目录 一、列表是什么 二、列表长什么样 三、列表的基本操作 &#xff08;1&#xff09;访问元素 &#xff08;2&#xff09;列表删除 &#xff08;3&#xff09;增加元素 &#xff08;4&#xff09;修改元素 四、结合一些函数的用法 &#xff08;1&#xff09;最大值、…

基于WEB的房屋出租管理系统设计

摘 要 在当今社会的蓬勃发展的现状下&#xff0c;网络与我们的生活息息相关。工作、生活、休闲我们都利用着网络带给我们 的便捷&#xff0c;网络的发展提供了很多工作机会&#xff0c;众多的人们在不同的城市寻找着合适的工作机会&#xff0c;在此的第一步就是寻 找一个合适自…

【算法day4】链表:应用拓展与快慢指针

题目引用 两两交换链表节点删除链表的倒数第n个节点链表相交环形链表 1.两两交换链表节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&am…

电商项目高级篇06-缓存

电商项目高级篇06-缓存 1、docker下启动redis2、项目整合redis3、redis改造三级分类业务 缓存 流程图&#xff1a; data cache.load(id);//从缓存加载数据 If(data null){ data db.load(id);//从数据库加载数据 cache.put(id,data);//保存到 cache 中 } return data;在我们…

osg、osgearth源码编译(二)

如果比较懒&#xff0c;也可以不看这篇文章&#xff0c;网上应该有很多编译好的库。也可以找我要。 本人还是建议学会编译&#xff0c;因为其他人电脑上编译好的&#xff0c;可能在你的电脑环境上&#xff0c;出现这样那样奇怪的问题&#xff0c;所以&#xff0c;最好还是自己能…