C++socket基础进阶:Select与封装

c++ socket简单封装 简单封装 并不实际应用 框架图 阅读详情

之前用C#做服务器没搞明白于是从笔者比较熟悉的C++开始入手从头学了一遍,整理一下笔记。
资料来源于《网络多人游戏架构与编程》第三章,这本书讲的很明白,比起网上每篇博客都在介绍的原理,这本书更偏向于代码实现。
代码应该没什么大问题,看懂逻辑的话能自己封装的更好。

上一篇介绍Socket基础函数的在这。

阻塞和非阻塞I/O

开启非阻塞模式

默认情况下,socket操作是阻塞模式,但可以由如下函数转变为非阻塞模式,非阻塞模式的socket被要求执行一个需要阻塞的操作时,它将立刻返回-1,还设置了错误代码errno或WSAGetLastError,分别返回EAGAIN或WSAEWOULDBLOCK。这个代码表示之前的socket行为已经阻塞,没有发生就被终止了。

//Windows系统
int ioctlsocket(
	SOCKET s,
	long cmd, //用于控制socket参数,在这种情况下,输入FIONBIO
	u_long* argp //是这个参数的取值,任意非零值将开启非阻塞模式,0将阻止开启
);
//POSIX兼容系统
int fcntl(
    int sock,
    int cmd, //发给socket的命令
    ...
);

在更新的POSIX系统上,必须首先使用F_GETEL获取当前与socket相关的标志,让它们与常数O_NONBLOCK按位或运算之后,使用F_SETEL命令更新socket上的标志。

当socket出于非阻塞模式,调用任何阻塞函数都是安全的,因为我们知道如果它不能在没有阻塞的情况下完成,它会立刻返回。

Select

socket库提供了同时检查多个socket的方式,只要其中有一个socket准备好了就开始执行:

int select(
	int nfds,
	fd_set* readfds,
	fd_set* writefds,
	fd_set* exceptfds,
	const timeval* timeout
);

参数:

  1. nfds在POSIX平台,是待检查的编号最大的socket的标识符。在POSIX平台,每一个socket只是一个整数,所以直接将所有socket的最大值传入这个函数。在Windows平台,socket表示为指针,而不是整数,所以这个参数不起作用,可以忽略。
  2. readfds是指向socket集合的指针,称为fd_set,包含要检查可读性的socket。
  3. writefds是指向fd_set的指针,存储待检查可写性的socket。当select函数返回时,保留在writefds中所有socket都保证可写,不会引起调用线程的阻塞。给writefds传入nullptr来跳过任何socket可写性的检查。通常,只有当socket的输出缓冲区有太多数据时,socket才会阻塞写操作。
  4. excptfds是指向fd_set的指针,这个fd_set存储待检查错误的socket。当select函数返回,保留在exceptfds中的所有socket都已经发生了错误。给exceptfds传入nullptr来跳过任何错误的检查。
  5. timeout是指向超时之前可以等待最长时间的指针。如果在readfds中的任意一个socket可读,writefds中的任意一个socket可写,或者exceptfds中的任意一个socket发生错误之前发生超时,清空所有集合,select函数将控制返回给调用线程。给timeout输入nullptr来表名没有超时限制。

select函数返回执行之后保留在readfds、writefds和exceptfds中socket的数量。如果发生超时,这个值是0.

fd_set:

fd_set myReadSet;
FD_ZERO(&myReadSet);	//初始化一个空的fd_set
FD_SET(mySocket, &myReadSet);	//给fd_set添加一个socket
FD_INSET(mySocket, &myReadSet);	//检查在select函数返回值后,一个socket是否在fd_set中

封装

类型安全的SocketAddress类

class SocketAddress {
public:
	SocketAddress(uint32_t inAddress, uint16_t inPort) {
		GetAsSockAddrIn()->sin_family = AF_INET;
		GetAsSockAddrIn()->sin_addr.S_un.S_addr = htonl(inAddress);
		GetAsSockAddrIn()->sin_port = htons(inPort);
	}
	SocketAddress(const sockaddr& inSockAddr) {
		memcpy(&mSockAddr, &inSockAddr, sizeof(sockaddr));
	}
	size_t GetSize() const { return sizeof(sockaddr); }
private:
	sockaddr mSockAddr;
	sockaddr_in* GetAsSockAddrIn() {
		return reinterpret_cast<sockaddr_in*> (&mSockAddr);
	}
};
typedef std::shared_ptr<SocketAddress> SocketAddressPtr;

