【Linux】socket编程(一)

news2024/12/26 9:18:07

目录

预备知识

理解源ip地址和目的ip地址

认识端口号

理解"端口号"和"进程ID"

认识TCP和UDP协议

网络字节序

socket编程接口

socket常见API

socket

bind

listen 

accept

conncet 

sockaddr与sockaddr_in 

socket的使用(简易UDP网络程序的编写)

udp_server.hpp编写

udp_server.cc编写

udp_client.cc编写


预备知识

理解源ip地址和目的ip地址

        在ip层报头,有两个ip地址,分别叫源ip地址和目的ip地址,这相关的内容包括mac地址,我在前一章详细的说明了,可以点击这里,拉到最后一个模块就是:网络基础知识

        但是我们仔细想一下,我们只有ip地址就可以完成通信了吗?比如我们qq发送消息的例子,有了对方的ip地址可以发送对方的机器上,但是我们不是想给对方的机器识别,而是想给对方电脑上的qq程序,所以我们还需要一个标识(端口号)来区分,这个数据要给哪一个程序解析。

        所以网络通信过程,本质是还是进程间通信! 将数据在主机间转发仅仅是手段,机器收到之后,需要将数据交付给指定的进程。

        如何知道交给对应的哪一个进程呢,需要用端口号来区分。


认识端口号

        端口号(port)是在网络通信中使用的一个16位数字(0-65536),用于标识计算机或网络设备上运行的特定应用程序或服务在TCP/IP协议中,端口号用于将传输层的数据包分发到相应的应用程序或服务。

        有了端口号,我们就可以利用IP地址 + 端口号能够标识网络上的某一台主机的某一个进程。

        一个端口号只能被一个进程占用。而一个进程可以绑定多个端口号。

        通过将源IP地址、目标IP地址和端口号组合在一起,网络通信中的数据包可以正确地路由到目标应用程序或服务源端口号用于回复数据包时将数据包返回到正确的发送者源ip地址和目标IP地址与源端口号和目标端口号的组合构成了网络中唯一的终点,也被称为套接字(Socket)。


理解"端口号"和"进程ID"

        我们之前学习系统编程的时候,也知道用pid来表示唯一一个进程,而端口号也用来标识唯一一个进程,它们两个有什么关系呢?

        1.其实两个没什么区别,都是用来标识进程的唯一性,用进程pid来替代端口号也是完成 可以做到的,但是没这个必要。因为进程pid是属于进程这一范畴,而端口是属于网络范畴,我们如果将端口号改为使用pid,会使两者糅合在一在,造成代码的可维护性变差。而单独使用端口,便和进程完成了解耦,更便于我们以后的操作和维护。

        2.还有并不是以后进程一定需要端口号,但是每个进程都必须要有pid.


认识TCP和UDP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)和UDP(User Datagram Protocol 用户数据报协议).有一个直观的认识; 后面我们再详细讨论TCP和UDP的一些细节问题.

TCP有以下四个特点:

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

UDP有以下四个特点:

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

        这里注意,可靠传输和不可靠传输只是两个各自的特点,不存在好坏之说。TCP虽然保证了可靠传输,但是由于大量的复杂实现,导致传输速率不如UDP。在实时领域,如我们平常看的直播基本上采用的是UDP协议,速度较快,以提供较低的延迟和更好的实时性能。而TCP由于会有各种错误检测及机制,会使得延迟较高等弊处。


网络字节序

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

        比如A机器想给B机器发送一段数据,但A机器是小段存储,B机器是大端存储,当B读取A发送的数据时,将会得到错误的数据.因此为了避免这样的麻烦:

        TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节

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

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

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。

  • htonl:“Host to Network Long”,用于将 32 位(4 字节)的主机字节序整数转换为网络字节序(大端字节序)。它接受一个无符号长整型参数,并返回转换后的结果。

  • htons:“Host to Network Short”,用于将 16 位(2 字节)的主机字节序短整数转换为网络字节序(大端字节序)。它接受一个无符号短整型参数,并返回转换后的结果。

  • ntohl:“Network to Host Long”,用于将 32 位的网络字节序整数转换为主机字节序。它接受一个无符号长整型参数,并返回转换后的结果。

  • ntohs:“Network to Host Short”,用于将 16 位的网络字节序短整数转换为主机字节序。它接受一个无符号短整型参数,并返回转换后的结果。

  • htonl 和 htons 函数用于将主机字节序的整数转换为网络字节序,以便发送给其他计算机。而 ntohl 和 ntohs 函数用于将接收到的网络字节序整数转换为主机字节序,以便在本地计算机上进行处理。


