yo!这里是socket网络编程相关介绍
目录
前言
基本概念
源ip&&目的ip
源端口号&&目的端口号
udp&&tcp初识
socket编程
网络字节序
socket常见接口
socket
bind
listen
accept
connect
地址转换函数
字符串转in_addr
in_addr转字符串
套接字读写函数
recvfrom&&recv
sendto&&send
简单udp服务器客户端程序
udp服务器
udp客户端
简单tcp服务器客户端程序
接口理解
tcp服务器
tcp客户端
后记
前言
在学习网络部分知识时,只学习或只背诵概念是没有用的,面试过程中面试官肯定会究其细节或理解从各方面考察,因此上章节介绍完网络初步相关概念之后,我们先上代码去直观地了解网络,为后面更深层次或者复杂的概念或网络传输过程的细节打下基础。那么,在网络部分socket编程就是用于在不同计算机之间进行通信,因此本章节就是介绍socket编程相关接口以及运用其编写简单的程序去了解通信过程。
基本概念
-
源ip&&目的ip
在ip数据包的头部中存在两个IP地址——源ip&&目的ip,即此数据包从哪来&&去哪里。我们知道,IP地址(此处说的是公网ip),标定了主机的唯一性,但通过IP地址将数据发送到主机并不是完整的网络通信过程,也就是说一台主机的信息到达了目标主机上真的就算是数据发送成功了吗?并不是,其实网络通信过程的本质就是进程间通信(由一台主机的进程发送到另一台主机的进程),将数据在主机间转发只是手段,主机收到后需要将数据交付给相应进程。但是,数据到达目标主机后该走向哪个进程呢?这就需要下一个概念——端口号了。
-
源端口号&&目的端口号
端口号(2字节16位的整数)是传输层的内容,标识特定主机上网络进程的唯一性,也就是标识一个进程,告诉os数据交给哪个进程来处理。可见,一个端口号只能被一个进程占用,但一个进程可以绑定多个端口号。
源端口号&&目的端口号存在于传输层的数据段中,标识哪个进程发的&&发给哪个进程。综上ip+端口号表示指定主机的指定进程,这是一个数据进入网络前要确定的东西。
-
udp&&tcp初识
先来简单初识一下TCP和UDP,在后面讲解传输层时会详解。TCP(Transmission Control Protocol 传输控制协议)和UDP(User Datagram Protocol 用户数据报协议)都是传输层协议,但TCP需要连接、可靠传输、面向字节流,而UDP无需连接、不可靠传输、面向数据报,如下图很具象地展现了TCP和UDP传输数据地过程。
其中,(无需)连接说的是udp没有tcp在通信的三次握手与四次挥手,而是没有建立连接过程,即“发送即结束”;可靠传输说的是tcp传输数据是不会出错的,udp传输数据可以出错;面向字节流与面向数据报是指看待数据的方式不同,tcp需要根据应用层协议对字节流作序列化和反序列化识别出一个报文,而udp直接默认拿到的就是一个报文。但是,综上来看真的意思是tcp比udp好吗?其实不然,两者的使用需要看场景,tcp可以多应用在需要数据准确交付的场景,比如重要文件传输,而udp可以多应用在对传输错误也可容忍的场景,比如游戏,直播等实时性要求高的环境下(有时看直播会卡,但是无伤大雅)。
-
socket编程
Socket编程是一种网络编程技术,它提供了一种在不同计算机之间进行通信的方式。
基于TCP/IP协议,它允许计算机之间通过网络进行数据传输。Socket编程提供了一些函数和方法,用于建立连接、传输数据和关闭连接。
在Socket编程中,有两个主要的角色:服务器和客户端。服务器程序监听指定的端口,等待客户端的连接请求。客户端程序通过指定服务器的IP地址和端口,发起连接请求。以上会在下面详细介绍。
网络字节序
我们知道,内存中的多字节数据相对于内存地址有大端和小端之分,而网络数据流也同样有大端小端之分,因此,TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.。不管这台主机是大端机还是小端机,都要按照这个TCP/IP规定的网络字节序来发送/接收数据,如果当前发送主机是小端,就需要先将数据转成大端,否则就忽略,直接发送即可。那么,如下接口就是转大端的相关接口,调用其可以做网络字节序和主机字节序的转换。
其中h表示host,n表示net,l表示32位长整数,s表示16位短整数,如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数将不做转换,将参数原封不动的返回。
socket常见接口
-
socket
功能:创建socket文件描述符(tcp和udp均会用到,服务器和客户端均需要)
参数:
domain:对于ipv4,指定为AF_INET,对于ipv6,指定为AF_INET6
type:对于UDP,指定为SOCK_DGRAM,表示面向数据报的传输协议,对于TCP,指定为SOCK_STREAM,表示面向字节流的传输协议
protocol:指定为0即可
返回值:成功返回一个文件描述符,类似读写文件一样,向此文件描述符读写就是在网络上收发数据,否则返回-1
-
bind
功能:绑定端口号(tcp和udp均会用到,仅服务器需要,因为服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,但是客户端程序的端口号通常是变化的,所以不需要bind),将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号
参数:
sockfd:需要绑定的sock套接字,其实就是socket函数的返回值
addr:通用指针类型,可以接受多种协议的sockaddr结构体地址(多种sockaddr结构体类型在下面介绍),但长短不同,需要传入长度
addrlen:addr指向的结构体长度
返回值:成功返回0,否则返回-1
sockaddr结构:
socket的接口适用于各种底层网络协议,但是各种网络协议的地址格式并不相同,又不想设计过多的接口,因此将所有接口进行统一,接口只有一套,协议格式统一使用struct sockaddr*,传其他协议地址(sockaddr_in、sockaddr_un,如下图)需要通过强转,但若需要知道是哪一种套接字地址,可以通过16位地址类型判断。
这三者中,本章节常用到sockaddr_in结构,也就是说常用到将sockaddr结构体强转成sockaddr_in结构体,这里详细说明一下sockaddr_in结构体,以下为其内部结构,在这个结构体中,最主要的就是端口号和IP地址,即图中标星号的部分。
在我们写的程序中一般是这样初始化sockaddr结构体的(如下代码块),首先清零,再指定地址类型,然后填入ip和端口号信息。注意转大端字节序,并且其中INADDR_ANY为宏(实则为0.0.0.0),表示本地的任意IP地址,则最后一个语句代表若需要指定ip地址则赋值给_ip,否则使用任意IP地址。
代码:
struct sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(_port);
peer.sin_addr.s_addr = _ip.size() == 0 ? INADDR_ANY : inet_addr(_ip.c_str());
-
listen
功能:开始监听socket(仅tcp会用到,仅服务器需要)
参数:
sockfd:如上
backlog:表示最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,常设置为20,后面会详细讲到
返回值:成功返回0,否则返回-1
-
accept
功能:接受请求(仅tcp会用到,仅服务器需要),三次握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来
参数:
sockfd:如上
addr和addrlen:输出型参数,需提前定义传入函数,函数返回后带出来对方的ip和端口号信息,若传NULL,则代表不关心客户端的地址
返回值:成功返回一个文件描述符,否则返回-1
-
connect
功能:建立连接(仅tcp会用到,仅客户端需要,因为客户端需要调用此函数连接服务器)
参数:
sockfd:自己(客户端)的fd
addr和addrlen:传入对方的IP地址和端口号信息
返回值:成功返回0,失败返回-1
地址转换函数
在上面介绍到的sockaddr中,sockaddr_in的成员sin_addr(struct in_addr)表示32位的IP地址,但是我们通常都是用点分十进制(比如192.168.1.10)表示IP地址,因此需要函数可以将字符串表示和in_addr之间来回转换。
-
字符串转in_addr
首先,三者的功能是一样的——将字符串转in_addr,先看前两个,cp就是需要转换的字符串,inp是一个输出型参数,结果由此参数传出,而第二个函数是直接返回出来,第三个函数较于前两个函数是新的,其中af可以填AF_INET或AF_INET6表示ipv4或ipv6,src还是传入需要转换的字符串,dst与inp的功能是一样的,只是需要接收ipv4的in_addr或ipv6的in6_addr,因此设置为void*可以同样接收(强转)。
-
in_addr转字符串
同样地,in传入in_addr,函数返回转换地字符串形式,而inet_ntop是新接口,可以接收ipv6,src就是传入不同类型的in_addr,dst接收转换的字符串指针,size传入字符串指针可以接收的字符串大小。
套接字读写函数
-
recvfrom&&recv
注意,recv用于tcp,recvfrom函数用于udp。可以看到,recvfrom函数除了比recv函数多了两个参数,其他的参数是一样的,因此只要介绍recvfrom即可。
sockfd:读写的套接字(文件描述符)
buffer&&len:读取的数据所放的缓冲区及大小
flags:控制接收行为,通常可设为0
src_addr&&addrlen:输出型参数,用来接收数据来源的地址信息及大小,注意addrlen在传入时就要是sre_addr的大小
返回值:正常返回接收成功的字符个数若数据读完或套接字关闭,则返回值为0,若出错则返回-1并设置错误码
-
sendto&&send
注意,send用于tcp,sendto函数用于udp。同样地,sendto函数也是比send函数多了两个参数。
sockfd:读写的套接字(文件描述符)
buf&&len:指向需要发送的数据的字符串指针及大小
flags:同recvfrom函数
dest_addr&&addrlen:不是输出型函数,需要传入需要发送的目的地址信息及大小
返回值:同recvfrom函数
注意:
思考一下recvfrom函数和sendto函数的使用顺序,比如说,你使用sendto函数给张三发信息,此时你需要知道张三的地址信息,张三使用recvfrom函数接收到了你的信息,并且他通过输出型参数得到了你的地址信息,之后张三拿着你的地址信息使用sendto函数给你回信,你通过recvfrom函数拿到了张三的回信并且拿到了他的地址信息。
在这个过程中,你与张三通信的前提是你知道张三的地址信息,你其实是知道的,无论是之前见面留下的联系方式还是曾经通信过,你们之间必须至少有一方是知道的,否则无法通信。类比到服务器与客户端,“你”就是客户端,“张三”就是服务器,一般来说都是客户端主动给服务器发信息,客户端都是提前知道服务器的地址信息的,客户端给服务器发送请求,服务器处理需求,将结果回信给客户端,客户端拿到需求即为享受到服务。
简单udp服务器客户端程序
-
udp服务器
下面的代码块是udp服务器的封装及主程序调用。先看udp服务器封装的类,属性包括监听的文件描述符sockfd、IP地址、端口,这些是一个udp服务器最基本需要的属性,构造函数中传入所决定的端口号、IP地址、sock(还没有生成,初始化为-1)。
在启动udp服务器前,先做一下准备工作——initserver(),创建监听的文件描述符sock和绑定地址信息(包括端口号和IP地址),之后启动udp服务器——start(),显然是死循环运行,作为服务器,我们需要先使用recvfrom函数接收客户端的请求(数据),返回值大于0,说明正确读到了数据,这里当作字符串打印出来,并调用sendto函数将读到的字符串写回给客户端(处理方式有很多,也可以客户端传来两个数字,服务器计算出结果将其返回,但是这涉及到http协议的设定及序列化、反序列化)。最后若关闭服务器,莫要忘记close监听的sockfd。
可以看到,udp服务器只能用到前面所谈到的socket常用接口其中的两个——socket()、bind(),这可以联想到udp的特性——无连接,而listen()、accept()、connect()意味着是有连接,在tcp服务器的实现中才会用到。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
using std::cout;
using std::endl;
#define SIZE 1024class UdpServer
{
public:UdpServer(uint16_t port,std::string ipAddr=""):_port(port),_ipAddr(ipAddr),_sock(-1){}bool initserver(){//1.创建套接字(ip+port)_sock=socket(AF_INET,SOCK_DGRAM,0);if(_sock<0){logMessage(FATAL,"%d:%s",errno,strerror(errno));exit(2); }logMessage(NORMAL,"create socket success");//2.通过bind函数将此ip和端口号与此进程绑定struct sockaddr_in local;bzero(&local,sizeof(local)); //设置内容之前将所有字段设置为0local.sin_family=AF_INET;//端口需要发送给对方主机,需要通过网络,因此将数据的字节序改为大端存储local.sin_port=htons(_port);//ip也是一样,但同时还需要将ip的字符串形式改为4个字节存储,因此4个字节足够存储一个ip,//比如说192.168.10.1,每段数字都是[0,255],用1字节即可存储//因此使用inet_addr函数,可同时完成上面两件事//同时,INADDR_ANY表示服务器在工作过程中,可以从任意IP中获取数据,也就是说不建议bind一个确定的地址local.sin_addr.s_addr = _ipAddr.size()==0 ? INADDR_ANY : inet_addr(_ipAddr.c_str());if(bind(_sock,(struct sockaddr*)&local,sizeof(local))<0){logMessage(FATAL,"%d:%s",errno,strerror(errno));exit(3);}logMessage(NORMAL,"bind success");return true;}void start(){char buffer[SIZE];for( ; ; ){//读取数据struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t rf=recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(rf>0){buffer[rf]=0; //缓冲区末尾放上‘\0’,当作字符串uint16_t cli_port=ntohs(peer.sin_port);std::string cli_ip=inet_ntoa(peer.sin_addr);printf("[%s:%d]: %s\n",cli_ip.c_str(),cli_port,buffer);}//分析处理数据//写回数据sendto(_sock,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,len);}}~UdpServer(){if(_sock>=0) close(_sock);}
private:uint16_t _port;std::string _ipAddr;int _sock;
};static void usage(std::string proc)
{cout<<"\nUsage:"<<proc<<" port\n"<<endl;
}int main(int argc,char* argv[])
{if(argc!=2){usage(argv[0]);exit(1);}//std::string ip=argv[1];uint16_t port=stoi(argv[1]);std::unique_ptr<UdpServer> server(new UdpServer(port));server->initserver();server->start();return 0;
}
-
udp客户端
下面代码块是udp客户端的实现(主程序实现,未封装udp客户端),与服务器实现并没有本质的不同(但可以发现客户端不需要bind)。通过命令行参数传入服务器的地址信息(端口号和ip地址),首先依旧是创建监听的文件描述符sock,之后就可以将服务器的地址信息填进struct sockaddr_in中,等待后面的sendto函数的使用,但是会发现客户端无需bind,为什么?因为客户端通过哪个端口与服务器通信并不重要,socket函数的函数体中会自动为客户端程序选择一个未被占用的端口号,并不需要用户去操心,那又为什么服务器程序需要指定端口号呢?因为服务器的端口号和IP地址是需要固定的,不然客户端程序如何得知新的变化的端口号和IP地址,因此需要调用bind函数固定下来。
显然,客户端程序主体也是一个死循环,先产生用户的需求,之后调用sendto函数将需求发给服务器程序,服务器处理之后,客户端调用rcvfrom函数接收结果,这里将结果显示出来(举例而已,也可以将结果作为下一次传入的需求),若客户端程序关闭,也莫要忘记关闭监听的文件描述符。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <unistd.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using std::cout;
using std::endl;
#define SIZE 1024static void usage(std::string proc)
{cout<<"\nUsage:"<<proc<<" ip port\n"<<endl;
}int main(int argc,char* argv[]) //传入服务器ip、port
{if(argc!=3){usage(argv[0]);exit(1);}int sock=socket(AF_INET,SOCK_DGRAM,0);if(sock<0){std::cerr<<"socket error"<<endl;exit(2);}uint16_t serverport=atoi(argv[2]);std::string serverip=argv[1];std::string message; //从键盘拿信息char buffer[SIZE]; //缓冲区struct sockaddr_in server;memset(&server,0,sizeof(server)); //提前置零server.sin_addr.s_addr=inet_addr(serverip.c_str());server.sin_port=htons(serverport);while(true){cout<<"请输入:";std::getline(std::cin,message);if(message=="quit")break;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));struct sockaddr_in tmp;socklen_t len=sizeof(tmp);ssize_t rf=recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&tmp,&len); //tmp与server相同,都是服务端ip、port相关信息if(rf>0){buffer[rf]=0;cout<<"server # "<<buffer<<endl;}}close(sock);return 0;
}
运行结果:
简单tcp服务器客户端程序
-
tcp服务器
以下代码块是tcp服务器的封装及主程序调用,先看tcp服务器封装,属性和构造函数与udp服务器封装一样。对于initserver(),可以看到,调用socket创建一个文件描述符及bind()绑定地址信息也都是一样的,但是tcp服务器紧接着调用listen()监听sockfd是否有客户端连接,这是tcp服务器必须要做的,其实本质就是使sockfd成为一个监听描述符。
之后,依旧是死循环开启tcp服务器——start()。首先,服务器会调用accept函数去阻塞等待客户端的连接,当有客户端调用connect函数去建立连接时,accept函数就会返回一个新的文件描述符serverfd,这个文件描述符专门用来和客户端通信,而原本的监听描述符继续监听客户端的connect函数申请连接,若存在就会继续返回另一个新的文件描述符与此客户端通信。这个过程就好像是,监听文件描述符是一个在饭店门口拉客的服务员,当拉到客人之后进店就会找另一个服务员(与此客户端通信的文件描述符)来接待这个客人(比如点菜,服务),而拉客服务员继续去门外拉客。
当accept函数返回一个新的文件描述符来与客户端通信,服务器就可以通过此文件描述符收到客户端的需求,并返回应答给客户端,这里封装成了一个服务函数——service(),首先recv函数(也可以read函数)读取客户端的数据,处理以后通过write函数(也可以send函数)返回结果给客户端,这里依旧是把数据当作字符串并写回给客户端。
代码:
#include <iostream>
#include <string>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"using std::cout;
using std::endl;
using std::string;static void service(int servicesock,string cli_ip,uint16_t cli_port)
{char buffer[1024];while(true){int n=read(servicesock,buffer,sizeof(buffer)-1);if(n>0){buffer[n]='\0';cout<<cli_ip<<"-"<<cli_port<<"# "<<buffer<<endl;}else if(n==0) //连接被关闭了{logMessage(NORMAL,"link shutdown");break;}else{logMessage(ERROR,"read error");break;}string msg;msg+=buffer;write(servicesock,msg.c_str(),msg.size());}
}class TcpServer
{
private:const static int g_backlog=20;
public:TcpServer(uint16_t port,string ip=""):_port(port),_ip(ip),_sock(-1){}void initserver(){//创建socket_sock=socket(AF_INET,SOCK_STREAM,0);if(_sock<0){logMessage(FATAL,"create socket error");exit(2);}//bindstruct sockaddr_in peer;memset(&peer,0,sizeof(peer));peer.sin_port=htons(_port);peer.sin_family=AF_INET;peer.sin_addr.s_addr=_ip.size()==0 ? INADDR_ANY : inet_addr(_ip.c_str());if(bind(_sock,(struct sockaddr*)&peer,sizeof(peer))<0){logMessage(FATAL,"bind error");exit(3);}//listen函数用以建立连接,相当于饭店的一个服务员在外面拉客,其中_sock就是在外面拉客的服务员if(listen(_sock,g_backlog)<0){logMessage(FATAL,"listen error");exit(4);}logMessage(NORMAL,"initserver success");}void start(){while(true){struct sockaddr_in peer;socklen_t len=sizeof(peer);int servicesock=accept(_sock,(struct sockaddr*)&peer,&len); //_sock将拉的客人带到饭店,店里接待的服务员出来一个招呼他,其中servicesock就是一个招待的服务员if(servicesock<0){logMessage(ERROR,"accept error");continue;}logMessage(NORMAL,"accept success");uint16_t clientport=ntohs(peer.sin_port);string clientip=inet_ntoa(peer.sin_addr);service(servicesock,clientip,clientport);}}~TcpServer(){if(_sock>0)close(_sock);}
private:uint16_t _port;string _ip;int _sock;
};static void usage(string proc)
{cout<<"usage: "<<proc<<" port"<<endl;
}// ./tcpserver port
int main(int argc,char* argv[])
{if(argc!=2){usage(argv[0]);exit(1);}uint16_t serverport=atoi(argv[1]);unique_ptr<TcpServer> server(new TcpServer(serverport));server->initserver();server->start();return 0;
}
-
tcp客户端
tcp客户端也并未做出封装,依旧是在主程序中直接实现,与udp客户端程序实现的差别在于socket函数创建文件描述符(依旧不需要bind函数)之后,调用connect函数与服务器建立连接,成功以后就可以正常的通信了,这一点与udp客户端实现也是一样。
这里重点讲的就是,其实connect函数就是在发起三次握手,三次握手成功以后服务器的accept函数就会返回一个与客户端通信的文件描述符。也可以提一嘴,四次挥手是在客户端关闭(close函数)了socket函数创建出的文件描述符,详细会在传输层详解中讲到。
代码:
#include <iostream>
#include <string>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "thread.hpp"
#include "log.hpp"using std::cout;
using std::cerr;
using std::endl;
using std::string;static void usage(string proc)
{cout<<"usage: "<<proc<<" port"<<endl;
}// ./tcpclient serverip serverport
int main(int argc,char* argv[])
{if(argc!=3){usage(argv[0]);exit(1);}string serverip=argv[1];uint16_t serverport=atoi(argv[2]);int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){cerr<<"create socket error"<<endl;exit(2);}struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(serverport);server.sin_addr.s_addr=inet_addr(serverip.c_str());if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0){cerr<<"connect error"<<endl;exit(3);}cout<<"connect success"<<endl;while(true){string msg;cout<<"请输入:";getline(cin,msg);if(msg=="quit")break;send(sock,msg.c_str(),msg.size(),0);char buffer[1024];ssize_t s=recv(sock,buffer,sizeof(buffer)-1,0);if(s>0){buffer[s]=0;cout<<"server# "<<buffer<<endl;}else if(s==0){cout<<"link break"<<endl;break;}elsebreak;}close(sock);return 0;
}
运行结果:
后记
在本章节中,我们先从网络编程所需要的概念基础下手,再从运用的视角去详细介绍了各种供数据在网络中传输的socket接口,接着编写简单的udp、tcp服务器综合使用这些接口加以巩固。虽接口看起来很多并且细节也多而且复杂,但是用于网络数据传输的接口大部分也就涉及到这些,也就是说只要掌握这些,网络传输的功能就掌握了一大半了,因此在学习这些接口时要仔细,最后的简单udp、tcp服务器可以反复的独立编写,加强记忆,在学习后面较为复杂的服务器时可以相对轻松,加油。
相关文章:

yo!这里是socket网络编程相关介绍
目录 前言 基本概念 源ip&&目的ip 源端口号&&目的端口号 udp&&tcp初识 socket编程 网络字节序 socket常见接口 socket bind listen accept connect 地址转换函数 字符串转in_addr in_addr转字符串 套接字读写函数 recvfrom&&a…...

polars学习-03 数据类型转换
背景 polars学习系列文章,第3篇 数据类型转换。 该系列文章会分享到github,大家可以去下载jupyter文件 仓库地址:https://github.com/DataShare-duo/polars_learn 小编运行环境 import sysprint(python 版本:,sys.version.spli…...

IDC 权威认可!Aloudata 入选金融领域中数据管理分析服务最佳实践案例
近日,国际知名数据咨询机构 IDC 重磅发布了《IDC PeerScape:金融领域中数据管理分析服务最佳实践案例》报告,Aloudata 与招商银行联合打造的 DDH 数据研发运维一体化平台成功入选,Aloudata 的技术、产品实力,以及在金融…...

RSA与AES算法比较及应用场景推荐
摘要:在现代加密通信中,RSA算法和AES算法被广泛应用。RSA算法是一种非对称加密算法,而AES算法是一种对称加密算法。本文将对比分析这两种算法的原理、性能及适用场景,并给出相应的推荐建议。 一、RSA算法简介 RSA算法࿰…...

揭秘 HTTP 代理:增强在线活动的安全性和匿名性
HTTP 代理在保护您的在线隐私、增强安全性以及允许访问受限内容方面发挥着关键作用。了解 HTTP 代理的工作原理以及如何有效地利用它们可以让您掌控自己的在线状态和浏览体验。让我们深入研究 HTTP 代理的世界,探索它们的优势、应用程序以及最大化其效用的最佳实践。…...

【经验】mysql冷热数据分离
使用mysql存储时,为了提升数据的查询效率,降低磁盘存储压力等,我们常常使用"冷热数据分离"分离的方案。即,将数据从所谓的“热表”(即经常有写入和查询操作的活跃表)迁移到“冷表”(用…...

【机器学习-06】Scikit-Learn机器学习工具包进阶指南:机器学习分类模型实战与数据可视化分析
🎩 欢迎来到技术探索的奇幻世界👨💻 📜 个人主页:一伦明悦-CSDN博客 ✍🏻 作者简介: C软件开发、Python机器学习爱好者 🗣️ 互动与支持:💬评论 &…...

蓝桥杯国赛每日一题:日志统计(双指针)
题目描述: 小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N行。 其中每一行的格式是: ts id 表示在 ts时刻编号 id 的帖子收到一个”赞”。 现在小明想统计有哪些帖子曾经是”热帖”。 如果一个帖子曾在任意一个长…...

佛山MES公司(盈致mes系统服务商)助力企业实现智能制造
佛山是中国制造业著名的城市之一,拥有众多制造企业。随着科技的不断发展和智能制造的兴起,越来越多的企业开始意识到数字化生产管理的重要性,MES制造执行系统作为智能制造的关键技术之一,受到了越来越多企业的关注和应用。 在佛山…...

算法设计课第五周(贪心法实现活动选择问题)
目录 一、【实验目的】 二、【实验内容】 三、实验源代码 一、【实验目的】 (1)熟悉贪心法的设计思想 (2)理解贪心法的最优解与正确性证明之间的关系 (3)比较活动选择的各种“贪心”策略,…...

Ubuntu20.04右键打不开终端
今天用virtualbox安装了ubuntu20.04 问题:右键打开终端,怎么也打开不了! 点了也没反应,或者鼠标转小圈圈,然后也没有反应… 解决方法: 1、Ctrl Alt F6 先切换到终端访问界面 mac电脑 Ctrl Alt F6 …...

XML元素
XML 元素是XML文档中的基本组成单位,它由开始标签、结束标签和内容组成,格式如下: <element>content</element>常见的XML元素包括: 根元素(Root Element):XML文档中的最外层元素&…...

融入新科技的SLM27211系列 120V, 3A/4.5A高低边高频门极驱动器兼容UCC27284,MAX15013A
SLM27211是高低边高频门极驱动器,集成了120V的自举二极管,支持高频大电流的输出,可在8V~17V的宽电压范围内驱动MOSFET,独立的高、低边驱动以方便控制,可用于半桥、全桥、双管正激和有源钳位正激等拓。有极好的开通、关…...

代码随想录算法训练营Day 38| 动态规划part01 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
代码随想录算法训练营Day 38| 动态规划part01 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯 文章目录 代码随想录算法训练营Day 38| 动态规划part01 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯理论基础一、常规题目二、解题步骤…...

CSS拟物按钮
<div class"btn">F</div>.btn {margin: 150px 0 0 150px;display: flex;justify-content: center;align-items: center;width: 100px;height: 100px;background-color: #fff;border-radius: 20px;font-size: 50px;color: #333;/* 禁止选中文本 */user-se…...

websevere服务器从零搭建到上线(三)|IO多路复用小总结和服务器的基础框架
文章目录 epollselect和poll的优缺点epoll的原理以及优势epoll 好的网络服务器设计Reactor模型图解Reactor muduo库的Multiple Reactors模型 epoll select和poll的优缺点 1、单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数…...

解决宝塔Nginx和phpMyAdmin配置端口冲突问题
问题描述 在对基于宝塔面板的 Nginx 配置文件进行端口修改时,我注意到 phpMyAdmin 的端口配置似乎也随之发生了变化! 解决方法 官方建议在处理 Nginx 配置时,应避免直接修改默认的配置文件,以确保系统的稳定性和简化后续的维护…...

光伏EPC管理软件都有哪些功能和作用?
光伏EPC管理软件是用于光伏工程项目管理的综合性工具,它涵盖了从项目策划、设计、采购、施工到运维的各个环节。 1、项目总览 管理所有项目计划,包括项目类型、项目容量等。 调整和优化项目计划,以应对不可预见的情况。 2、施工管理 制定…...

BGP学习一:关于对等体建立和状态组改变
目录 一.BGP基本概念 (1).BGP即是协议也是分类 1.早期EGP 2.BGP满足不同需求 3.BGP区域间传输的优势 (1)安全性——只传递路由信息 (2)跨网段建立邻居 4.BGP总结 5.BGP的应用 (1&#…...

ETL工具kettle(PDI)入门教程,Transform,Mysql->Mysql,Csv->Excel
什么是kettle,kettle的下载,安装和配置:ETL免费工具kettle(PDI),安装和配置-CSDN博客 mysql安装配置:Linux Centos8 Mysql8.3.0安装_linux安装mysql8.3-CSDN博客 1 mysql -> mysql 1.1 mysql CREATE TABLE user_…...

常见地图坐标系间的转换算法JavaScript实现
文章目录 🍉 不同的地图厂商使用不同的坐标系来表示地理位置。以下简述:🍉 前置常量和方法:🍉 BD-09转GCJ-02(百度转谷歌、高德)🍉 GCJ-02转BD-09(谷歌、高德转百度)🍉 WGS84转GCJ-02(WGS84转谷歌、高德)🍉 GCJ-02转WGS84(谷歌、高德转WGS84)🍉 BD-09转wgs84坐…...

基于python的大麦网自动抢票工具的设计与实现
基于python的大麦网自动抢票工具的设计与实现 Design and Implementation of Da Mai Net Ticket Grabbing tool based on Python 完整下载链接:基于python的大麦网自动抢票工具的设计与实现 文章目录 基于python的大麦网自动抢票工具的设计与实现摘要第一章 引言1.1 研究背景…...

2024年5月树莓集团快讯
树莓集团近期快讯 1 园区专场招聘会进校园 国际数字影像产业园联合四川城市职业学院的专场招聘会成功召开,共计提供400余个工作岗位。 2 园区硬件优化再升级 园区硬件优化再升级,智能门禁系统及人脸识别系统下周投入使用。 3 基地短剧合作交流 天府…...

网站localhost和127.0.0.1可以访问,本地ip不可访问解决方案
部署了一个网站, 使用localhost和127.0.0.1加端口号可以访问, 但是使用本机的ip地址加端口号却不行. 原因可能有多种. 可能的原因: 1 首先要确认是否localhost对应的端口是通的(直接网址访问), 以及你无法访问的那个本机ip是否正确(使用ping测试); 2 检查本机的防火…...

Docker Dockerfile如何编写?
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。 1.指令说明 FROM,构建镜像基于哪个镜像 MAINTAINER,镜像维护者姓名或邮箱地址 RUN,构建镜像时运行的指令 CMD,运行容器时执…...

Python数独游戏
数独(Sudoku)是一种逻辑性的数字填充游戏,玩家需要在一个分为九宫的81格网格上填入数字,同时满足每一行、每一列以及每个宫(3x3的子网格)的数字都不重复。 在Python中实现一个数独游戏可以涉及到多个方面&…...

24 | MySQL是怎么保证主备一致的?
MySQL 主备的基本原理 内部流程 备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。一个事务日志同步的完整过程是这样的: 在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始…...

2.数据类型与变量(java篇)
目录 数据类型与变量 数据类型 变量 整型变量 长整型变量 短整型变量 字节型变量 浮点型变量 双精度浮点型 单精度浮点型 字符型变量 布尔型变量(boolean) 类型转换 自动类型转换(隐式) 强制类型转换(显式) 类型提升 字符串类型 数据类…...

QT设计模式:桥接模式
基本概念 桥接模式是一种结构型设计模式,它将抽象部分与它的实现部分分离,使得它们可以独立地变化,而不会相互影响。 需要实现的结构如下: 抽象部分(Abstraction):定义了抽象类的接口&#x…...

简单粗暴的翻译英文pdf
背景:看书的时候经常遇到英文pdf,没有合适的翻译软件可以快速翻译全书。这里提供一个解决方案。 Step 1 打开英文pdfCTRLA全选文字CTRLC复制打开记事本CTRLV复制保存为data.txt Step 2 写一个C脚本 // ToolPdf2Html.cpp : 此文件包含 "main&quo…...