Linux网络编程(二-套接字)

目录

一、背景知识

1.1 端口号

1.2 网络字节序

1.3 地址转换函数 

二、Socket简介

三、套接字相关的函数 

3.1 socket()

3.2 bind()

3.3 connect()

3.4 listen()

3.5 accept() 

3.6 read()/recv()/recvfrom()

3.7 send()/sendto()

 3.8 close()

 四、UPD客服/服务端实验 


一、背景知识

1.1 端口号

端口号是访问服务器的标识,就好像是门牌号一样,客户端可以通过ip地址找到对应的服务器端,但是服务器端是有很多端口的,每个应用程序对应一个端口号,通过类似门牌号的端口号,客户端才能真正的访问到该服务器。为了对端口进行区分,将每个端口进行了编号,这就是端口号。

端口包括逻辑端口物理端口两种类型:

物理端口是用于连接物理设备之间的接口,如ADSL Modem、集线器、交换机、路由器上用于连接其他网络设备的接口,如RJ-45端口、SC端口等等 。

逻辑端口是指逻辑意义上用于区分服务的端口,比如用于浏览网页服务的80端口,用于FTP服务的21端口等。如TCP/IP协议中的服务端口,通过不同的逻辑端口来区分不同的服务。

注意事项:

  • 一个IP地址的端口通过16bit进行编号,最多可以有65536个端口 。
  • 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理。
  • IP地址+端口号能够标识网络上的某一台主机的某一进程。
  • 一个进程可以绑定多个端口号,一个端口号只能被一个进程占用。

1.2 网络字节序

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

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

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

#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);
uint32_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint32_t ntohs(uint16_t netshort);
  • h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回。
  • 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。 

1.3 地址转换函数 

使用bind()进行ip地址和端口绑定时,要创建sockaddr_in类型变量,把端口号和ip地址都填输入进sockaddr_in类型变量中,sockaddr_in中的成员变量sin_addr用于填入ip地址,其类型为struct in_addr,表示32位的IP地址,但是我们通常用点分十进制的字符串表示IP 地址(例如"127.0.0.1"),但我们在sockaddr_in中需要填入in_addr类型表示的IP地址,以下函数可以在字符串表示和in_addr类型表示之间转换;

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>//字符串表示转in_addr类型表示函数
int inet_aton(const char *cp, struct in_addr *inp); 
//in_addr只有一个成员变量s_addr其类型为in_addr_t
in_addr_t inet_addr(const char *cp);// in_addr类型表示转字符串表示函数
char *inet_ntoa(struct in_addr in);

二、Socket简介

套接字(socket)是一种通信机制,凭借这种机制,客户端<->服务器 模型的通信方式既可以在本地设备上进行,也可以跨网络进行。套接字的创建和使用与管道是有区别的,因为套接字明确地将客户端、服务器区分开来,而且套接字机制可以实现将多个客户连接到一个服务器。

在Socket中,它使用一个套接字来记录网络的一个连接,套接字是一个整数,就像我们操作文件一样,利用一个文件描述符,可以对它打开、读、写、关闭等操作,类似的,在网络中,我们也可以对Socket套接字进行这样的操作,比如开启一个网络的连接、读取连接主机发送来的数据、向连接的主机发送数据、终止连接等操作。它跟我们的文件描述符非常像,其实就是一个整数,套接字API最初是作为UNIX操作系统的一部分而开发的,所以套接字API与系统的其他I/O设备集成在一起。当应用程序要为网络通信而创建一个套接字(socket)时,操作系统就返回一个整数作为描述符(descriptor)来标识这个套接字。然后,应用程序以该描述符作为传递参数,通过调用Socket API接口的函数来完成某种操作(例如通过网络传送数据或接收输入的数据)。

三、套接字相关的函数 

套接字相关函数需要包含的头文件如下:

#include <sys/types.h>
#include <sys/socket.h>

3.1 socket()

int socket(int domain, int type, int protocol);