socket编程接口

socket常见API

  • socket

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
  • domain:指定套接字的协议族,如AF_INET(IPv4)或AF_INET6(IPv6)。
  • type:指定套接字的类型,如SOCK_STREAM(面向连接的流套接字)或SOCK_DGRAM(无连接的数据报套接字)。
  • protocol:指定使用的传输协议,通常可以设置为0以自动选择合适的协议。
  • 作用:创建一个套接字,将domain、type和protocol参数传递给socket()函数以指定套接字的特性,并返回一个套接字描述符,用于后续的操作。
  • bind

//绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
  • sockfd:要绑定的套接字描述符。
  • addr:指向要绑定的套接字地址结构体指针,可以是struct sockaddr、struct sockaddr_in或struct sockaddr_in6等类型的指针。
  • addrlen:指定addr结构体的大小。
  • 作用:将给定的套接字描述符和地址绑定在一起,使套接字可以通过特定的地址和端口号进行通信。
  • listen 

// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
  • socket:要设置为监听状态的套接字描述符。
  • backlog:指定等待连接队列的最大长度,用于限制同时可以等待处理的连接请求的数目。
  • 作用:将套接字设置为监听状态,开始接受客户端的连接请求。通过指定backlog参数,可以控制连接队列的长度。
  • accept

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len)
  • sockfd:监听 套接字描述符
  • addr:用于存储客户端的地址信息的结构体指针,可以是struct sockaddr、struct sockaddr_in或struct sockaddr_in6等类型的指针。
  • addrlen:指向一个整数变量,用于传递addr结构体的大小,并在接受连接后更新为实际的地址长度。
  • 作用:等待并接受客户端的连接请求,并返回一个新的套接字描述符,该描述符用于与客户端进行通信。同时,可以获取客户端的地址信息
  • conncet 

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
  • sockfd:要进行连接的套接字描述符
  • addr:要连接的目标地址的结构体指针,可以是struct sockaddr、struct sockaddr_in或struct sockaddr_in6等类型的指针。
  • addrlen:指定addr结构体的大小。
  • 作用:与另一个套接字建立连接,通常用于客户端连接服务器。通过指定目标地址和端口号,使套接字能够与目标进行通信。 

sockaddr与sockaddr_in 

上面我们好几个函数参数都包括了struct sockaddr结构体,那么这个结构体是个什么呢?

        sockaddr结构体用于表示套接字的地址信息,它是一个通用的地址结构体,在网络编程中经常使用。

该结构体内部如下:

struct sockaddr {
    sa_family_t sa_family; 		/* address family, AF_xxx */
    char sa_data[14];			/* 14 bytes of protocol address */
};

        sockaddr结构体中的主要字段是sa_family和sa_data.  sa_family表示地址的协议族,用来指定该地址的类型,常见的值包括AF_INET(IPv4)和AF_INET6(IPv6)sa_data是一个字节数组,用于存储地址和端口号信息的具体内容,具体内容的长度和格式依赖于协议族的不同。

        很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体。但是sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了。IPv4为了解决这个问题,于是便设计了sockaddr_in结构体,把port和ip分开来存储。


  sockaddr_in 是用于 IPv4 地址的特定地址结构体。它扩展了 sockaddr,并提供了 IPv4 地址(通过 sin_addr 字段存储)和端口号(通过 sin_port 字段存储)的字段。sockaddr_in 使用的是网络字节序(大端字节序)来存储这些值。

该结构体内部如下:

struct sockaddr_in {
    sa_family_t    sin_family;   // 地址族,AF_INET(IPv4)
    in_port_t      sin_port;     // 端口号
    struct in_addr sin_addr;     // IPv4地址
    unsigned char  sin_zero[8];  // 为了保持与sockaddr结构体的大小相同而填充的字节,一般不用处理
};

 里面还有一个结构体是struct in_addr,这个函数内部如下:

 /* Internet address. */