使用SocketAddressFactory类的域名解析

class SocketAddressFactory {
public:
	static SocketAddressPtr CreateIPv4FromString(const std::string& inString) {
		auto pos = inString.find_last_of(':');
		std::string host, service;
		if (pos != std::string::npos) {
			host = inString.substr(0, pos);
			service = inString.substr(pos + 1);
		}
		else {
			host = inString;
			//use default port
			service = "0";
		}
		addrinfo hint;
		memset(&hint, 0, sizeof(hint));
		hint.ai_family = AF_INET;
		addrinfo* result;
		int error = getaddrinfo(host.c_str(), service.c_str(), &hint, &result);
		if (error != 0) {
			freeaddrinfo(result);
			return nullptr;
		}
		while (!result->ai_addr && result->ai_next)result = result->ai_next;
		if (!result->ai_addr) {
			freeaddrinfo(result);
			return nullptr;
		}
		auto toRet = std::make_shared<SocketAddress>(*result->ai_addr);
		freeaddrinfo(result);
		return toRet;
	}
};

类型安全的UDP Socket

class UDPSocket {
public:
	~UDPSocket();
	int Bind(const SocketAddress& inToAddress);
	int SendTo(const void* inData, int inLen, const SocketAddress& inTo);
	int ReceiveFrom(void* inBuffer, int inLen, SocketAddress& outFrom);
private:
	UDPSocket(SOCKET inSocket) : mSocket(inSocket) {}
	SOCKET mSocket;
};
typedef std::shared_ptr<UDPSocket> UDPSocketPtr;

int UDPSocket::Bind(const SocketAddress& inBindAddress) {
	int err = bind(mSocket, &inBindAddress.mSockAddr, inBindAddress.GetSize());
	if (err != 0) {
		//return error from UDPSocket::Bind
		return -1;
	}
	return 0;
}
int UDPSocket::SendTo(const void* inData, int inLen, const SocketAddress& inTo) {
	int byteSentCount = sendto(mSocket, static_cast<const char*>(inData), inLen, 0, &inTo.mSockAddr, inTo.GetSize());
	if (byteSentCount >= 0) return byteSentCount;
	//return error from UDPSocket::SendTo
	return -1;
}
int UDPSocket::ReceiveFrom(void* inBuffer, int inLen, SocketAddress& outFrom) {
	int fromLength = outFrom.GetSize();
	int readByteCount = recvfrom(
		mSocket,
		static_cast<char*> (inBuffer),
		inLen,
		0,
		&outFrom.mSockAddr,
		&fromLength
	);
	if (readByteCount >= 0) return readByteCount;
	//return error from UDPSocket::ReceiveFrom
	return -1;
}
UDPSocket::~UDPSocket() {
	closesocket(mSocket);
}

类型安全的TCP Socket

class TCPSocket {
public:
	~TCPSocket();
	int Connect(const SocketAddress& inAddress);
	int Bind(const SocketAddress& inToAddress);
	int Listen(int inBackLog = 32);
	std::shared_ptr<TCPSocket> Accept(SocketAddress& inFromAddress);
	int Send(const void* inData, int inLen);
	int Receive(void* inBuffer, int inLen);
private:
	TCPSocket(SOCKET inSocket) : mSocket(inSocket) {}
	SOCKET mSocket;
};
typedef std::shared_ptr<TCPSocket> TCPSocketPtr;

int TCPSocket::Connect(const SocketAddress& inAddress) {
	int err = connect(mSocket, &inAddress.mSockAddr, inAddress.GetSize());
	if (err < 0) return -1;
	return NO_ERROR;
}
int TCPSocket::Listen(int inBackLog) {
	int err = listen(mSocket, inBackLog);
	if (err < 0)return -1;
	return NO_ERROR;
}
int TCPSocket::Send(const void* inData, int inLen) {
	int bytesSendCount = send(
		mSocket,
		static_cast<const char*> (inData),
		inLen, 0
	);
	if (bytesSendCount < 0)return -1;
	return bytesSendCount;
}
int TCPSocket::Receive(void* inBuffer, int inLen) {
	int bytesReceiveCount = recv(
		mSocket,
		static_cast<char*> (inBuffer),
		inLen, 0
	);
	if (bytesReceiveCount < 0)return -1;
	return bytesReceiveCount;
}
TCPSocket::~TCPSocket() {
	closesocket(mSocket);
}

与类型安全的TCPSocket一起使用的select函数

