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客户端能正常连接到服务器,服务器必须遵循以下流程处理:
调用socket()函数创建对应的套接字类型。
调用bind()函数将套接字绑定到本地的一个端口地址。
调用listen()函数让服务器进程进入监听状态,等待客户端的连接请求。
调用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协议的网络状态


相关文章:
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客服/服务端实…...
【DeepLearning-1】 注意力机制(Attention Mechanism)
1.1注意力机制的基本原理: 计算注意力权重: 注意力权重是通过计算输入数据中各个部分之间的相关性来得到的。这些权重表示在给定上下文下,数据的某个部分相对于其他部分的重要性。 加权求和: 使用这些注意力权重对输入数据进行加权…...
c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)
文章目录 1. 415. 字符串相加题目详情代码1思路1代码2思路2 2. 125. 验证回文串题目详情代码1(按照要求修改后放到新string里)思路1代码2(利用双指针/索引)思路2 3. 541. 反转字符串 II题目详情代码1思路1 4. 557. 反转字符串中的单词 III题目详情代码1&…...
HuoCMS|免费开源可商用CMS建站系统HuoCMS 2.0下载(thinkphp内核)
HuoCMS是一套基于ThinkPhp6.0Vue 开发的一套HuoCMS建站系统。 HuoCMS是一套内容管理系统同时也是一套企业官网建设系统,能够帮过用户快速搭建自己的网站。可以满足企业站,外贸站,个人博客等一系列的建站需求。HuoCMS的优势: 可以使用统一后台…...
VsCode + CMake构建项目 C/C++连接Mysql数据库 | 数据库增删改查C++封装 | 信息管理系统通用代码 ---- 课程笔记
这个是B站Up主:程序员程子青的视频 C封装Mysql增删改查操作_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1m24y1a79o/?p6&spm_id_frompageDriver&vd_sourcea934d7fc6f47698a29dac90a922ba5a3安装mysql:mysql 下载和安装和修改MYSQL8.0 数据库存储…...
HackTheBox - Medium - Linux - Ransom
Ransom 外部信息搜集 端口扫描 循例nmap Web枚举 /api/login 它似乎受nosql注入影响,我们能够登录成功 把返回的cookie丢到cookie editor,回到主页 zip是加密的 Foothold 我们可以得知加密类型是ZipCrypto 谷歌能够找到这篇文章,它将告诉我…...
柠檬微趣面试准备
简单介绍一下spring原理 Spring框架是一个开源的Java应用程序框架,它提供了广泛的基础设施支持,帮助开发者构建Java应用程序。Spring的设计原则包括依赖注入(DI)和面向切面编程(AOP)等,以促使代…...
uniapp嵌套webview,无法返回上一级?
uniapp嵌套webview,如何解决回退问题? 文章目录 uniapp嵌套webview,如何解决回退问题?遇到问题解决方式方式一方式二 场景: 进入首页,自动跳转第三方应用 遇到问题 在设备上运行时,无法回退上…...
【优先级队列 之 堆的实现】
文章目录 前言优先级队列 PriorityQueue优先队列的模拟实现 堆堆的储存方式堆的创建建堆的时间复杂度堆的插入与删除 总结 前言 优先级队列 PriorityQueue 概念:对列是先进先出的的数据结构,但有些情况,数据可能带有优先级,一般出…...
Vue中$watch()方法和watch属性的区别
vue中$watch()和watch属性都是监听值的变化的,是同一个作用,但是有两个不同写法。 用法一: //注意:这种方法是监听不到对象的变化的。 this.$watch((newVal,oldVal)>{ }) 用法二: watch:{xxx:(newVal,oldVal)>…...
openssl3.2 - 官方demo学习 - test - certs - 001 - Primary root: root-cert
文章目录 openssl3.2 - 官方demo学习 - test - certs - 001 - Primary root: root-cert概述笔记备注END openssl3.2 - 官方demo学习 - test - certs - 001 - Primary root: root-cert 概述 实验前置条件为 openssl3.2 - linux脚本(.sh)调用openssl命令行参数的简单确认方法 …...
小程序商城能不能自己开发?
在数字化时代,小程序商城已经成为商家拓展销售渠道、提升品牌影响力的重要工具。那么,商家能否自己动手开发小程序商城呢?答案是肯定的。接下来,以乔拓云为例,为大家详细介绍如何自己搭建小程序商城。 首先,…...
GPTBots:利用FlowBot中的卡片和表单信息,提供丰富的客服体验
在当今的数字化时代,客户服务的形式和体验正在经历着前所未有的变革。传统的文字消息方式已经无法满足现代用户对于服务体验的多元化需求。那么,如何才能在这个信息爆炸的时代,让我们的服务方式更加个性化、多样化,从而提供更丰富…...
ERC20 解读
1.ERC20 什么叫做代币? 代币可以在以太坊中表示任何东西: 在线平台中的信誉积分游戏中一个角色的技能彩票卷金融资产类似于公司股份的资产像美元一样的法定货币一盎司黄金及更多... 以太坊的这种强大特点必须以强有力的标准来处理,对吗&a…...
C#,入门教程(31)——预处理指令的基础知识与使用方法
上一篇: C#,入门教程(30)——扎好程序的笼子,错误处理 try catchhttps://blog.csdn.net/beijinghorn/article/details/124182386 Visual Studio、C#编译器以及C#语法所支持的预处理指令,绝对是天才设计。 编译程序的时候会发现&am…...
Java SE:面向对象(下)
1. static关键字 静态区的特点:静态区里面的每一样东西都是唯一有且仅有一个的,如此时str1 "abc"即此时静态区里面已经创建了字符串abc并将abc地址赋给str1,后面在进行赋值也不会在静态区开辟一串新的"abc" 1.1 static修…...
搭建开源数据库中间件MyCat2-配置mysql数据库双主双从
mycat2官网:MyCat2 前言:mycat2下载地址无法访问,不知道是不是被DNS污染了,还是需要搭梯子访问,所以我只能找到1.21的版本进行安装。搭建mycat2的前提是搭建数据库主从复制。 架构:双主双从 配置…...
Oracle 19c rac集群管理 -------- 集群启停操作过程
Oracle rac集群启停操作过程 首先查看数据库的集群的db_unique_name SQL> show parameter nameNAME TYPE VALUE ------------------------------------ ----------- --------------------------- cdb_cluster_name …...
【Java】HttpServlet类中前后端交互三种方式(query string、form表单、JSON字符串)
在前后端的交互中,前端通过以下三种方式来与后端进行交互🌟 ✅query string ✅form表单 ✅JSON字符串 下面我们将书写这三种方式的后端代码并进行讲解 1、Query String QueryString即在url中写入键值对,一般用doGet方法进行交互 代码如下 …...
【深蓝学院】移动机器人运动规划--第2章 基于搜索的路径规划--笔记
0. Outline 1. Graph Search Basis Configuration Space等概念 机器人配置: 指机器人位置和所有点的表示。 DOF: 指用于表示机器人配置所需的最小的实数坐标的数量n。 C-space: 包含机器人n维所有配置的空间。 在C-space中机器人的pose是一个点。 机器人在C-space中被表示为一…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
