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服务器可以反复的独立编写,加强记忆,在学习后面较为复杂的服务器时可以相对轻松,加油。


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

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

相关文章

Java入门基础学习笔记2——JDK的选择下载安装

搭建Java的开发环境&#xff1a; Java的产品叫JDK&#xff08;Java Development Kit&#xff1a; Java开发者工具包&#xff09;&#xff0c;必须安装JDK才能使用Java。 JDK的发展史&#xff1a; LTS&#xff1a;Long-term Support&#xff1a;长期支持版。指的Java会对这些版…

pycharm报错Process finished with exit code -1073740791 (0xC0000409)

pycharm报错Process finished with exit code -1073740791 (0xC0000409) 各种垃圾文章&#xff08;包括chatgpt产生的垃圾文章&#xff09;&#xff0c;没有给出具体的解决办法。 解决办法就是把具体报错信息显示出来&#xff0c;然后再去查。 勾选 然后再运行就能把错误显示…

MetaRTC-play拉流客户端代码分析

渲染使用opengl&#xff0c;音频播放使用alsa。 当点击播放按钮后&#xff0c;以此调用的类如下&#xff0c;开始建立rtc连接&#xff0c;AV解码&#xff0c;音频渲染&#xff0c;视频渲染。 如果想去除QT&#xff0c;改为cmake工程管理&#xff0c;去掉渲染部分即可。 下方是…

Linux---vim编辑器(续写)

5. vim正常模式命令集 插入模式 按「i」切换进入插入模式「insert mode」&#xff0c; 按“i”进入插入模式后是从光标当前位置开始输入文件&#xff1b; 按「a」进入插入模式后&#xff0c;是从目前光标所在位置的下一个位置开始输入文字&#xff1b; 按「o」进入插入模式…

从头开始学Spring—01Spring介绍和IOC容器思想

目录 1.Spring介绍 1.1Spring概述 1.2特性 1.3五大功能模块 2.IOC容器 2.1IOC思想 ①获取资源的传统方式 ②反转控制方式获取资源 ③DI 2.2IOC容器在Spring中的实现 ①BeanFactory ②ApplicationContext ③ApplicationContext的主要实现类 1.Spring介绍 1.1Sprin…

Linux系统一步一脚印式学习

Linux操作系统具有许多特点和优势。首先&#xff0c;它是开放源代码的&#xff0c;也就意味着任何人都可以对源代码进行查看和修改。其次&#xff0c;可以同时支持多个用户且可以同时执行多个任务&#xff0c;此外&#xff0c;Linux操作系统也非常稳定和安全。相对于其他操作系…

安全测试|常见SQL注入攻击方式、影响及预防

SQL注入 什么是SQL注入&#xff1f; SQL注入是比较常见的网络攻击方式之一&#xff0c;主要攻击对象是数据库&#xff0c;针对程序员编写时的疏忽&#xff0c;通过SQL语句&#xff0c;实现无账号登录&#xff0c;篡改数据库。 SQL注入简单来说就是通过在表单中填写包含SQL关键…

SSD-60S施耐德电机保护器EOCR-SSD

EOCR主要产品有电子式电动机保护继电器&#xff0c;电子式过电流继电器&#xff0c;电子式欠电流继电器&#xff0c;电子式欠电压继电器&#xff0c;其它保护装置&#xff0c;电流互感器。EOCR-SSD 10-60A电机保护器 系列型号: EOCRSSD-05SEOCRssD-30s EOCRSSD-60SEOCRSSD-0…

开源即时通讯IM框架 MobileIMSDK v6.5 发布

一、更新内容简介 本次更新为次要版本更新&#xff0c;进行了bug修复和优化升级&#xff08;更新历史详见&#xff1a;码云 Release Notes、Github Release Notes&#xff09;。 MobileIMSDK 可能是市面上唯一同时支持 UDPTCPWebSocket 三种协议的同类开源IM框架。轻量级、高…

8种常见的CMD命令

1.怎么打开CMD窗口 步骤1&#xff1a;winr 步骤2&#xff1a;在弹出的窗口输入cmd&#xff0c;然后点击确认&#xff0c;就会出现一个cmd的窗口 2.CMD的8种常见命令 2.1盘符名称冒号 说明&#xff1a;切换盘的路径 打开CMD窗口这里默认的是C盘的Users的27823路径底下&#xf…

