【Linux网络】网络套接字socket
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12891150.html
目录
Socket 编程预备
理解源 IP 地址和目的 IP 地址
认识端口号
端口号范围划分
理解 "端口号" 和 "进程 ID"
理解源端口号和目的端口号
理解 socket
传输层的典型代表
TCP 协议
UDP 协议
网络字节序
socket 编程接口
sockaddr 结构
Udp网络编程
echo server-版本1
InetAddr.hpp
LockGuard.hpp
Log.hpp
nocopy.hpp
UdpClientMain.cc
UdpServer.hpp
UdpServerMain.cc
DictServer-版本2
Dict.hpp
dict.txt
UdpServer.hpp
UdpServerMain.cc
简单聊天室-版本3
Route.hpp
Thread.hpp
ThreadPool.hpp
UdpClientMain.cc
UdpServer.hpp
UdpServerMain.cc
地址转换函数
前言
💬 hello! 各位铁子们大家好哇。
今日更新了Linux网络编程的内容
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
Socket 编程预备
理解源 IP 地址和目的 IP 地址
- IP 在网络中,用来标识主机的唯一性
问题:数据传输到主机是目的吗?不是的。因为数据是给人用的。比如:聊天是人在聊天,下载是人在下载,浏览网页是人在浏览?但是人是怎么看到聊天信息的呢?怎么执行下载任务呢?怎么浏览网页信息呢?通过启动的 qq,迅雷,浏览器。 而启动的 qq,迅雷,浏览器都是进程。换句话说,进程是人在系统中的代表,只要把数据给进程,人就相当于就拿到了数据。 所以:数据传输到主机不是目的,而是手段。到达主机内部,在交给主机内的进程,才是目的。 但是系统中,同时会存在非常多的进程,当数据到达目标主机之后,怎么转发给目标进程?
认识端口号
端口号(port)是传输层协议的内容.
- 端口号是一个 2 字节 16 位的整数;
- 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
- IP 地址 + 端口号能够标识网络上的某一台主机的某一个进程;
- 一个端口号只能被一个进程占用
网络通信的本质就是进程间通信
端口号范围划分
- 0 - 1023: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的端口号都是固定的.
- 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的
理解 "端口号" 和 "进程 ID"
系统编程中, pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这两者之间是怎样的关系?
进程 ID 属于系统概念,技术上也具有唯一性,确实可以用来标识唯一的一个进程,但是这样做,会让系统进程管理和网络强耦合,实际设计的时候,并没有选择这样做。
另外, 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定;
理解源端口号和目的端口号
传输层协议(TCP 和 UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 "数据是谁发的, 要发给谁"
理解 socket
- 综上,IP 地址用来标识互联网中唯一的一台主机,port 用来标识该主机上唯一的一个网络进程
- IP+Port 就能表示互联网中唯一的一个进程
- 所以,通信的时候,本质是两个互联网进程代表人来进行通信,{srcIp,srcPort,dstIp,dstPort}这样的 4 元组就能标识互联网中唯二的两个进程
- 所以,网络通信的本质,也是进程间通信
- 我们把 ip+port 叫做套接字 socket
传输层的典型代表
传输层是属于内核的,那么我们要通过网络协议栈进行通信,必定调用的是传输层提供的系统调用,来进行的网络通信。
TCP 协议
对 TCP(Transmission Control Protocol 传输控制协议)的一个直观的认识:
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
UDP 协议
对 UDP(User Datagram Protocol 用户数据报协议)的一个直观的认识:
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
网络字节序
内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分.
- 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
- 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
- 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
- TCP/IP 协议规定,网络数据流应采用大端字节序,即低地址高字节.
- 不管这台主机是大端机还是小端机, 都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据;
- 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可
为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
- 这些函数名很好记,h 表示 host,n 表示 network,l 表示 32 位长整数,s 表示16 位短整数。
- 例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
- 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
- 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
socket 编程接口
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockaddr 结构
socket API 是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及 UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.
- IPv4 和 IPv6 的地址格式定义在 netinet/in.h 中,IPv4 地址用sockaddr_in 结构体表示,包括 16 位地址类型, 16 位端口号和 32 位 IP 地址.
- IPv4、IPv6 地址类型分别定义为常数 AF_INET、AF_INET6. 这样,只要取得某种 sockaddr 结构体的首地址,不需要知道具体是哪种类型的 sockaddr 结构体,就可以根据地址类型字段确定结构体中的内容.
- socket API 可以都用 struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收 IPv4, IPv6, 以及UNIX DomainSocket 各种类型的 sockaddr 结构体指针做为参数;
sockaddr_in用于网络通信,sockaddr_un用于本地通信。
sockaddr结构体根据传入的指针指向的对象是AF_INET还是AF_UNIX,会进行网络通信或者本地通信。这样就可以使用同一套socket API来设计更通用的可以兼顾网络通信和本地通信的一套接口。
Udp网络编程
参数1是域,可以填入上面红框中的选项,AF_UNIX表示这个套接字创建好后在本地网络进行通信。如果填AF_INET表示使用网络协议来实现网络通信。
参数2是套接字类型,SOCK_DGRAM表示用户数据报套接字(无连接,不可靠),也就是udp套接字。
参数3表示协议编号,一般设为0即可。
返回值:成功则返回新的文件描述符,失败则返回-1。
参数1是套接字,参数2的结构体是套接字信息。bind的作用就是把套接字信息跟套接字关联起来。
上面的这个函数可以把字符串风格的ip地址转成4字节,并且转成网络序列的ip。
该函数的作用是收消息。参数1是文件描述符;参数2是缓冲区,读到的数据就放在这里;参数3是缓冲区大小;参数4flags表示阻塞标记位,这里设为0,表示阻塞读取;参数5是输入输出型参数,他把数据放到缓冲区中,并且我们也能由此知道发送方是谁;参数6就是参数5的大小;返回值是实际收到多少个字节。
sendto的作用是发信息。通过sockfd把指定的字符串buf发送给dest_addr。
云服务器上,服务端不能直接(也强烈不建议)bind自己的公网ip。因为云服务器的公网ip是虚拟出来的,云服务器上根本没有对应的公网ip。
云服务器上的服务端ip一般bind为0。这样服务端就bind了任意IP。
如果服务器上有两个ip地址ip1和ip2,上层bind的端口号为8888。如果服务器bind了ip1和8888,未来收到各种发给8888的报文,此时只会收到ip1和8888这组套接字上的信息。如果服务器端bind的ip为0,不管收到哪些报文,只要它发送的目标端口是8888的,全都能收到。
这个接口能直接把4字节地址转字符串。
echo server-版本1
简单的回显服务器和客户端代码
InetAddr.hpp
#pragma once#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>class InetAddr
{
private:void ToHost(const struct sockaddr_in &addr) //主机转本地地址{_port=ntohs(addr.sin_port);_ip=inet_ntoa(addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr):_addr(addr){ToHost(addr);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}
private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};
LockGuard.hpp
#pragma once#include <pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};
Log.hpp
#pragma once#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include <pthread.h>
#include "LockGuard.hpp"namespace log_ns
{enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};std::string LevelToString(int level){switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetCurrTime(){time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curr_time->tm_year + 1900,curr_time->tm_mon + 1,curr_time->tm_mday,curr_time->tm_hour,curr_time->tm_min,curr_time->tm_sec);return buffer;}class logmessage{public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;};#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;// log.logMessage("", 12, INFO, "this is a %d message ,%f, %s hellwrodl", x, , , );class Log{public:Log(const std::string &logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage &lg){printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());}void FlushLogToFile(const logmessage &lg){std::ofstream out(_logfile, std::ios::app);if (!out.is_open())return;char logtxt[2048];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());out.write(logtxt, strlen(logtxt));out.close();}void FlushLog(const logmessage &lg){// 加过滤逻辑 --- TODOLockGuard lockguard(&glock);switch (_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void logMessage(std::string filename, int filenumber, int level, const char *format, ...){logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber = filenumber;lg._curr_time = GetCurrTime();va_list ap;va_start(ap, format);char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap);va_end(ap);lg._message_info = log_info;// 打印出来日志FlushLog(lg);}~Log(){}private:int _type;std::string _logfile;};Log lg;#define LOG(Level, Format, ...) \do \{ \lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \} while (0)
#define EnableScreen() \do \{ \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE() \do \{ \lg.Enable(FILE_TYPE); \} while (0)
};
nocopy.hpp
#pragma onceclass nocopy
{
public:nocopy(){}~nocopy(){}nocopy(const nocopy&) =delete;const nocopy& operator=(const nocopy&) =delete;
};
UdpClientMain.cc
#include<iostream>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>//客户端在未来一定要知道服务器IP地址和端口号
// ./udp_client server-ip server-port
// ./udp_client 127.0.0.1 8888
int main(int argc,char* argv[])
{if(argc!=3){std::cerr<<"Usage: "<<argv[0]<<"server-ip server-port"<<std::endl;exit(0);}std::string serverip=argv[1];uint16_t serverport=std::stoi(argv[2]);int sockfd=::socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){std::cerr<<"create socket error"<<std::endl;exit(1);}//client的端口号,一般不让用户自己设定,而是让client OS随机选择//client需要bind它自己的IP和端口,但是client不需要 “显示” bind它自己的IP和端口//client在首次向服务器发送数据的时候,OS会自动给client bind它自己的IP和端口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());while(1){std::string line;std::cout<<"Please Enter# ";std::getline(std::cin,line);int n=sendto(sockfd,line.c_str(),line.size(),0,(struct sockaddr*)&server,sizeof(server));//你要发送消息,你得知道你要发给谁if(n>0){struct sockaddr_in temp;socklen_t len=sizeof(temp);char buffer[1024];int m=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);if(m>0){buffer[m]=0;std::cout<<buffer<<std::endl;}else{std::cout<<"recvfrom error"<<std::endl;break;}}else{std::cout<<"sendto error"<<std::endl;break;}}::close(sockfd);return 0;
}
UdpServer.hpp
#pragma once #include<iostream>
#include<unistd.h>
#include<string>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>#include"nocopy.hpp"
#include"Log.hpp"
#include"InetAddr.hpp"using namespace log_ns;static const int gsockfd=-1;
static const uint16_t glocalport=8888;enum {SOCKET_ERROR=1,BIND_ERROR
};//UdpServer user("192.168.1.1",8899)
class UdpServer : public nocopy
{
public:UdpServer(uint16_t localport=glocalport):_sockfd(gsockfd),_localport(localport),_isrunning(false){}void InitServer(){//1.创建socket文件_sockfd=::socket(AF_INET,SOCK_DGRAM,0);if(_sockfd<0){LOG(FATAL,"socket error/n");exit(SOCKET_ERROR);}LOG(DEBUG,"socket create success, _sockfd: %d\n",_sockfd);//3//2. bindstruct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_localport);// local.sin_addr.s_addr=inet_addr(_localip.c_str());//1.需要4字节ip 2.需要网络序列的ip--暂时local.sin_addr.s_addr=INADDR_ANY;//服务器端,进行任意ip地址绑定int n=::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n<0){LOG(FATAL,"bind error\n");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success\n");}void Start(){_isrunning=true;char inbuffer[1024];while(_isrunning){struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&peer,&len);if(n>0){InetAddr addr(peer);inbuffer[n]=0;std::cout<<"["<<addr.Ip()<<":"<<addr.Port()<<"]# "<<inbuffer<<std::endl;std::string echo_string="[udp_server echo] #";echo_string+=inbuffer; sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,(struct sockaddr*)&peer,len);}else{std::cout<<"recvfrom,error"<<std::endl;}}}~UdpServer(){if(_sockfd>gsockfd) ::close(_sockfd);}
private:int _sockfd;uint16_t _localport;// std::string _localip;//TODO:后面要专门处理这个ipbool _isrunning;
};
UdpServerMain.cc
#include"UdpServer.hpp"#include<memory>// ./udp_server local-port
// ./udp_server 8888
int main(int argc,char* argv[])
{if(argc!=2){std::cerr<<"Usage: "<<argv[0]<<"local-port"<<std::endl;exit(0);}uint16_t port=std::stoi(argv[1]);EnableScreen();std::unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(port);usvr->InitServer();usvr->Start();return 0;
}
运行结果图:
DictServer-版本2
实现一个简单的英译汉的网络字典
仅需修改部分版本1和添加部分代码,其余没变化代码不再显示。
Dict.hpp
#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<unordered_map>
#include<unistd.h>
#include"Log.hpp"using namespace log_ns;const static std::string sep=": ";class Dict
{
private:void LoadDict(const std::string& path){std::ifstream in(path);if(!in.is_open()){LOG(FATAL,"open %s failed!\n",path.c_str());exit(0);}std::string line;while(std::getline(in,line)){LOG(DEBUG,"load info: %s ,success\n",line.c_str());if(line.empty()) continue;auto pos=line.find(sep);if(pos==std::string::npos) continue;std::string key=line.substr(0,pos);if(key.empty()) continue;std::string value=line.substr(pos+sep.size());if(value.empty()) continue;_dict.insert(std::make_pair(key,value));}LOG(INFO,"load %s done\n",path.c_str());in.close();}public:Dict(const std::string& dict_path):_dict_path(dict_path){LoadDict(_dict_path);}std::string Translate(std::string word){if(word.empty()) return "None";auto iter=_dict.find(word);if(iter==_dict.end()) return "None";else return iter->second;}~Dict(){}
private:std::unordered_map<std::string,std::string> _dict;std::string _dict_path;
};
dict.txt
apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天
UdpServer.hpp
#pragma once #include<iostream>
#include<unistd.h>
#include<string>
#include<cstring>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>#include"nocopy.hpp"
#include"Log.hpp"
#include"InetAddr.hpp"using namespace log_ns;static const int gsockfd=-1;
static const uint16_t glocalport=8888;enum {SOCKET_ERROR=1,BIND_ERROR
};using func_t =std::function<std::string(std::string)>;//UdpServer user("192.168.1.1",8899)
class UdpServer : public nocopy
{
public:UdpServer(func_t func,uint16_t localport=glocalport):_func(func),_sockfd(gsockfd),_localport(localport),_isrunning(false){}void InitServer(){//1.创建socket文件_sockfd=::socket(AF_INET,SOCK_DGRAM,0);if(_sockfd<0){LOG(FATAL,"socket error/n");exit(SOCKET_ERROR);}LOG(DEBUG,"socket create success, _sockfd: %d\n",_sockfd);//3//2. bindstruct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_localport);// local.sin_addr.s_addr=inet_addr(_localip.c_str());//1.需要4字节ip 2.需要网络序列的ip--暂时local.sin_addr.s_addr=INADDR_ANY;//服务器端,进行任意ip地址绑定int n=::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n<0){LOG(FATAL,"bind error\n");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success\n");}void Start(){_isrunning=true;char inbuffer[1024];while(_isrunning){struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&peer,&len);if(n>0){InetAddr addr(peer);inbuffer[n]=0;//一个一个的单词std::cout<<"["<<addr.Ip()<<":"<<addr.Port()<<"]# "<<inbuffer<<std::endl;std::string result=_func(inbuffer);sendto(_sockfd,result.c_str(),result.size(),0,(struct sockaddr*)&peer,len);}else{std::cout<<"recvfrom,error"<<std::endl;}}}~UdpServer(){if(_sockfd>gsockfd) ::close(_sockfd);}
private:int _sockfd;uint16_t _localport;// std::string _localip;//TODO:后面要专门处理这个ipbool _isrunning; func_t _func;
};
UdpServerMain.cc
#include"UdpServer.hpp"
#include"Dict.hpp"#include<memory>// ./udp_server local-port
// ./udp_server 8888
int main(int argc,char* argv[])
{if(argc!=2){std::cerr<<"Usage: "<<argv[0]<<"local-port"<<std::endl;exit(0);}uint16_t port=std::stoi(argv[1]);EnableScreen();Dict dict("./dict.txt");func_t translate=std::bind(&Dict::Translate,&dict,std::placeholders::_1);std::unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(translate,port);usvr->InitServer();usvr->Start();return 0;
}
运行结果如下图:
简单聊天室-版本3
Route.hpp
#pragma once#include<iostream>
#include<string>
#include<vector>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<pthread.h>
#include"InetAddr.hpp"
#include"ThreadPool.hpp"
#include"LockGuard.hpp"using task_t =std::function<void()>;class Route
{
public:Route(){pthread_mutex_init(&_mutex,nullptr);}void CheckOnlineUser(InetAddr& who){LockGuard lockguard(&_mutex);for(auto& user:_online_user){if(user==who){LOG(DEBUG,"%s is exists\n",who.AddrStr().c_str());return;}}LOG(DEBUG,"%s is not exists,add it\n",who.AddrStr().c_str());_online_user.push_back(who);}void Offline(InetAddr& who){LockGuard lockguard(&_mutex);auto iter=_online_user.begin();for(;iter!=_online_user.end();iter++){if(*iter==who){LOG(DEBUG,"%s is offline\n",who.AddrStr().c_str());_online_user.erase(iter);break;}}}void ForwardHelper(int sockfd,const std::string message,InetAddr who){LockGuard lockguard(&_mutex);std::string send_message="["+who.AddrStr()+"]#"+message;for(auto& user:_online_user){struct sockaddr_in peer =user.Addr();LOG(DEBUG,"Forward message to %s,message is %s\n",user.AddrStr().c_str(),send_message.c_str());::sendto(sockfd,send_message.c_str(),send_message.size(),0,(struct sockaddr*)&peer,sizeof(peer));} }void Forward(int sockfd,const std::string& message,InetAddr& who)//转发{//1.该用户是否在 在线用户列表中?如果在,什么都不做;如果不在,自动添加到_online_userCheckOnlineUser(who);//1.1 message == "QUIT" "Q"if(message=="QUIT" || message== "Q"){Offline(who);}//2.who 一定在_online_user列表里面//ForwardHelper(sockfd,message);task_t t=std::bind(&Route::ForwardHelper,this,sockfd,message,who);ThreadPool<task_t>::GetInstance()->Equeue(t);}~Route(){pthread_mutex_destroy(&_mutex);}
private:std::vector<InetAddr> _online_user;pthread_mutex_t _mutex;
};
Thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>namespace ThreadMoudle
{// 线程要执行的方法,后面我们随时调整// typedef void (*func_t)(ThreadData *td); // 函数指针类型// typedef std::function<void()> func_t;using func_t = std::function<void(const std::string&)>;class Thread{public:void Excute(){_isrunning = true;_func(_name);_isrunning = false;}public:Thread(const std::string &name, func_t func):_name(name), _func(func){}static void *ThreadRoutine(void *args) // 新线程都会执行该方法!{Thread *self = static_cast<Thread*>(args); // 获得了当前对象self->Excute();return nullptr;}bool Start(){int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this);if(n != 0) return false;return true;}std::string Status(){if(_isrunning) return "running";else return "sleep";}void Stop(){if(_isrunning){::pthread_cancel(_tid);_isrunning = false;}}void Join(){::pthread_join(_tid, nullptr);}std::string Name(){return _name;}~Thread(){}private:std::string _name;pthread_t _tid;bool _isrunning;func_t _func; // 线程要执行的回调函数};
} // namespace ThreadModle
ThreadPool.hpp
#pragma once#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include "Thread.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"using namespace ThreadMoudle;
using namespace log_ns;static const int gdefaultnum = 5;void test()
{while (true){std::cout << "hello world" << std::endl;sleep(1);}
}template <typename T>
class ThreadPool
{
private:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void Wakeup(){pthread_cond_signal(&_cond);}void WakeupAll(){pthread_cond_broadcast(&_cond);}void Sleep(){pthread_cond_wait(&_cond, &_mutex);}bool IsEmpty(){return _task_queue.empty();}void HandlerTask(const std::string &name) // this{while (true){// 取任务LockQueue();while (IsEmpty() && _isrunning){_sleep_thread_num++;LOG(INFO, "%s thread sleep begin!\n", name.c_str());Sleep();LOG(INFO, "%s thread wakeup!\n", name.c_str());_sleep_thread_num--;}// 判定一种情况if (IsEmpty() && !_isrunning){UnlockQueue();LOG(INFO, "%s thread quit\n", name.c_str());break;}// 有任务T t = _task_queue.front();_task_queue.pop();UnlockQueue();// 处理任务t(); // 处理任务,此处不用/不能在临界区中处理// std::cout << name << ": " << t.result() << std::endl;//LOG(DEBUG, "hander task done, task is : %s\n", t.result().c_str());}}void Init(){func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);for (int i = 0; i < _thread_num; i++){std::string threadname = "thread-" + std::to_string(i + 1);_threads.emplace_back(threadname, func);LOG(DEBUG, "construct thread %s done, init success\n", threadname.c_str());}}void Start(){_isrunning = true;for (auto &thread : _threads){LOG(DEBUG, "start thread %s done.\n", thread.Name().c_str());thread.Start();}}ThreadPool(int thread_num = gdefaultnum): _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}ThreadPool(const ThreadPool<T> &) = delete;void operator=(const ThreadPool<T> &) = delete;public:void Stop(){LockQueue();_isrunning = false;WakeupAll();UnlockQueue();LOG(INFO, "Thread Pool Stop Success!\n");}// 如果是多线程获取单例呢?static ThreadPool<T> *GetInstance(){if (_tp == nullptr){LockGuard lockguard(&_sig_mutex);if (_tp == nullptr){LOG(INFO, "create threadpool\n");// thread-1 thread-2 thread-3...._tp = new ThreadPool<T>();_tp->Init();_tp->Start();}else{LOG(INFO, "get threadpool\n");}}return _tp;}void Equeue(const T &in){LockQueue();if (_isrunning){_task_queue.push(in);if (_sleep_thread_num > 0)Wakeup();}UnlockQueue();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}private:int _thread_num;std::vector<Thread> _threads;std::queue<T> _task_queue;bool _isrunning;int _sleep_thread_num;pthread_mutex_t _mutex;pthread_cond_t _cond;// 单例模式// volatile static ThreadPool<T> *_tp;static ThreadPool<T> *_tp;static pthread_mutex_t _sig_mutex;
};template <typename T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;
template <typename T>
pthread_mutex_t ThreadPool<T>::_sig_mutex = PTHREAD_MUTEX_INITIALIZER;
UdpClientMain.cc
#include<iostream>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"Thread.hpp"using namespace ThreadMoudle;int InitClient()
{int sockfd=::socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){std::cerr<<"create socket error"<<std::endl;exit(1);}return sockfd;
}void RecvMessage(int sockfd,const std::string &name)
{while(true){struct sockaddr_in peer;socklen_t len=sizeof(peer);char buffer[1024];int n=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(n>0){buffer[n]=0;std::cerr<<buffer<<std::endl;}else{std::cerr<<"recvfrom error"<<std::endl;break;}}
}void SendMessage(int sockfd,std::string serverip,uint16_t serverport,const std::string &name)
{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());std::string cli_profix=name+"#";//sender-thread# 你好while(true){std::string line;std::cout<<cli_profix;std::getline(std::cin,line);int n=sendto(sockfd,line.c_str(),line.size(),0,(struct sockaddr*)&server,sizeof(server));if(n<=0)break;}
}int main(int argc,char* argv[])
{ if(argc!=3){std::cerr<<"Usage: "<<argv[0]<<"server-ip server-port"<<std::endl;exit(0);}std::string serverip=argv[1];uint16_t serverport=std::stoi(argv[2]);int sockfd=InitClient();Thread recver("recver-thread",std::bind(&RecvMessage,sockfd,std::placeholders::_1));Thread sender("sender-thread",std::bind(&SendMessage,sockfd,serverip,serverport,std::placeholders::_1)); recver.Start();sender.Start();recver.Join();sender.Join();::close(sockfd);return 0;
}
UdpServer.hpp
#pragma once #include<iostream>
#include<unistd.h>
#include<string>
#include<cstring>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>#include"nocopy.hpp"
#include"Log.hpp"
#include"InetAddr.hpp"using namespace log_ns;static const int gsockfd=-1;
static const uint16_t glocalport=8888;enum {SOCKET_ERROR=1,BIND_ERROR
};using service_t =std::function<void(int,const std::string& message,InetAddr& who)>;//UdpServer user("192.168.1.1",8899)
//一般服务器主要是用来进行网络数据读取和写入的,即IO的
//服务器IO逻辑 和 业务逻辑 解耦
class UdpServer : public nocopy
{
public:UdpServer(service_t func,uint16_t localport=glocalport):_func(func),_sockfd(gsockfd),_localport(localport),_isrunning(false){}void InitServer(){//1.创建socket文件_sockfd=::socket(AF_INET,SOCK_DGRAM,0);if(_sockfd<0){LOG(FATAL,"socket error/n");exit(SOCKET_ERROR);}LOG(DEBUG,"socket create success, _sockfd: %d\n",_sockfd);//3//2. bindstruct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_localport);// local.sin_addr.s_addr=inet_addr(_localip.c_str());//1.需要4字节ip 2.需要网络序列的ip--暂时local.sin_addr.s_addr=INADDR_ANY;//服务器端,进行任意ip地址绑定int n=::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n<0){LOG(FATAL,"bind error\n");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success\n");}void Start(){_isrunning=true;char message[1024];while(_isrunning){struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_sockfd,message,sizeof(message)-1,0,(struct sockaddr*)&peer,&len);if(n>0){InetAddr addr(peer);message[n]=0;LOG(DEBUG,"[%s]# %s\n",addr.AddrStr().c_str(),message);_func(_sockfd,message,addr);LOG(DEBUG,"return udpserver\n");}else{std::cout<<"recvfrom,error"<<std::endl;}}}~UdpServer(){if(_sockfd>gsockfd) ::close(_sockfd);}
private:int _sockfd;//读写都用同一个sockfd,说明:udp是全双工通信的uint16_t _localport;// std::string _localip;//TODO:后面要专门处理这个ipbool _isrunning; service_t _func;
};
UdpServerMain.cc
#include"UdpServer.hpp"
#include"Route.hpp"#include<memory>// ./udp_server local-port
// ./udp_server 8888
int main(int argc,char* argv[])
{if(argc!=2){std::cerr<<"Usage: "<<argv[0]<<"local-port"<<std::endl;exit(0);}uint16_t port=std::stoi(argv[1]);EnableScreen();Route messageRoute;service_t message_route=std::bind(&Route::Forward,&messageRoute,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3);std::unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(message_route,port);usvr->InitServer();usvr->Start();return 0;
}
地址转换函数
上面代码中,我们用的地址转换函数是inet_ntoa,在InetAddr.hpp中。
inet_ntoa 这个函数返回了一个 char*, 很显然是这个函数自己在内部为我们申请了一块内存来保存 ip 的结果. 那么是否需要调用者手动释放呢?
man 手册上说, inet_ntoa 函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放.
如果我们调用多次这个函数, 会有什么样的效果呢?
因为 inet_ntoa 把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆盖掉上一次的结果
在多线程环境下, 推荐使用 inet_ntop, 这个函数由调用者提供一个缓冲区保存结果, 可以规避线程安全问题;
src就是4字节的ip地址,dst就是自己传的缓冲区,size就是缓冲区的大小。
使用示范:
相关文章:
【Linux网络】网络套接字socket
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343 🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12891150.html 目录 Socket 编程预备 理解源 IP 地址和目的 IP 地址 认识端口号 端口号范围划分…...
ubuntu wifi配置(命令行版本)
1、查询当前设备环境的wifi列表 nmcli dev wifi list2、连接wifi nmcli dev wifi connect "MiFi-SSID" password "Password" #其中MiFi-SSID是wifi的密码,Password是wifi的密码3、查看连接情况 nmcli dev status...
配环境的经验
pip install -e . 该命令用于以“编辑模式”(也称为开发模式)安装当前目录下的 Python 包,比如包含有 setup.py、setup.cfg 或 pyproject.toml 文件的项目-e 是 --editable 的简写。以编辑模式安装时,pip 会在你的 Python 环境中创…...
STM32cubmax配置STM32407VET6,实现网络通信
文章目录 一、开发准备1、硬件准备2、软件准备 二、STM32CubeMX工程配置步骤1、创建新工程 三、外设配置步骤1)调试接口(SWD)配置2)时钟配置3)串口(USART)配置4)IO口配置(…...
LeetCode 热题 100_完全平方数(84_279_中等_C++)(动态规划(完全背包))
LeetCode 热题 100_完全平方数(84_279) 题目描述:输入输出样例:题解:解题思路:思路一(动态规划(完全背包)): 代码实现代码实现(思路一…...
【C++】vector的底层封装和实现
目录 目录前言基本框架迭代器容量第一个测试,野指针异常第二轮测试,浅拷贝的问题 元素访问修改操作push_backinsert迭代器失效问题 erase 默认成员函数构造函数双重构造引发调用歧义 拷贝构造赋值重载析构函数 源码end 目录 前言 废话不多说࿰…...
AI前端组件库Ant DesIgn X
Ant Design X AI:体验新秩序 Ant Design 团队精心打造 RICH 设计范式,为 AI 界面提供卓越解决方案,引领智能交互新体验。 设计语言与理论 官网: Ant Design X - 轻松打造 AI 驱动的界面。 AI 设计范式 —— RICH 是我们在蚂蚁…...
BGP路由协议之解决 IBGP 水平分割带来的问题
主要有以下 3 种方案: 全互联 :配置量大、耗费资源联邦: 配置量大、邻居会重建、中断时间较长RR 路由反射器:目前主流使用、简单、好用 联邦 IBGP 水平分割问题用与防止 AS 内部产生环路,在很大程度上杜绝了 IBGP 路…...
基于Java的人脸识别在线考试系统(jsp+springboot+mysql8.x)
基于Java的人脸识别在线考试系统(jspspringbootmysql8.x) 在线考试系统提供全面的考试管理和用户管理功能。登录界面支持管理员、教师和学生三种身份验证,确保不同用户访问相应的功能模块。系统自动组卷功能允许管理员根据不同科目和题型,如单选题、多选…...
如何对LLM大型语言模型进行评估与基准测试
基础概念 这几年,随着生成式 AI 和大型语言模型(LLMs)的兴起,AI 领域整体迎来了一波大爆发。 随着各种基于 LLM 的应用程序在企业里落地,人们开始需要评估不同推理部署方案的性价比。 LLM 应用的部署成本,…...
C语言内存函数和数据在内存的存储
一、内存操作函数深度解析 函数名原型核心特性典型应用场景注意事项memcpyvoid* memcpy(void* dest, const void* src, size_t num)内存块无重叠复制,性能高数组拷贝、结构体复制1. 必须确保目标空间足够 2. 不支持重叠内存(用memmove替代) …...
ChatGPT之智能驾驶问题讨论
ChatGPT之智能驾驶问题讨论 1. 源由2. 问题:2.1 智能驾驶级别定义🚗 L2(部分自动化,Partial Automation)🤖 L3(有条件自动化,Conditional Automation)🛸 L4&a…...
【PalladiumZ2 使用专栏 1 -- 波形 trigger 抓取详细介绍】
文章目录 Palladium Z2 OverviewPalladium 波形抓取Palladium 波形存放文件创建Palladium Trigger 断点设置Palladium 加探针并 dumpPalladium 波形查看 Palladium Z2 Overview Cadence Palladium Z2 是 Cadence 推出的企业级硬件仿真加速平台,旨在应对复杂 SoC 设…...
elasticsearch 8设置验证登录查询
最近总是困扰于9200网络勒索,老是在捣乱,动不动给我清理了index,实在是费劲,今天研究了下config配置,设置ca验证。 以下是完整的步骤和配置,确保生成的证书文件与elasticsearch.yml的配置一致: 1. 生成CA证书 运行以下命令生成CA证书:让输入账号或密码请直接回车。 …...
为什么使用了CDN源服务器需要关闭防火墙?
在网站运营过程中,不少站长会遇到这样的困惑:当使用 CDN 源服务器时,好像就得关闭源服务器的防火墙,不然就状况百出。这背后究竟是什么原因呢? 当你在浏览网页时,要是看到 “502 - 服务暂时不可用” 的提…...
Android 学习之 Navigation导航
1. Navigation 介绍 Navigation 组件 是 Android Jetpack 的一部分,用于简化应用内导航逻辑,支持 Fragment、Activity 和 Compose 之间的跳转。核心优势: 单 Activity 架构:减少 Activity 冗余,通过 Fragment 或 Com…...
初识 Three.js:开启你的 Web 3D 世界 ✨
3D 技术已经不再是游戏引擎的专属,随着浏览器技术的发展,我们完全可以在网页上实现令人惊艳的 3D 效果。而 Three.js,作为 WebGL 的封装库,让 Web 3D 的大门向更多开发者敞开了。 这是我开启这个 Three.js 专栏的第一篇文章&…...
PyTorch 笔记
简介与安装 PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由C实现,应用于人工智能领域,如计算机视觉和自然语言处理。 PyTorch 最初由 Meta Platforms 的人工智能研究团队开发,现在属 于Linux 基金会的…...
day24学习Pandas库
文章目录 三、Pandas库4.函数计算3遍历3.1.遍历Series对象3.2.遍历DataFrame对象 4排序4.1 sort_index4.2 sort_values 5.去重drop_duplicates6.先分组在计算6.1 groupby6.2 filter过滤 7.合并未完待续.. 三、Pandas库 4.函数计算 3遍历 3.1.遍历Series对象 在讲解Series部…...
AI日报 - 2025年4月8日
AI日报 - 2025年4月8日 🌟 今日概览(60秒速览) ▎🤖 模型进展 | Llama 4发布引爆讨论 (性能、应用、部署、训练争议),OpenAI保持高速迭代,香港大学推Dream 7B扩散模型。 Meta Llama 4 Scout & Maveric…...
Linux学习笔记(2) 命令基础:从概念到实践(期末,期中复习笔记全)
前言 一、认识命令行与命令 二、Linux 命令的基础格式 三、命令示例解析 (1)ls -l /home/itheima (2)cp -r test1 test2 四结语 前言 在 Linux 系统的世界里,命令行是与系统交互的重要方式。熟练掌握 Linux 命令…...
langgraph简单Demo4(checkpoint检查点)
在 langgraph 里,检查点(checkpoint)是一项重要的功能,它能够记录工作流在执行过程中的中间状态。当工作流因某些原因中断时,可以从检查点恢复继续执行,避免从头开始,提升效率。 示例ÿ…...
【题解】AtCoder AT_abc400_c 2^a b^2
题目大意 我们定义满足下面条件的整数 X X X 为“好整数”: 存在一个 正整数 对 ( a , b ) (a,b) (a,b) 使得 X 2 a ⋅ b 2 X2^a\cdot b^2 X2a⋅b2。 给定一个正整数 N N N( 1 ≤ N ≤ 1 0 18 1\le N\le 10^{18} 1≤N≤1018)ÿ…...
七种驱动器综合对比——《器件手册--驱动器》
目录 九、驱动器 概述 定义 功能 分类 1. 按负载类型分类 2. 按功能特性分类 工作原理 优势 应用领域 详尽阐述 1 隔离式栅极驱动器 定义 工作原理 应用场景 优势 2 变压器驱动器 定义 工作原理 应用场景 优势 设计注意事项 3 LED驱动 定义 功能与作用 应用场景 设计…...
GStreamer开发笔记(一):GStreamer介绍,在windows平台部署安装,打开usb摄像头对比测试
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/147049923 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、O…...
西湖大学团队开源SaProt等多款蛋白质语言模型,覆盖结构功能预测/跨模态信息搜索/氨基酸序列设计等
2025 年 3 月 22—23 日,上海交通大学「AI 蛋白质设计峰会」正式举行。 本次峰会汇聚了来自清华大学、北京大学、复旦大学、浙江大学、厦门大学等知名高校的 300 多位专家学者,以及 200 余位行业领军企业代表和技术研发人员,深入探讨了 AI 在…...
ansible+docker+docker-compose快速部署4节点高可用minio集群
目录 github项目地址 示例服务器列表 安装前 修改变量文件group_vars/all.yml 修改ansible主机清单 修改setup.sh安装脚本 用法演示 安装后验证 github项目地址 https://github.com/sulibao/ansible_minio_cluster.git 示例服务器列表 安装前 修改变量文件group_var…...
说话人分离中的聚类方法:深入解析Agglomerative聚类、KMeans聚类和Oracle聚类
说话人分离(Speaker Diarization)是将音频流根据说话人身份划分为同质片段的过程。这一过程中的关键步骤是聚类,即将说话人嵌入(embeddings)分组为不同的簇,每个簇代表一个独特的说话人。在pyannote.audio管…...
蓝桥杯真题——前缀总分、遗迹
蓝桥杯2024年第十五届省赛真题-前缀总分 题目描述 给定 n 个由小写英文字母组成的字符串 s1, s2, , sn ,定义前缀总分为V ∑i<j P(si, sj) ,其中 P(si, sj) 表示 si, sj 的最长公共前缀的长度。 小蓝可以选择其中一个字符串,并修改其…...
性能比拼: MySQL vs PostgreSQL
本内容是对知名性能评测博主 Anton Putra MySQL vs PostgreSQL Performance Benchmark (Latency - Throughput - Saturation) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 MySQL vs PostgreSQL 数据库性能对比** 在本内容中,我们将对比 MySQL 和 Pos…...
TypeScript 中的 infer 关键字用途
infer 是 TypeScript 中的高级类型关键字,主要用于条件类型中推断类型。它允许我们在条件类型的 extends 子句中声明一个类型变量,然后在该条件类型的 true 分支中使用这个推断出的类型。 1. 基本语法 type SomeType<T> T extends infer U ? U…...
关于Spring MVC中@RequestParam注解的详细说明,用于在前后端参数名称不一致时实现参数映射。包含代码示例和总结表格
以下是关于Spring MVC中RequestParam注解的详细说明,用于在前后端参数名称不一致时实现参数映射。包含代码示例和总结表格: 1. 核心作用 RequestParam用于显式绑定HTTP请求参数到方法参数,支持以下场景: 参数名不一致࿱…...
Spring Boot中Spring MVC相关配置的详细描述及表格总结
以下是Spring Boot中Spring MVC相关配置的详细描述及表格总结: Spring MVC 配置项详解 1. 异步请求配置 spring.mvc.async.request-timeout 描述:设置异步请求的超时时间(单位:毫秒)。默认值:未设置&…...
Shell脚本编程之正则表达式
一、概念 在 Shell 脚本中,正则表达式是一种强大且常用的文本处理工具,它可以用来匹配、搜索、替换和截取字符串。 正则表达式是由一些字符去描述规则,在正则表达式中有两类字符 (1)元字符(Meta Character):Shell 环境中具有特殊含…...
spring-ai-openai调用Xinference1.4.1报错
1、Xinference 报错logs 此处是调用 /v1/chat/completions 接口 2025-04-06 15:48:51 xinference | return await dependant.call(**values) 2025-04-06 15:48:51 xinference | File "/usr/local/lib/python3.10/dist-packages/xinference/api/restful_api.py", …...
XC7K160T-2FFG676I Kintex‑7系列 Xilinx 赛灵思 FPGA 详细技术规格
XC7K160T-1FFG676I XC7K160T-1FFG676C XC7K160T-2FFG676C 1. 基本概述 XC7K160T-2FFG676I 属于 Xilinx Kintex‑7 系列 FPGA,该系列芯片采用 28nm (HKMG)工艺制造,旨在提供高性能与低功耗的平衡。该芯片主要面向对高速数据处理、…...
C++学习之udp通信
1.UDP特点 c /* udp 传输层协议, 和tcp是一样的 特点: 面向无连接的, 不安全的, 报式传输协议 1. 无连接: udp通信的时候不需要connect 1). 通信不需要建立连接 2). 如果想给对方发送数据, 只需要指定对方的IP和端口 2. udp会丢包 1). 数…...
2020年-全国大学生数学建模竞赛(CUMCM)试题速浏、分类及浅析
2020年-全国大学生数学建模竞赛(CUMCM)试题速浏、分类及浅析 全国大学生数学建模竞赛(China Undergraduate Mathematical Contest in Modeling)是国家教委高教司和中国工业与应用数学学会共同主办的面向全国大学生的群众性科技活动,目的在于激励学生学习数学的积极性,提高学…...
【数据标准】数据标准化实施流程与方法-保障机制篇
导读:1、数据标准化保障机制(组织架构、协作流程)是战略落地的基石,确保责权分明与资源协同;2、数据标准化制度建设(政策、标准、工具)构建了统一治理框架,规范数据…...
ZLMediaKit部署与配置
ZLMediaKit编译 # 安装编译器 sudo apt install build-essential cmake# 其它依赖库 sudo apt-get install libssl-dev libsdl-dev libavcodec-dev libavutil-dev ffmpeg git cd /usr/local/srcgit clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit.git cd ZLMediaKit# …...
38、web前端开发之Vue3保姆教程(二)
三、Vue3语法详解 1、组件 1 什么是组件? 组件是 Vue.js 中最重要的概念之一。它是一种可复用的 Vue 实例,允许我们将 UI 拆分为独立的、可复用的部分。组件可以提高代码的组织性和可维护性。 2 创建组件 在 Vue 3 中,组件通常使用单文件组件(SFC)编写,其包含三个主…...
知识中台如何重构企业信息生态?关键要素解析
在信息化快速发展的时代,企业面临着如何高效整合和管理知识资源的挑战。知识中台作为企业信息管理的核心工具,正在帮助企业提升运营效率和创新力。本文将探讨知识中台如何重构企业信息生态,并解析其关键要素。 一、什么是知识中台?…...
蓝桥杯python组备赛(记录个人模板)
文章目录 栈队列堆递归装饰器并查集树状数组线段树最近公共祖先LCAST表字典树KMPmanacher跳表(代替C STL的set)dijkstra总结 栈 用list代替 队列 用deque双端队列替代 堆 用heapq 递归装饰器 众所周知,python的递归深度只有1000,根本满足不了大部…...
C++的多态 - 下
目录 多态的原理 虚函数表 1.计算包含虚函数类的大小 2.虚函数表介绍 多态底层原理 1.父类引用调用 2.父类指针调用 3.动态绑定与静态绑定 单继承和多继承关系的虚函数表 函数指针 1.函数指针变量 (1)函数指针变量创建 (2)函数指针变量的使用 (3)两段有趣的代码 …...
XSS(跨站脚本攻击)
什么是 XSS 攻击? XSS 攻击(Cross-Site Scripting)是一种常见的网络攻击手段,攻击者通过在网站上注入恶意的 JavaScript 代码,让网站在用户的浏览器中执行这些恶意代码,进而达到 窃取信息、篡改网页内容 或…...
LLM Agents的历史、现状与未来趋势
引言 大型语言模型(Large Language Model, LLM)近年在人工智能领域掀起革命,它们具备了出色的语言理解与生成能力。然而,单纯的LLM更像是被动的“回答者”,只能根据输入给出回复。为了让LLM真正“行动”起来ÿ…...
最简rnn_lstm模型python源码
1.源码 GitCode - 全球开发者的开源社区,开源代码托管平台 不到120行代码,参考了《深度学习与交通大数据实战》3.2节。注意这本书只能在京东等在线商城网购,才能拿到相应的数据集和源码。我的是在当地新华书店买的——买清华出版社,记得这个…...
基于Android的图书借阅和占座系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 基于Android的图书借阅和占座系统设计的目的是为用户提供图书信息、图书馆、图书资讯等内容,用户可以进行图书借阅、预约选座等操作。 与PC端应用程序相比,图书借阅和占座系统的设计主要面向于广大用户,旨在为用户提供一个图书借阅及占…...
vue3+element-plus动态与静态表格数据渲染
一、表格组件: <template> <el-table ref"myTable" :data"tableData" :header-cell-style"headerCellStyle" header-row-class-name"my-table-header" cell-class-name"my-td-cell" :row-style"r…...
数据库50个练习
数据表介绍 --1.学生表 Student(SId,Sname,Sage,Ssex) --SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 --2.课程表 Course(CId,Cname,TId) --CId 课程编号,Cname 课程名称,TId 教师编号 --3.教师表 Teacher(TId,Tname) --TId 教师编号,Tname 教师姓名 --4.成绩…...