socket()函数用于创建一个socket描述符(socket descriptor),它唯一标识一个socket,这个socket描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket函数的三个参数分别为:

  • domain:参数domain表示该套接字使用的协议族,在Linux系统中支持多种协议族,对于TCP/IP协议来说,选择AF_INET就足以,当然如果你的IP协议的版本支持IPv6,那么可以选择AF_INET6,可选的协议族具体见:
    • AF_UNIX, AF_LOCAL: 本地通信

    • AF_INET : IPv4

    • AF_INET6 : IPv6

    • AF_IPX : IPX - Novell 协议

    • AF_NETLINK : 内核用户界面设备

    • AF_X25 : ITU-T X.25 / ISO-8208 协议

    • AF_AX25 : 业余无线电 AX.25 协议

    • AF_ATMPVC : 访问原始ATM PVC

    • AF_APPLETALK : AppleTalk

    • AF_PACKET : 底层数据包接口

    • AF_ALG : 内核加密API的AF_ALG接口

  • type:参数type指定了套接字使用的服务类型,可能的类型有以下几种:
    • SOCK_STREAM:提供可靠的(即能保证数据正确传送到对方)面向连接的Socket服务,多用于资料(如文件)传输,如TCP协议。

    • SOCK_DGRAM:是提供无保障的面向消息的Socket 服务,主要用于在网络上发广播信息,如UDP协议,提供无连接不可靠的数据报交付服务。

    • SOCK_SEQPACKET:为固定最大长度的数据报提供有序的,可靠的,基于双向连接的数据传输路径。

    • SOCK_RAW:表示原始套接字,它允许应用程序访问网络层的原始数据包,这个套接字用得比较少,暂时不用理会它。

    • SOCK_RDM:提供不保证排序的可靠数据报层。

  • protocol:参数protocol指定了套接字使用的协议,在IPv4中,只有TCP协议提供SOCK_STREAM这种可靠的服务,只有UDP协议提供SOCK_DGRAM服务,对于这两种协议,protocol的值均为0,因为当protocol为0时,会自动选择type类型对应的默认协议。
  • 返回值:当创建套接字成功的时候,该函数返回一个int类型的值,也就是socket描述符,该值大于等于0;而如果创建套接字失败时则返回-1。

3.2 bind()

int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

bind()函数用于将一个IP 地址和端口号与一个套接字进行绑定。许多时候内核会帮我们自动绑定一个IP地址与端口号,然而有时用户可能需要自己来完成这个绑定的过程,以满足实际应用的需要,最典型的情况是一个服务器进程需要绑定一个众所周知的地址和端口以等待客户来连接,作为服务器端,这一步绑定的操作是必要的,而作为客户端,则不是必要的,因为内核会帮我们自动选择合适的IP地址与端口号。

bind()函数并不是总是需要调用的,只有用户进程想与一个具体的地址或端口相关联的时候才需要调用这个函数。如果用户进程没有这个需要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择。

 参数

  • sockfd:sockfd是由socket()函数返回的套接字描述符。

  • my_addr:my_addr是一个指向套接字地址结构的指针。

  • addrlen:addrlen指定了以addr所指向的地址结构体的字节长度。

  • 若bind()函数绑定成功则返回0,若出错则为-1

sockaddr 结构体内容如下:

struct sockaddr {sa_family_t     sa_family;char            sa_data[14];
}

我们需要填写的IP地址与端口号等信息,都在sa_data连续的14字节信息里面,但这个结构体对用户操作不友好,一般我们在使用的时候都会使用sockaddr_in结构体,sockaddr_in和sockaddr是并列的结构(占用的空间是一样的),指向sockaddr_in的结构体的指针也可以指向sockadd的结构体,并代替它,而且sockaddr_in结构体对用户将更加友好,在使用的时候进行类型转换就可以了。

struct sockaddr_in {short int sin_family;               /* 协议族 */unsigned short int sin_port;        /* 端口号 */struct in_addr sin_addr;            /* IP地址 */unsigned char sin_zero[8];          /* sin_zero是为了让sockaddr与sockaddr_in两个数据结构体保持大小相同而保留的空字节 */
};

sockaddr_in结构体的第一个字段是与sockaddr结构体是一致的,而剩下的字段就是sa_data连续的14字节信息里面的内容,只不过重新定义了成员变量而已,sin_port字段是我们需要填写的端口号信息,sin_addr字段是我们需要填写的IP地址信息,in_addr结构如下图,剩下sin_zero 区域的8字节保留未用。

typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
}
// bind()使用示例
struct sockaddr_in server;
bzero(&server, sizeof(server));
// 向server中填入IP和端口号
server.sin_family = AF_INET;  //协议族
server.sin_addr.s_addr = htonl(INADDR_ANY); //IP
server.sin_port = htons(6666);   //端口号// 将IP和端口号进行绑定
bind(sockfd, (struct sockaddr*)&server, sizeof(server));

3.3 connect()

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect()函数是套接字连接操作,参数与bind()函数相同,对于TCP协议来说,connect()函数操作成功之后代表对应的套接字已与远端主机建立了连接,可以发送与接收数据。