struct in_addr {
  uint32_t    s_addr;   /* address in network byte order */
};

 它内部其实就是一个4字节的整数,也就是说IP地址其实就是一个4字节整数,具体如何使用,我们在下一模块中讲解。

        所以当我们使用IPv4协议簇进行bind时,需要先创建一个struct sockaddr_in结构体类型,填写好内部相关的数据后,然后再传入参数,并将这个参数强转为struct sockaddr*。

       

        具体来说,在 bind() 函数内部,会检查传入的 struct sockaddr(或其子类型)的 sa_family 字段来判断地址类型。如果 sa_family 字段的值为 AF_INET,则表示使用的是 IPv4 地址;如果 sa_family 字段的值为 AF_INET6,则表示使用的是 IPv6 地址。

基于检查到的地址类型,函数会运行相应的实现方式来执行绑定操作

各结构体组成如下:

程序员不应操作sockaddr,sockaddr是给操作系统用的
程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。

也就是,程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数。 


socket的使用(简易UDP网络程序的编写)

        以上说了socket常见接口的用法,现在我们就来用代码实现一下,具体是如何使用的。

        我们要实现一个简易版的UDP服务,效果是当客户端向服务端发送消息时,服务端收到消息并将消息返回到客户端,客户端收到后,再输出出来.

首先我们需要三个文件,分别是udp_client.cc,udp_server.cc,udp_server.hpp.

udp_server.hpp编写

首先我们要编写udp_server.hpp,代表服务器的相关接口与操作。有三个成员变量:

1、ip地址_ip 2、端口号_port 3、创建的套接字_sock.

然后我们实现第一个成员函数,初始化:

initServer:

  1. 首先我们要利用socket函数来创建一个套接字.
_sock = socket(AF_INET,SOCK_DGRAM,0);

指定的协议是IPv4,面向数据报读取。

     2.然后我们要bind将ip和port绑定我们当前的进程。

        首先创建一个sockaddr_in结构体,然后利用bzero函数对结构体所有变量进行初始化,然后分别填入相关的字段:

        这里有一个需要说明的地方:我们平常看到的ip地址,例如"111,222.112.113" --> 点分十进制字符串风格的IP地址,是为了给用户看。

一共四个区域,每一个区域的取值范围为[0-255]:即1个字节代表一个区域、理论上,表示一个ip地址,4个区域即4个字节就够了,所以我们平常使用的时候,是先传输给网络,所以要将点分十进制风格的字符串的IP地址转化成4字节(如下代码中最后一行,s_addr就是一个四字节无符号整数)

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端口号

//1.首先要将字符串风格的IP地址转化成 4字节IP
//2.4字节主机序列 -> 网络序列
//有一套接口,这里使用inet_addr可一次帮我们完成这两件事情
local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());//sin_addr结构体中有一个成员变量s_addr,代表4字节表示的ip地址。如果地址为空,则将其设置为INADDR_ANY,代表inet_addr("0.0.0.0")服务器
if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
      logMessage(FATAL, "%d:%s", errno, strerror(errno));
      exit(2);
}

第二个成员函数:start()

        作为一款网络服务器,它是永远不退出的,我们想要的效果是,客户端clinet给服务端server发送一个消息,然后服务端收到后再原路返回。

        这里还需要用到recvfrom函数,该函数用来从指定的套接字中接收数据,并将数据存放在指定的缓冲区中,该函数原型如下:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
  • sockfd:套接字描述符,用于标识要接收数据的套接字。
  • buf:指向接收数据的缓冲区。
  • len:缓冲区的大小,表示能够接收的最大数据量。
  • flags:可选的标志参数,可以为 0 或常量 MSG_DONTWAIT(非阻塞模式)等。
  • src_addr:指向用于存储发送方地址信息的 sockaddr 结构体的指针。
  • addrlen:指向保存发送方地址信息大小的变量的指针。

所以我们需要提前定义一个sockaddr_in结构体类型的指针来存储发送方的ip地址和端口号等信息。