基于微信小程序+JAVA Springboot 实现的【网上商城小程序】app+后台管理系统 (内附设计LW + PPT+ 源码+ 演示视频 下载)

项目名称 项目名称&#xff1a; 基于微信小程序的网上商城 项目技术栈 该项目采用了以下核心技术栈&#xff1a; 后端框架/库&#xff1a; Java, SSM框架数据库&#xff1a; MySQL前端技术&#xff1a; 微信开发者工具&#xff0c;微信小程序框架 项目展示 5.1 管理员服务…

Mat: Unknown HPROF Version

问题&#xff1a;Mat 加载 android studio 导出的 hprof 文件失败 原因&#xff1a;android hprof 文件不是标准的 java hprof 文件 解决办法&#xff1a; 使用 android sdk 自带的命令将 hprof 转换成标准的 java hprof

瞬息全宇宙——穿越之旅终极教程,手把手教你做出百万点赞视频

最近一种叫“瞬息全宇宙”的视频火了&#xff0c;抖音一期视频百万赞&#xff0c;各个博主视频都在带瞬息全宇宙这个标签&#xff0c;于是就有很多朋友催我出教程了&#xff0c;在琢磨了几天之后&#xff0c;终于整出来了 教程包含了插件的安装&#xff0c;界面的讲解&#xff…

生产制造行业推拉式生产的复合应用

一、案例分析&#xff08;汽配行业&#xff09; 重点&#xff1a; 1. MTO/MTS 与 PUSH/PULL 有关系但是不是充分关系 2. MTO/MTS 是公司经营策略&#xff0c;更多是对市场需求的经营策略&#xff0c;体现在生产时机上的不同&#xff0c;一个是等客户需求&#xff0c;一个是填…

HTML4(三):表单

文章目录 表单1. 基本结构2. 常用表单控件2.1 文本输入框2.2 密码输入框2.3 单选框2.4 复选框2.5 隐藏域2.6 提交按钮2.7 重置按钮2.8 普通按钮2.9 文本域2.10 下拉框2.11 示例 3. 禁用表单控件4. lable标签5. fieldset与legend标签6. 总结 表单 概念&#xff1a;一种包含交互…

Raft论文阅读笔记+翻译:In Search of Understandable Consensus Algorithm

In Search of Understandable Consensus Algorithm 摘要 Raft是一种管理复制日志的共识算法。它产生与&#xff08;多&#xff09;Paxos等效的结果&#xff0c;并且与Paxos一样高效&#xff0c;但其结构与Paxos不同。这使得Raft比Paxos更易理解&#xff0c;也为构建实际系统提供…

近屿OJAC带你解读:什么是大模型幻觉?

忠实性幻觉也可以细分&#xff0c;分为指令不一致&#xff08;输出偏离用户指令&#xff09;、上下文不一致&#xff08;输出与上下文信息不符&#xff09;、逻辑不一致三类&#xff08;推理步骤以及与最终答案之间的不一致&#xff09;。 具体解析 大模型产生幻觉的原因可能…

国内使用 CloudFlare 避坑指南

最近明月收到了不少新手使用 CloudFlare 的求助,发现很多首次使用 CloudFlare 的甚至包括已经在使用 CloudFlare 的站长们对 CloudFlare 的使用有很多的误区,再加上国内简中互联网上有关 CloudFlare 的教程良莠不齐,更是加深了新手使用 CloudFlare 入坑的概率,让一些别有用…

Today At Apple 20240512 学习拍照

文章目录 微距打开模式设置曝光值人像模式设置光模式实况 官网&#xff1a; https://www.apple.com/today/Apple 亚洲第一大商店&#xff1a;Apple 静安零售店现已在上海开幕如下预约课程&#xff1a;下载apple store&#xff08;不是app store&#xff09;&#xff0c;点击课程…

进程间的IPC通信机制

一、介绍 进程与进程间的用户空间相互独立&#xff0c;内核空间共享。 1.传统的进程间通信机制 a.无名管道 pipe b.有名管道 fifo c.信号 signal 2.system V中的IPC对象 a.消息队列 message queue b.共享内存 shared memory c.信号灯集 semaphoare 3.可用于跨主机传输…