对于UDP协议来说,没有连接的概念,在这里可将其描述为记录远端主机的IP地址与端口号,UDP协议经过connect()函数调用成功之后,在通过sendto()函数发送数据报时不需要指定目的地址、端口,因为此时已经记录到了远端主机的IP地址与端口号。UDP协议还可以给同一个套接字进行多次connect()操作,而TCP协议不可以,TCP只能指定一次connect操作。

3.4 listen()

listen()函数只能在TCP服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求,listen()函数在一般在bind()函数之后调用,在accept()函数之前调用,它的函数原型为:

int listen(int sockfd, int backlog);

 参数:

  • sockfd:sockfd是由socket()函数返回的套接字描述符。

  • backlog:用来描述sockfd的等待连接队列能够达到的最大值。在服务器进程正处理客户端连接请求的时候,可能还存在其它的客户端请求建立连接,因为TCP连接是一个过程,由于同时尝试连接的用户过多,使得服务器进程无法快速地完成所有的连接请求,那怎么办呢?直接丢掉其他客户端的连接肯定不是一个很好的解决方法。因此内核会在自己的进程空间里维护一个队列,这些连接请求就会被放入一个队列中,服务器进程会按照先来后到的顺序去处理这些连接请求,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限,这个backlog告诉内核使用这个数值作为队列的上限。而当一个客户端的连接请求到达并且该队列为满时,客户端可能会收到一个表示连接失败的错误,本次请求会被丢弃不作处理。

3.5 accept() 

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

为了能够正常让TCP客户端能正常连接到服务器,服务器必须遵循以下流程处理:

  1. 调用socket()函数创建对应的套接字类型。

  2. 调用bind()函数将套接字绑定到本地的一个端口地址。

  3. 调用listen()函数让服务器进程进入监听状态,等待客户端的连接请求。

  4. 调用accept()函数处理到来的连接请求。

accept()函数用于TCP服务器中,等待着远端主机的连接请求,并且建立一个新的TCP连接,在调用这个函数之前需要通过调用listen()函数让服务器进入监听状态,如果队列中没有未完成连接套接字,并且套接字没有标记为非阻塞模式,accept()函数的调用会阻塞应用程序直至与远程主机建立TCP连接;如果一个套接字被标记为非阻塞式而队列中没有未完成连接套接字, 调用accept()函数将立即返回EAGAIN。

3.6 read()/recv()/recvfrom()

一旦客户端与服务器建立好TCP连接之后,我们就可以通过sockfd套接字描述符来收发数据,这与我们读写文件是差不多的操作,接收网络中的数据函数可以是read()、recv()、recvfrom()等。

ssize_t read(int fd, void *buf, size_t count);

参数:

  • fd:在socket编程中是指定套接字描述符。

  • buf:指定存放数据的地址。

  • count:是指定读取的字节数,将读取到的数据保存在缓冲区buf中。

错误代码:

  • EINTR:在读取到数据前被信号所中断。

  • EAGAIN:使用O_NONBLOCK 标志指定了非阻塞式输入输出,但当前没有数据可读。

  • EIO:输入输出错误,可能是正处于后台进程组进程试图读取其控制终端,但读操作无效,或者被信号SIGTTIN所阻塞, 或者其进程组是孤儿进程组,也可能执行的是读磁盘或者磁带机这样的底层输入输出错误。

  • EISDIR:fd 指向一个目录。

  • EBADF:fd不是一个合法的套接字描述符,或者不是为读操作而打开。

  • EINVAL:fd所连接的对象不可读。

  • EFAULT:buf 超出用户可访问的地址空间。

read() 从描述符 fd(描述符可以是文件描述符也可以是套接字描述符)中读取 count 字节的数据并放入从 buf 开始的缓冲区中,read()函数调用成功返回读取到的字节数,此返回值受文件剩余字节数限制,当返回值小于指定的字节数时并不意味着错误;这可能是因为当前可读取的字节数小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者read()函数被信号中断等),出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0。

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:

  • sockfd:指定接收端套接字描述符。

  • buf:指定一个接收数据的缓冲区,该缓冲区用来存放recv()函数接收到的数据。

  • len:指定recv()函数拷贝的数据长度。