recvfrom() 函数的返回值是实际接收到的数据的字节数,如果返回 -1,则表示出现错误。

所以代码如下:

 void Start()
    {
        // 作为一款网络服务器,永远不退出!
        // 服务器启动 -> 启动 -> 常驻进程 ->永远在内存中存在,除非异常挂掉
        // echo serverf :clinet发送消息,我们原封不动返回
        char buffer[BUFFER_SIZE];
        for (;;)
        {
            // 注意:
            // peer,纯输出型参数
            struct sockaddr_in peer;
            bzero(&peer, sizeof peer);

            // 输入:peer 缓冲区大小
            // 输出:实际读到的p eer大小
            socklen_t len = sizeof(peer);
            // 1.读取数据
            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (s > 0)
            {
                buffer[s] = 0;
                // 1.输出发送的数据信息
                // 2.是谁发送的信息
                uint16_t cli_port = ntohs(peer.sin_port); // 从网络中来的,需要转化成本机序列
                string cli_ip = inet_ntoa(peer.sin_addr); // 4字节的网络序列IP,需要我们转成本主机字符串风格的IP,方便显示
                printf("[%s:%d]# %s\n",cli_ip.c_str(), cli_port, buffer);
            }
            // 分析和处理数据

            // 2.写回数据
            sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
        }
    }

我们在获取到发送方信息的同时将其信息输出到屏幕上,然后将数据利用sendto返回给客户端。

sendto() 函数在网络编程中用于发送数据到指定的套接字。

该函数原型如下:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
  • sockfd:套接字描述符,用于标识要发送数据的套接字。
  • buf:指向要发送数据的缓冲区。
  • len:要发送的数据的大小。
  • flags:可选的标志参数,可以为 0 或常量 MSG_DONTWAIT(非阻塞模式)等。
  • dest_addr:指向包含目标地址信息的 sockaddr 结构体的指针。
  • addrlen:表示目标地址信息的大小。

sendto() 函数的返回值是发送数据的字节数,如果返回 -1,则表示出现错误.

至此,我们的udp_server.hpp便封装完成,后面只要使用这些接口就可以了,就很简单了。

udp_server.cc编写

我们想让用户输入的格式是./udp_server port,所以我们需要用到命令行参数.

我们首先获得到用户输入的端口号port,然后利用unique_ptr指针创建一个UdpServer对象,将port传进去,这样我们就得到了成功创建好了服务器的ip和端口.

代码如下:

static void usage(string proc)
{
    cout << "\nUsage: " << proc << " port" << endl;
}
//./udp_serber ip port
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        usage(argv[0]);
        exit(1);
    }
    // string ip = argv[1];
    uint16_t port = atoi(argv[1]);
    unique_ptr<UdpServer> svr(new UdpServer(port));

    svr->initServer();
    svr->Start();

    return 0;
}

udp_client.cc编写

同样地,我们需要用户输入连接的服务器的ip 和 port.

然后用户创建一个套接字,此时不需要我们显式bind,因为

client是一个客户端 -> 普通人下载安装启动使用->程序员如果自己bind,也一定bind了一个固定的ip和端口->万一其他客户端提前占用了该port,那这个客户端便无法启动、所以client一般不需要显式bind指定port,操作系统会自动随机选择.

然后提示用户输入信息,并发送到服务端,服务端将消息在返送回来,将返送回来的消息也输出出来.

代码如下:

static void usage(string proc)
{
    cout << "Usage: " << proc << "ServerIP ServerPort" << endl;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0)
    {
        cerr << "socket error" << endl;
        exit(2);
    }
    //cilent 要不要bind? 需要,但是client不会显式bind,程序员不会自己bind
    //client是一个客户端 -> 普通人下载安装启动使用->程序员如果自己bind,也一定bind了一个固定的ip和端口
    //->万一其他客户端提前占用了port,那这个客户端便无法启动
    //所以client一般不需要显式bind指定port,操作系统会自动随机选择
    string message;
    struct sockaddr_in server;
    bzero(&server,sizeof server);
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);

    char buffer[1024];
    while(true)
    {
        cout << "please Enter Meassage# ";
        getline(cin,message);
        if(message == "quit")
        {
            break;
        }
        //当cilent首次发送消息给服务器的时候,OS会自动给client bind它的ip和port
        sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof server);

        struct sockaddr_in temp;
        bzero(&temp,sizeof temp);
        socklen_t len = sizeof(temp);
        ssize_t s = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);
        if(s > 0)
        {
            buffer[s] = 0;
            //将返送回来的消息也输出出来
            cout << "server echo# " << buffer << endl;
        }
    }
    return 0;
}