fd_set* FillSetFromVector(fd_set& outSet,
	const std::vector<TCPSocketPtr>* inSockets) {
	if (inSockets) {
		FD_ZERO(&outSet);
		for (const TCPSocketPtr& socket : *inSockets)
			FD_SET(socket->mSocket, &outSet);
		return &outSet;
	}
	return nullptr;
}

void FillVectorFromSet(
	std::vector<TCPSocketPtr>* outSockets,
	const std::vector<TCPSocketPtr>* inSockets,
	const fd_set& inSet
) {
	if (inSockets && outSockets) {
		outSockets->clear();
		for (const TCPSocketPtr& socket : *inSockets)
			if (FD_ISSET(socket->mSocket, &inSet))
				outSockets->push_back(socket);
	}
}

int Select(
	const std::vector<TCPSocketPtr>* inReadSet,
	std::vector<TCPSocketPtr>* outReadSet,
	const std::vector<TCPSocketPtr>* inWriteSet,
	std::vector<TCPSocketPtr>* outWriteSet,
	const std::vector<TCPSocketPtr>* inExceptSet,
	std::vector<TCPSocketPtr>* outExceptSet
) {
	fd_set read, write, except;
	fd_set* readPtr = FillSetFromVector(read, inReadSet);
	fd_set* writePtr = FillSetFromVector(read, inWriteSet);
	fd_set* exceptPtr = FillSetFromVector(read, inExceptSet);

	int toRet = select(0, readPtr, writePtr, exceptPtr, nullptr);
	if (toRet > 0) {
		FillVectorFromSet(outReadSet, inReadSet, read);
		FillVectorFromSet(outWriteSet, inWriteSet, write);
		FillVectorFromSet(outExceptSet, inExceptSet, except);
	}
	return toRet;
}

运行一个TCP服务器循环

void DoTCPLoop() {
	TCPSocketPtr listenSocket = CreateTCPSocket(INET);
	SocketAddress receivingAddres(INADDR_ANY, 48000);
	if (listenSocket->Bind(receivingAddres) != NO_ERROR)return;
	std::vector<TCPSocketPtr> readBlockSockets;
	readBlockSockets.push_back(listenSocket);
	std::vector<TCPSocketPtr> readableSockets;
	while (true) {
		if (Select(&readBlockSockets, &readableSockets,
			nullptr, nullptr,
			nullptr, nullptr
		)) {
			for (const TCPSocketPtr& socket : readableSockets) {
				if (socket == listenSocket) {
					SocketAddress newCilentAddress;
					TCPSocketPtr newSocket = listenSocket->Accept(newCilentAddress);
					readBlockSockets.push_back(newSocket);
					// 在这了处理新连入 ProcessNewCilent(newSocket, newCilentAddress);
				}
				else {
					char segment[MAX_SEGMENT_SIZE];
					int dataReceived = socket->Receive(segment, MAX_SEGMENT_SIZE);
					if (dataReceived > 0) {
						//在这里处理新数据 ProcessDataFromClient(socket, segment, dataReceived);
					}
				}
			}
		}
	}
}
C++网络编程实战:基于TCP的消息边界处理与Socket封装 网络编程中,TCP作为面向字节流的传输层协议,虽然保证了数据的可靠有序传输,但无法自动识别应用层消息边界,这导致了粘包和拆包问题。为了解决这一核心挑战,开发者需要在TCP之上设计自定义的应用层协议,常见方案包括固定长度、分隔符和头部长度字段等方法。其中,基于长度字段的TLV(Type-Length-Value)模式因其高效和灵活而被广泛采用,它通过明确的消息头定义,确保了数据的完整解析。这种底层协议设计能力是构建稳定网络应用的基础,对于理解gRPC、Redis等上层框架的通信机制至关重要。本文通过一个C++ 阅读详情

相关推荐

跨平台C++ Socket封装库:轻量级TCP通信解决方案与实战

网络编程是构建分布式系统和网络应用的基础,其核心在于实现不同主机间的进程通信。TCP作为可靠的传输层协议,通过三次握手建立连接、流量控制和拥塞控制机制保证数据传输的可靠性。然而,直接使用原生Socket API进行跨平台开发面临诸多挑战,如Windows的WSAStartup与Linux的socket初始化差异、粘包拆包处理、连接超时管理以及多线程并发等复杂问题。这些底层细节不仅增加开发难度,也影响代码的可维护性和可移植性。为此,封装一个轻量级、跨平台的Socket库具有重要技术价值,它能屏蔽平台差异,提供

weixin_29195217的博客 358

网络多人游戏架构与编程-源码-源代码