参数 flags 一般设置为0即可,其他数值定义如下:

  • MSG_OOB:接收以out-of-band送出的数据。

  • MSG_PEEK:保持原有数据,就是说接收到的数据并不会被删除,如果再调用recv()函数还会拷贝相同的数据到buf中。

  • MSG_WAITALL:强迫接收到指定len大小的数据后才能返回,除非有错误或信号产生。

  • MSG_NOSIGNAL:recv()函数不会被SIGPIPE信号中断,返回值成功则返回接收到的字符数,失败返回-1,错误原因存于errno中。

错误代码:

  • EBADF:fd 不是一个合法的套接字描述符,或者不是为读操作而打开。

  • EFAULT:buf 超出用户可访问的地址空间。

  • ENOTSOCK:参数 s 为一文件描述词, 非socket.

  • EINTR:在读取到数据前被信号所中断。

  • EAGAIN:此动作会令进程阻塞, 但参数s的 socket 为不可阻塞。

  • ENOBUFS:buf内存空间不足。

  • ENOMEM:内存不足。

  • EINVAL:传入的参数不正确。

recv()函数会先检查套接字 sockfd 的接收缓冲区,如果 sockfd 接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv()函数就把 sockfd 的接收缓冲中的数据拷贝到 buf 中,但是要注意的是议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv()函数才能把s的接收缓冲中的数据拷贝完。recv()函数仅仅是拷贝数据,真正的接收数据是由协议来完成的,recv函数返回其实际拷贝的字节数。如果recv()函数在拷贝时出错,那么它返回SOCKET_ERROR;如果recv()函数在等待协议接收数据时网络中断了,那么它返回0。

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

recvfrom()前三个参数和recv()一样,后两个参数为输出型参数,会将客户端的IP、端口号等信息填入到这两个参数中。

3.7 send()/sendto()

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

无论是客户端还是服务器应用程序都可以用send()函数来向TCP连接的另一端发送数据。

参数

  • sockfd:指定发送端套接字描述符。

  • buf:指定要发送数据的缓冲区。

  • len:指明实际要发送的数据的字节数。

  • flags:一般设置为0即可

当调用该函数时,send()函数会先比较待发送数据的长度len和套接字sockfd的发送缓冲的长度。 如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于sockfd的发送缓冲区的长度,那么send()函数先检查协议是否正在发送sockfd的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送sockfd的发送缓冲中的数据或者sockfd的发送缓冲中没有数据,那么send()函数就比较sockfd的发送缓冲区的剩余空间和len。如果len大于剩余空间大小,send()函数就一直等待协议把sockfd的发送缓冲中的数据发送完。如果len小于剩余空间大小,send()函数就仅仅把buf中的数据拷贝到sockfd的发送缓冲区的剩余空间里。 

如果send()函数拷贝数据成功,就返回实际copy的字节数,如果send()函数在拷贝数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

注意:send()函数把buf中的数据成功拷贝到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

sendto()函数与send()函数类似,但是它会通过 struct sockaddr 指向的 dest_addr 结构体指定要发送给哪个远端主机,在 dest_addr 参数中需要指定远端主机的IP地址、端口号等,而 addrlen 参数则是指定 dest_addr 结构体的字节长度。

 3.8 close()

int close(int fd);

close()函数是用于关闭文件描述符,套接字其实也是文件描述符的一种,所以也可以用来关闭套接字,在关闭套接字后,将无法使用对应的套接字描述符,这个函数比较简单,当你不需要使用某个套接字描述符时,就将其关闭即可。

 四、UPD客服/服务端实验 

服务端代码: 