整体效果如下:

 整体所有的代码可以私信我要哦,gitee原来的账号无法使用了,没办法上传到gitee. 

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

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

相关文章

17款奔驰S400升级原厂无钥匙进入系统,提升您的便利性

奔驰无钥匙进入功能&#xff0c;只要身上装着车钥匙进入车内&#xff0c;车辆就能感应到钥匙的存在&#xff0c;这时只需按下启动键就可启动车辆了 奔驰无钥匙进入功能主要有两大使用体验&#xff0c;首先就是要注意主驾驶位车门的有效检测距离不小于1.5m&#xff0c;其他门钥匙…

2022年09月 C/C++(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;课程冲突 小 A 修了 n 门课程, 第 i 门课程是从第 ai 天一直上到第 bi 天。 定义两门课程的冲突程度为 : 有几天是这两门课程都要上的。 例如 a11&#xff0c;b13&#xff0c;a22&#xff0c;b24 时, 这两门课的冲突程度为 2。 现在你需要求的是这 n 门课中冲…

vue 中 axios 的安装及使用

vue 中 axios 的安装及使用 1. axios 安装2. axios使用 1. axios 安装 首先&#xff0c;打开当前的项目终端&#xff0c;输入 npm install axios --save-dev验证是否安装成功&#xff0c;检查项目根目录下的 package.json,其中的 devDependencies 里面会多出一个axios及其版本…

【中国善网】资源对接(场地捐赠)公示

-----仰和百花心理咨询认领安利公益基金会捐赠场地 近日&#xff0c;经过中国善网与安利公益基金会的沟通与对接&#xff0c;仰和百花心理咨询获得免费认领安利公益基金会场地&#xff08;安利深圳体验馆&#xff09;8月22-25日为期4天的使用权&#xff0c;特此公示。为推动公…

(嵌入式c语言)类型修饰符

类型修饰符 对内存资源存储位置的限定 auto 默认的类型修饰符 修饰的变量可读可写 register 因为你内部寄存器比较少&#xff0c;使用此类型修饰符&#xff0c;会告诉编译器尽量把此数据放到寄存器。 CPU内部寄存器是编号来定义&#xff0c;无地址编号&#xff0c;所以r…

网络控价方法论

品牌为什么要做控价&#xff1f; 不做控价的品牌&#xff0c;会面对价值受损、经销商流失、口碑下降的局面&#xff0c;因为低价不是一家店铺的行为&#xff0c;会随着时间的推移&#xff0c;不断蔓延&#xff0c;当越来越多的店铺低价&#xff0c;则表示渠道越来越乱&#xf…

亚马逊或将更改付款期限?卖家如何有效应对?

据外媒报道&#xff0c;亚马逊将更改其付款期限&#xff0c;英国和欧洲的卖家必须等到商品交付一周后才能收到款项。这项变更将于9月1日至6日分阶段生效&#xff0c;目前亚马逊已经向欧洲和英国的卖家发出通知。 一、亚马逊付款期限调整有何影响&#xff1f; 据了解&#xff…

【Python原创设计】基于Python Flask的上海美食信息与可视化宣传网站项目-附下载方式以及往届优秀论文,原创项目其他均为抄袭

基于Python Flask的上海美食信息与可视化宣传网站&#xff08;获取方式访问文末官网&#xff09; 一、项目简介二、开发环境三、项目技术四、功能结构五、运行截图六、功能实现七、数据库设计八、源码获取 一、项目简介 随着大数据和人工智能技术的迅速发展&#xff0c;我们设…

【React基础全篇】

文章目录 一、关于 React二、脚手架2.1 create-react-app 脚手架的使用2.2 项目目录解析2.3 抽离配置文件2.4 webpack 二次封装2.4.1 集成 css 预处理器2.4.2 配置解析别名 2.5 setupProxy 代理 三、JSX3.1 jsx 语法详解3.2 React.createElement 四、组件定义4.1 类组件4.2 函数…

日常中msvcp140.dll丢失是什么原因,msvcp140.dll丢失4个解决方法

在计算机编程过程中&#xff0c;我们可能会遇到各种错误和问题。其中&#xff0c;丢失msvcp140.dll文件是一个常见的问题。msvcp140.dll是Microsoft Visual C 2015 Redistributable的一部分&#xff0c;它包含了运行许多应用程序所需的运行时库。当这个文件丢失时&#xff0c;可…

【Python原创设计】基于Python Flask的全国气象数据采集及可视化系统-附下载方式以及项目参考论文,原创项目其他均为抄袭

基于Python Flask的全国气象数据采集及可视化系统 一、项目简介二、项目技术三、项目功能四、运行截图五、分类说明六、实现代码七、数据库结构八、源码下载 一、项目简介 本项目是一个基于Web技术的实时气象数据可视化系统。通过爬取中国天气网的各个城市气象数据&#xff0c…

【Go语言】基于Socket编程的P2P通信程序示例

Go语言的Socket编程实现为开发者提供了一种高效且强大的方式来实现网络通信。通过Go语言的并发模型和内置的网络库&#xff0c;如net包&#xff0c;开发者可以轻松地创建基于套接字的通信应用。Go语言的goroutine和channel机制使并发处理变得简单&#xff0c;能够轻松处理多个连…

Address already in use: bind 如何解决端口号被占用

Address already in use: bind 程序报错&#xff0c;说明端口号已经被占用了。在不重启计算机的情况下&#xff0c;可通过如下方式解决 一&#xff1a; winR 快捷键 输入cmd指令打开黑框 二&#xff1a;输入指令 netstat -ano 查看端口号 三&#xff1a;根据端口号&#xff0c…

色差分量接口ESD静电保护推荐TVS二极管:DW03DLC-B-S和DW05R-E

YCbCr/YPbPr色差分量接口是S-Video端子的升级产品&#xff0c;支持1080P高清&#xff0c;由红、绿、蓝三种颜色的线组成&#xff0c;其中&#xff0c;蓝色和红色分别传输蓝色差信号&#xff08;Cb/Pb&#xff09;和红色差信号&#xff08;Cr/Pr&#xff09;&#xff0c;而绿色传…

【vue3+ts项目】配置husky+配置commitlint

上一篇文章中配置了eslint校验代码工具 【vue3ts项目】配置eslint校验代码工具&#xff0c;eslintprettierstylelint 1、配置husky 每次手动执行命令才能格式化代码&#xff0c;如果有人没有格式化就提交到远程仓库&#xff0c;这个规范就起不到作用了&#xff0c;所有需要强…

【C++初阶】模拟实现vector

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

【测试】pywinauto的简单使用(安装、常用对象、元素控件、鼠标操作、键盘操作)

1.说明 pywinauto是一个用于自动化Python 模块&#xff0c;适合Windows系统的软件&#xff08;GUI&#xff09;&#xff0c;可以通过Pywinauto遍历窗口&#xff08;对话框&#xff09;和窗口里的控件&#xff0c;也可以控制鼠标和键盘输入&#xff0c;所以它能做的事情比之前介…

鲁图中大许少辉博士八一新书《乡村振兴战略下传统村落文化旅游设计》山东省图书馆典藏

鲁图中大许少辉博士八一新书《乡村振兴战略下传统村落文化旅游设计》山东省图书馆典藏

数据宽度、KEIL汇编和GNU汇编的区别

数据宽度 DCB data control byte(byte) DCW data control half word(short) DCD data control word(int) DCQ data control (long)KEIL汇编和GNU汇编的区别 IDA关于please position the cursor within a funtion的解决 我是在C/C++反编译中出现的问题,因为在动态调试的时候…

ctfshow-web11

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up 这里要求输入的passwd和session必需要相当&#xff0c;那么我们只要让密码和session都为空即可。 以上