git地址 https://github.com/MultiplayerBook/MultiplayerBook 好气啊,找个东西全都传到csdn上了,下载还要积分,就不能传到百度云上面吗,又不是商业的东西,烦,搞个打赏一毛钱起不就可以了吗!!!!! ...

lingdi2000的专栏 2757

C++ Socket编程实战:从零实现Telnet客户端

Socket是网络编程的基础,它提供了进程间通信的端点,通过TCP/IP协议栈实现可靠的数据传输。其核心原理在于建立连接、收发数据和管理连接生命周期。掌握原生Socket API对于深入理解网络通信机制、调试复杂网络问题具有重要价值,尤其在开发高性能服务器、游戏服务端等场景中必不可少。本文聚焦于**C++ Socket编程**和**Telnet协议解析**,通过实现一个完整的Telnet客户端,将网络通信、字节流处理、协议状态机等核心概念串联起来,为开发者构建坚实的网络编程基础。

weixin_30435261的博客 406

c++socket的select函数以及多路复用

c++socket的select函数以及多路复用

qq_44913716的博客 2680

现代C++封装inotify与select实现高效文件监控方案

文件系统监控是Linux开发中的常见需求,传统轮询方案实时性差且资源消耗大。inotify作为内核提供的事件驱动机制,能够高效监控文件变化并即时通知应用。结合I/O多路复用技术如select,可以实现单线程同时监控多个文件描述符,构建非阻塞的异步事件处理模型。这种方案在现代C++工程实践中,通过RAII机制和类型安全封装,将底层系统调用转化为安全易用的高级抽象,广泛应用于日志分析、配置热重载等场景。本文深入探讨了如何利用select与inotify实现一套符合现代C++设计哲学的文件监控组件,涵盖核心封装、

weixin_30258901的博客 407

C++跨平台Socket封装:从Winsock到Linux的差异与实战

网络编程中,跨平台通信是服务端开发的常见需求,但C++标准库并不包含网络API,导致Windows与Linux下的Socket接口存在明显差异。Windows依赖Winsock2,而Linux沿用BSD Socket,两者在头文件、句柄类型、错误处理、初始化及关闭方式上均不相同,直接套用代码常导致编译失败或运行异常。解决这类问题的核心思路是通过一层轻量封装抽象平台差异,将sock_init、sock_close、非阻塞设置等接口统一起来,使业务逻辑无需关注底层系统细节。这种封装不仅适合Windows服务端向

weixin_30983353的博客 173

【多人在线游戏架构实战-基于C++的分布式游戏编程】开篇

学习一门语言很久了,却从来没有用它做过项目,刚学的时候用它来写过一个黑白通讯录,后来又常常用它来刷题。会了Java以后,刷题也不愿意去用这门语言了,没错它就是C++,一把锋利的瑞士军刀。这个国庆节期间,我要定时更新这本《多人在线游戏架构实战-基于C++的分布式游戏编程》的笔记,用来给学生时代的C++留下一个美好的回忆。本系列笔记的内容均在 deepin 20.7 环境下完成。2022-10-1于深圳。

凤凰于飞 2989

C++ Socket封装实战:跨平台设计、RAII与超时控制

网络编程是后端开发的核心技能之一,而C++中的Socket API在不同平台上的差异常让开发者踩坑。封装一套可复用的Socket类,不仅是为了统一接口,更是为了用RAII管理句柄生命周期、用状态机约束误用、用超时机制避免阻塞失控。本文从基础概念出发,梳理Socket封装的核心设计原理,涵盖接口映射、跨平台条件编译、TCP流式协议处理、非阻塞模式及错误码标准化等关键技术,并给出生产环境下的避坑指南。无论你是初学网络编程的新手,还是需要构建可靠网络底层的工程师,这套实践都能帮你少走弯路,快速落地稳定的通信模块。

weixin_29188043的博客 94

C++网络编程之select