#include <iostream>
#include <unordered_map>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <memory>
#define SIZE 1024class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = ""):_port(port),_ip(ip),_sock(-1){}bool InitServer(){// 1. 创建套接字_sock = socket(AF_INET, SOCK_DGRAM, 0);if(_sock < 0){std::cout << "socket error" << std::endl;exit(2);}// 2. 将用户设置的ip和port通过bind进行绑定struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port); // 主机序列转网络序列// 3. 先将字符串风格ip地址转为主机序列,再将主机序列转为网络序列 local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());if(bind(_sock, (struct sockaddr*)&local,sizeof(local)) < 0){std::cout << "bind failed" << std::endl;exit(2);}std::cout << "InitServer Success!" << std::endl; return true;         }void Start(){char buffer[SIZE];while(true){struct sockaddr_in peer; //输出型参数bzero(&peer, sizeof(peer));socklen_t len = sizeof(peer);std::string cmd_echo;// 读数据ssize_t s = recvfrom(_sock, buffer, sizeof(buffer)-1, 0, (struct     sockaddr*)&peer, &len);if(s > 0){buffer[s] = 0; //当作字符串uint16_t client_port = ntohs(peer.sin_port); //从网络中获取std::string client_ip = inet_ntoa(peer.sin_addr);// 网络序列->字符串风格ipprintf("[%s:%d]# %s\n",client_ip.c_str(), client_port, buffer);}// 写回数据sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len);                                    }}~UdpServer(){if(_sock >= 0){close(_sock);}}private:// 一个服务器,一般需要ip地址和端口号uint16_t _port;std::string _ip;int _sock;    
};int main(int argc, char* argv[])
{if(argc != 3){std::cout << "\nUsage:" << argv[0] << "ip port" << std::endl;exit(1);}uint16_t port = atoi(argv[2]);std::unique_ptr<UdpServer> svr(new UdpServer(port, argv[1])); //智能指针svr->InitServer();svr->Start();return 0;    
}

 客户端代码:

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>int main(int argc, char* argv[])
{if(argc != 3){std::cout << "\nUsage:" << argv[0] << "ServerIp ServerPort\n" << std::endl;exit(1);}int sock = socket(AF_INET, SOCK_DGRAM, 0);if(sock < 0){std::cout << "socket error" << std::endl;}std::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){std::cout << "请输入你的信息: ";std::getline(std::cin, message);sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));ssize_t s = recv(sock, buffer, sizeof(buffer), 0);if(s > 0){buffer[s] = 0;std::cout << "server echo# " << buffer << std::endl;}}return 0;
}

netstat -anup 可以查看udp协议的网络状态 

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

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

相关文章

打造专业级ChatGPT风格聊天界面:SpringBoot与Vue实现动态打字机效果,附完整前后端源码

大家好&#xff0c;今天用SpringBoot、vue写了一个仿ChatGPT官网聊天的打字机效果。 所有代码地址:gitee代码地址 &#xff0c;包含前端和后端&#xff0c;可以直接运行 使用本技术实现的项目&#xff1a;aicnn.cn&#xff0c;欢迎大家体验 如果文章知识点有错误的地方&#xf…

AEB滤镜再破碎,安全焦虑「解不开」?

不久前&#xff0c;理想L7重大交通事故&#xff0c;再次引发了公众对AEB的热议。 根据理想汽车公布的事故视频显示&#xff0c;碰撞发生前3秒&#xff0c;车速在178km/h时驾驶员采取了制动措施&#xff0c;但车速大幅超出AEB&#xff08;自动紧急刹车系统&#xff09;的工作范…

Java下载FTP服务器上的资源,附带FTP工具类

通过xftp可以看到目标服务器上面的资源如下&#xff1a; 第一步&#xff1a;导入ftp依赖&#xff1a; <dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.7</version> <!-- 使用最新…

PyTorch 内 LibTorch/TorchScript 的使用

PyTorch 内 LibTorch/TorchScript 的使用 1. .pt .pth .bin .onnx 格式1.1 模型的保存与加载到底在做什么&#xff1f;1.2 为什么要约定格式&#xff1f;1.3 格式汇总1.3.1 .pt .pth 格式1.3.2 .bin 格式1.3.3 直接保存完整模型1.3.4 .onnx 格式1.3.5 jit.trace1.3.6 jit.scrip…

了解云工作负载保护:技术和最佳实践

云工作负载是指云环境中的应用程序或存储元素&#xff0c;无论是公共云、私有云还是混合云。每个云工作负载都使用云的资源&#xff0c;包括计算、网络和存储。 云工作负载可以多种多样&#xff0c;例如运行应用程序、数据库或托管网站。它们可以是静态的或动态的&#xff0c;…

mysql 导入数据 1273 - Unknown collation: ‘utf8mb4_0900_ai_ci‘

前言: mysql 导入数据 遇到这个错误 1273 - Unknown collation: utf8mb4_0900_ai_ci 具体原因没有深究 但应该是设计数据库的 字符集类型会出现这个问题 例如: char varchar text..... utf8mb4 类型可以存储表情 在现在这个时代会用很多 以后会用的更多 所以不建议改…

LabVIEW滚动轴承故障在线监测

展示了如何将LabVIEW开发出一种有效的滚动轴承故障在线监测系统。介绍了该系统的开发过程、工作原理及其在实际应用中的效果。该系统成功地应用于对滚动轴承故障的早期诊断&#xff0c;提高了故障检测的准确性和效率。 滚动轴承在工作过程中会产生复杂的振动信号&#xff0c;包…

【C语言进阶】预处理详解

引言 对预处理的相关知识进行详细的介绍 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》 &#x1f388;跟着猪巴戒&#xff0c;一起学习C语言&#x1f388; 目录 引言 预定义符号 #define定义常量 #define定义宏 带有副作用的宏参数 宏替换的规则 …

开源的测试平台快2千星了,能带来多少收益呢

最近看了下自己去年初开源的测试平台&#xff0c;star一起算的话也到1.7k了&#xff1a; 做开源的初心一方面是想把自己的理解和思想展示出来&#xff0c;另一方面是想进一步打造个人IP&#xff0c;提升影响力&#xff08;其实这个想法很早之前就有了&#xff0c;计划过无数次但…

【BERT】详解

BERT 简介 BERT 是谷歌在 2018 年时提出的一种基于 Transformer 的双向编码器的表示学习模型&#xff0c;它在多个 NLP 任务上刷新了记录。它利用了大量的无标注文本进行预训练&#xff0c;预训练任务有掩码语言模型和下一句预测&#xff0c;掩码语言模型指的是随机地替换文本中…

「云渲染C4D」C4D如何进行云渲染?

云渲染C4D的过程可现实一键式完成&#xff0c;目前云渲染平台随着技术的发展&#xff0c;平台的使用越发容易操作&#xff0c;无论是渲染文件的传输性、安全性、高效性都有较大的提升&#xff0c;本次为大家简单说明下关于云渲染操作方法。 &#xff08;图源网络&#xff09; …

Android状态栏布局隐藏的方法

1.问题如下&#xff0c;安卓布局很不协调 2.先将ActionBar设置为NoActionBar 先打开styles.xml 3.使用工具类 package com.afison.newfault.utils;import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graph…

“深入理解 Docker 和 Nacos 的单个部署与集成部署“

目录 引言&#xff1a;Docker Nacos 单个部署1.1 什么是 Docker&#xff1f;Docker 的概念和工作原理Docker 为什么受到广泛应用和认可 1.2 什么是 Nacos&#xff1f;Nacos 的核心功能和特点Nacos 在微服务架构中的作用 1.3 Docker 单个部署 Nacos Docker Nacos 集成部署总结&a…

如何使用固定公网地址访问多个本地Nginx服务搭建的网站

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 本文主要介绍如何在Windows系统对Nginx进行配置&#xff0c;并结合cpolar内网穿透工具实现固定公网地址远程访问多个本地站…

Spring Boot整合Redis的高效数据缓存实践

引言 在现代Web应用开发中&#xff0c;数据缓存是提高系统性能和响应速度的关键。Redis作为一种高性能的缓存和数据存储解决方案&#xff0c;被广泛应用于各种场景。本文将研究如何使用Spring Boot整合Redis&#xff0c;通过这个强大的缓存工具提高应用的性能和可伸缩性。 整合…

操作系统导论-课后作业-ch14

1. 代码如下&#xff1a; #include <stdio.h> #include <stdlib.h>int main() {int *i NULL;free(i);return 0; }执行结果如下&#xff1a; 可见&#xff0c;没有任何报错&#xff0c;执行完成。 2. 执行结果如下&#xff1a; 3. valgrind安装使用参考&a…

接口自动化测试(Python+Requests+Unittest)

(1)接口自动化测试的意义、前后端分离思想 接口自动化测试的优缺点&#xff1a; 优点&#xff1a; 1、测试复用性。 2、维护成本相对UI自动化低一些。 为什么UI自动化维护成本更高&#xff1f; 因为前端页面变化太快&#xff0c;而且UI自动化比较耗时&#xff08;比如等待页…

常见PCB封装

表面贴片封装 通孔封装 公众号 | FunIO 微信搜一搜 “funio”&#xff0c;发现更多精彩内容。 个人博客 | blog.boringhex.top

基于神经网络的电力系统的负荷预测

一、背景介绍&#xff1a; 电力系统负荷预测是生产部门的重要工作之一&#xff0c;通过准确的负荷预测&#xff0c;可以经济合理地安排机组的启停、减少旋转备用容量、合理安排检修计划、降低发电成本和提高经济效益。负荷预测按预测的时间可以分为长期、中期和短期负荷预测。…

使用pysimplegui+opencv编写一个摄像头的播放器

需求 使用pysimplegui和opencv实现一个播放器&#xff0c;播放 摄像头的画面。 代码实现 import cv2 import time from typing import Iterable, NamedTuple, Optionalimport PySimpleGUI as sgclass CameraSpec(NamedTuple):name: strindex: intwidth: intheight: intfps: i…