select函数决定一个或者多个套接字(socket)的状态,如果需要的话,等待执行异步I/O。 int select( __in int nfds, __inout fd_set *readfds, __inout fd_set *writefds, ...

weixin_30849403的博客 510

C++ Socket封装实战:UDP广播边界处理与避坑指南

网络编程中,Socket是应用层与传输层之间的通用接口,C++开发者绕不开Winsock API的底层细节。UDP广播通信则是局域网内一对多消息分发的轻量方案,核心原理在于通过SO_BROADCAST选项打开发送许可,借助广播地址与固定端口完成数据投递。工程实践中,端口绑定一致性、缓冲区按返回长度截断、多网卡接口选择等边界条件,常常决定广播功能能否稳定运行。这类技术常用于设备发现、服务通告、小范围消息同步等场景。一份可复用的Socket封装,能将这些易错点收敛为简洁的调用接口,降低反复排查API参数的成本。

weixin_30133813的博客 268

大型多人在线游戏开发,大型多人在线游戏开发 pdf,C,C++

大型多人在线游戏开发文档设计流程和方案!

C++ Socket编程实战:基于select实现高效多客户端服务端

I/O多路复用是网络编程中解决高并发连接的核心技术,其原理是通过单个进程同时监视多个文件描述符,当任一描述符就绪时,内核通知进程进行读写操作,从而避免为每个连接创建独立线程带来的资源消耗和上下文切换开销。这项技术能显著提升系统的并发处理能力和资源利用率,广泛应用于游戏服务器、即时通讯、数据采集等需要同时处理大量连接的场景。在具体实现中,select作为最经典且跨平台的I/O多路复用模型,通过fd_set管理套接字集合,配合SO_REUSEADDR选项可有效解决“Address already in use”

weixin_30372371的博客 513

IPSec Over GRE和GRE Over IPSec技术

IPSec Over GRE 和 GRE Over IPSec 一. 技术介绍 IPsec     主要作用是对数据进行加密,因为他能提供所有有时候被单独用作实现加密的一种方法!IPsec建立的是一个逻辑隧道,并不是真正意义上的隧道!并且不能提供路由功能,因为IPsec不支持非ip流量,也不支持广播(组播)! GRE     (通用路由封装)能很好的提供一个真正意义上的点对点的隧道,GRE是一种三层VPN封装技术。GRE可以对某些网络层协议(如IPX、Apple Talk、IP等)的报文进行封装,使封装后

qq_56228347的博客 9870

WSL2环境C++ TCP聊天服务器实战:select多路复用与Socket编程详解

网络编程是构建分布式系统的核心技术,其核心在于实现不同主机间的进程通信。TCP/IP协议栈提供了可靠的字节流传输服务,而Socket API则是应用层与传输层之间的编程接口。通过Socket编程,开发者可以创建客户端-服务器模型的应用,实现数据交换。在并发处理方面,I/O多路复用技术如select、poll和epoll能够有效管理多个连接,提升服务器性能。这些技术在高并发服务器、实时通信系统和物联网网关等场景中具有重要价值。本文聚焦于在WSL2环境中,使用C++和select机制实现一个支持多客户端的TCP

weixin_30570101的博客 371

VM与欧姆龙PLC通讯设置实战:Fins TCP/UDP协议详解

本文详细解析了VM(VisionMaster)与欧姆龙PLC通过Fins TCP/UDP协议进行通讯的实战设置。内容涵盖网络配置、协议选择(TCP可靠性与UDP实时性)、VM端地址映射与数据点添加、以及数据接收解析流程,并提供了常见错误排查与高级调试技巧,帮助视觉与自动化工程师快速实现稳定可靠的工业通讯集成。

weixin_29172963的博客 329

高频功率放大器的设计实现

摘 要 随着无线通信技术的高速发展,市场对射频电路的需求越来越大,同时对射频电路的性能要求也越来越高。高频功率放大器是位于无线发射级末端的重要部件,为了弥补信号在无线传输过程中的衰耗,要求发射机具有较大功率的输出,通信距离越远,要求的输出功率越大。在高频范围内,为了获得足够大的高频输出功率,就要采用高频功率放大器。 本次课程设计的高频功率放大器的技术指标包括输出功率、效率、功率增益、由于高频频功率放大器的输出功率比较大,耗能比较多,所以工作效率就显得比较重要。本课程设计的高频功率放大器电路由两极功率放大器

qq_43543515的博客 3万+

C++网络编程Socket封装

关于C++网络编程简单功能的的socket封装 server端#pragma once class Socket { public: Socket(char* ip,int port) { this->ip = ip; this->port = port; //绑定套接字 } Socket() { }

hello_bravo_的博客 2640

产业大脑+未来工厂:揭秘浙江数字经济系统的5大核心场景搭建指南

本文深入解析了“产业大脑+未来工厂”这一浙江数字经济系统的核心范式,并提供了五大核心场景的实战搭建指南。文章详细阐述了从供应链协同、产能共享到产品创新、质量追溯及绿色低碳等关键场景的构建逻辑与技术架构,旨在帮助企业实现从技术驱动到场景驱动的价值落地,破解数据孤岛难题,提升产业链整体效能。

mac99的博客 667

C++游戏服务器框架笔记(四)_封装Select

Windows下IO多路复用-select()的介绍和封装统一接口

木可的博客 1531

【AutoSAR】【以太网】TCP/IP 栈详述与配置

目录 一、TCP/IP栈架构 二、TCP\IP栈的可扩展性 三、以太网协议V4 四、以太网协议 V6 五、基于IP的协议 六、函数调用序列图 一、TCP/IP栈架构 根据以上架构图,该栈主要包括:UPD\TCP\DHCP\ICMP\IPV4\IPV6\ARP; 二、TCP\IP栈的可扩展性 TCP\IP栈为适应不同的应用场景需求,将分为三类: 配置工程里的配置项: 三、以太网协议V4 1 IPV4 Internet Protocol (IP)是T.

专注汽车软件开发、AutoSAR、车载以太网、SOA、EE架构。 5696

SOUL.md 人格定制的 7 个高阶技巧:Hermes Agent 精准控制 AI 行为模式

揭示 SOUL.md 的本质是行为契约而非人格设定,介绍7个围绕边界约束的高阶技巧,包括用上下文锚点(INPUT SCOPE/OUTPUT TARGET/CONSTRAINTS)替代角色描述、动态渲染锚点适配CI流程等,显著降低token消耗与人工review耗时。

weixin_42520239的博客 251

Linux C++ UDP客户端封装:从socket系统调用到面向对象设计实践

在网络编程中,UDP(用户数据报协议)作为一种无连接的传输层协议,以其低延迟和轻量级特性,在实时通信、音视频传输和游戏同步等场景中发挥着关键作用。其核心原理是通过数据包直接发送,无需建立连接,牺牲了可靠性以换取更高的传输效率。在Linux环境下,使用C++进行UDP编程通常涉及socket、bind、sendto和recvfrom等底层系统调用。通过面向对象设计封装这些调用,可以构建高内聚、低耦合的UDP客户端类,这不仅提升了代码的可维护性和复用性,还体现了RAII(资源获取即初始化)等现代C++工程实践思

weixin_30509393的博客 430

学Simulink——基于新型动力与推进系统的场景实例:混合动力无人机的续航能力优化

通过本次 Simulink 仿真,你已掌握:✅混合动力系统架构设计✅内燃机、电机、电池联合建模✅三种能量管理策略(规则型、功率跟随、优化型)✅续航能力仿真与对比分析✅参数调优与优化方法核心收获混合动力是突破续航瓶颈的有效路径能量管理策略(EMS)决定系统效率电池SOC管理是关键Simulink 支持复杂多能源系统的协同仿真掌握混合动力无人机技术,你将具备设计长航时巡检机、应急通信平台、边境巡逻系统的能力。

xiaoheshang_123的博客 100

C++服务器(六):socket 异步模型与select 的实现

之前在另一篇博客上提到一些关于socket 的异步模型的资料,其中有一篇博客写得很详细,在此附上链接: socket阻塞与非阻塞,同步与异步、I/O模型[1]这篇博客已经讲得很好了。但是我还是觉得,有必要的话,应该捧个书本系统地探究一下socket 异步模型的区别和实现。在这里,我选择的实现是使用select 模型。 原因如下: 服务器目前只是个人使用,所以,流量并不会很大,少数的socket

小键233 9803

数据仓库数据质量监控和处理方法最佳实践

指的是检查数据是否存在反常或者错误的情况,例如数值反常地过大或者过小,或者超过记录的波动值,对应的规则包括数值波动检查,最大值、最小值检查,跨表准确性等。指的是检查数据是否存在缺失的情况,这里的缺失可以是整个数据集的记录缺失,也可以是某个字段记录的缺失,对应的规则包括空值检查,表总行数检查。指的是检查数据是否符合规范,是否按照规定的格式存储,对应的规则包括正则表达式检查,字段长度检查,枚举集合检查,数值范围等。指的是检查哪些数据是重复数据或者数据的哪些属性是重复的,对应的规则例如主键唯一性检查。

大模型大数据攻城狮的专栏 898

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Linux下基于C++11的socket网络编程(基础版本)

Linux下,基于C++11的网络编程-基础版本

RMB20150321的博客 5557

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

上一篇: Redis——商品秒杀之库存遗留问题
下一篇: 关于Matrix工作室第九届换届事宜的安排
matrix_studio
博客等级 码龄5年 106粉丝 81原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值