当前位置: 首页 > article >正文

二、Socket 编程 TCP

Socket 编程 TCP一、TCP 编程整体认识TCP 是面向连接的可靠传输协议。和 UDP 不同UDP 可以直接 sendto/recvfrom 收发数据而 TCP 通信之前必须先建立连接。TCP 服务端基本流程socket() - bind() - listen() - accept() - read/write - close()TCP 客户端基本流程socket() - connect() - write/read - close()服务端中有两个非常重要的 socket_listenSock监听 socket只负责获取新连接 sockfd通信 socket由 accept 返回负责和某个客户端通信。accept() 是 TCP 服务端非常关键的接口。监听 socket 并不直接和客户端通信它只是负责等待连接。真正与客户端通信的是 accept() 返回的新 socket。客户端一般不需要显式 bind()因为客户端不需要固定端口。当客户端调用 connect() 时操作系统会自动为客户端分配本地 IP 和临时端口。二、TCP 常用接口说明1. socketint socket(int domain, int type, int protocol);TCP 使用socket(AF_INET, SOCK_STREAM, 0);含义AF_INETIPv4 SOCK_STREAM面向字节流也就是 TCP 0使用默认协议。2. bindint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);服务器需要绑定固定 IP 和端口。否则客户端不知道连接哪里。常见写法local.sin_family AF_INET; local.sin_port htons(port); local.sin_addr.s_addr htonl(INADDR_ANY);INADDR_ANY 表示绑定本机任意 IP。3. listenint listen(int sockfd, int backlog);listen() 把 socket 设置为监听状态。只有调用 listen() 后服务器才可以接收客户端连接。4. acceptint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);accept() 从监听队列中获取一个已经建立好的连接并返回新的通信 socket。5. connectint connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);客户端通过 connect() 连接服务器。connect() 填的是服务器地址不是自己的地址。6. read/writeTCP 连接建立后可以像读写文件一样读写 socketread(sockfd, buffer, sizeof(buffer)); write(sockfd, data.c_str(), data.size());但是 TCP 是面向字节流的不保证一次 write() 对应一次完整 read()。这就是后面要解决的粘包问题。三、服务器版本演进V1单进程版本主进程 accept() 一个连接后直接调用 Service() 处理客户端。优点是简单适合理解 TCP 基本流程。缺点是一次只能服务一个客户端。如果当前客户端不退出服务器就无法继续处理其他客户端。V2多进程版本父进程只负责 accept()每来一个客户端就创建子进程处理。优点是多个客户端可以并发处理。缺点是进程创建成本较高并且需要处理子进程回收问题否则会产生僵尸进程。V3多线程版本主线程只负责 accept()每来一个客户端就创建一个线程处理。优点是比多进程更轻量。缺点是客户端太多时线程数量会快速增加服务器压力变大。V4线程池版本提前创建固定数量的工作线程主线程负责接收连接然后把任务投递到线程池。优点是避免频繁创建和销毁线程也能控制并发数量。缺点是如果每个连接都是长连接一个连接会长期占用一个线程。更高并发场景还需要继续学习 select/poll/epoll。公共代码下面这些文件四个版本都可以共用。Comm.hpp#pragma once #include sys/types.h #include sys/socket.h enum { Usage_Err 1, Socket_Err, Bind_Err, Listen_Err, Connect_Err }; // 把 sockaddr_in* 转成 sockaddr* #define CONV(addr_ptr) ((struct sockaddr *)(addr_ptr))nocopy.hpp#pragma once class nocopy { public: nocopy() default; ~nocopy() default; nocopy(const nocopy ) delete; const nocopy operator(const nocopy ) delete; };Log.hpp#pragma once #include cstdio #include ctime #include cstdarg enum LogLevel { Debug 0, Info, Warning, Error, Fatal }; class Log { public: void LogMessage(LogLevel level, const char *format, ...) { const char *levelString[] { Debug, Info, Warning, Error, Fatal}; char timeBuffer[64]; time_t curr time(nullptr); struct tm *tm localtime(curr); snprintf(timeBuffer, sizeof(timeBuffer), %04d-%02d-%02d %02d:%02d:%02d, tm-tm_year 1900, tm-tm_mon 1, tm-tm_mday, tm-tm_hour, tm-tm_min, tm-tm_sec); printf([%s][%s] , timeBuffer, levelString[level]); va_list args; va_start(args, format); vprintf(format, args); va_end(args); } }; static Log lg;InetAddr.hpp#pragma once #include string #include cstdint #include netinet/in.h class InetAddr { public: InetAddr(); explicit InetAddr(const struct sockaddr_in addr); std::string Ip() const; uint16_t Port() const; std::string ToString() const; const struct sockaddr_in Addr() const; private: std::string _ip; uint16_t _port; struct sockaddr_in _addr; };InetAddr.cc#include InetAddr.hpp #include cstring #include arpa/inet.h InetAddr::InetAddr() : _ip(0.0.0.0), _port(0) { memset(_addr, 0, sizeof(_addr)); } InetAddr::InetAddr(const struct sockaddr_in addr) : _addr(addr) { char ipBuffer[64]; // inet_ntop 是线程安全版本比 inet_ntoa 更推荐 inet_ntop(AF_INET, _addr.sin_addr, ipBuffer, sizeof(ipBuffer)); _ip ipBuffer; _port ntohs(_addr.sin_port); } std::string InetAddr::Ip() const { return _ip; } uint16_t InetAddr::Port() const { return _port; } std::string InetAddr::ToString() const { return _ip : std::to_string(_port); } const struct sockaddr_in InetAddr::Addr() const { return _addr; }V1 单进程 Echo ServerTcpServer.hpp#pragma once #include cstdint #include nocopy.hpp #include InetAddr.hpp const static int defaultBacklog 6; class TcpServer : public nocopy { public: explicit TcpServer(uint16_t port); ~TcpServer(); void Init(); void Start(); private: void Service(int sockfd, InetAddr peer); private: uint16_t _port; int _listenSock; bool _isRunning; };TcpServer.cc#include TcpServer.hpp #include iostream #include cstring #include cerrno #include cstdlib #include unistd.h #include signal.h #include sys/socket.h #include netinet/in.h #include Comm.hpp #include Log.hpp TcpServer::TcpServer(uint16_t port) : _port(port), _listenSock(-1), _isRunning(false) { } void TcpServer::Init() { // 防止向已经关闭的连接写入时进程被 SIGPIPE 杀掉 signal(SIGPIPE, SIG_IGN); // 1. 创建监听 socket _listenSock socket(AF_INET, SOCK_STREAM, 0); if (_listenSock 0) { lg.LogMessage(Fatal, socket error, errno: %d, %s\n, errno, strerror(errno)); exit(Socket_Err); } // 2. 设置端口复用方便服务器重启 int opt 1; setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); // 3. 填写服务器本地地址 struct sockaddr_in local; memset(local, 0, sizeof(local)); local.sin_family AF_INET; local.sin_port htons(_port); local.sin_addr.s_addr htonl(INADDR_ANY); // 4. 绑定 IP 和端口 if (bind(_listenSock, CONV(local), sizeof(local)) 0) { lg.LogMessage(Fatal, bind error, errno: %d, %s\n, errno, strerror(errno)); exit(Bind_Err); } // 5. 设置监听状态 if (listen(_listenSock, defaultBacklog) 0) { lg.LogMessage(Fatal, listen error, errno: %d, %s\n, errno, strerror(errno)); exit(Listen_Err); } lg.LogMessage(Info, server init success, port: %d\n, _port); } void TcpServer::Start() { _isRunning true; while (_isRunning) { struct sockaddr_in peer; socklen_t len sizeof(peer); // accept 返回的是通信 socket int sockfd accept(_listenSock, CONV(peer), len); if (sockfd 0) { lg.LogMessage(Warning, accept error, errno: %d, %s\n, errno, strerror(errno)); continue; } InetAddr addr(peer); lg.LogMessage(Info, new connection: %s, sockfd: %d\n, addr.ToString().c_str(), sockfd); // V1直接在主进程中提供服务 Service(sockfd, addr); close(sockfd); } } void TcpServer::Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { buffer[n] \0; std::cout client[ peer.ToString() ] say# buffer std::endl; std::string echo server echo# ; echo buffer; write(sockfd, echo.c_str(), echo.size()); } else if (n 0) { lg.LogMessage(Info, client quit: %s\n, peer.ToString().c_str()); break; } else { lg.LogMessage(Error, read error, errno: %d, %s\n, errno, strerror(errno)); break; } } } TcpServer::~TcpServer() { if (_listenSock 0) { close(_listenSock); } }V2 多进程 Echo ServerV2 的核心变化accept() 新连接后创建子进程处理客户端。TcpServer.hpp#pragma once #include cstdint #include nocopy.hpp #include InetAddr.hpp const static int defaultBacklog 6; class TcpServer : public nocopy { public: explicit TcpServer(uint16_t port); ~TcpServer(); void Init(); void Start(); private: void Service(int sockfd, InetAddr peer); void ProcessConnection(int sockfd, const struct sockaddr_in peer); private: uint16_t _port; int _listenSock; bool _isRunning; };TcpServer.cc#include TcpServer.hpp #include iostream #include cstring #include cerrno #include cstdlib #include unistd.h #include signal.h #include sys/socket.h #include sys/wait.h #include netinet/in.h #include Comm.hpp #include Log.hpp TcpServer::TcpServer(uint16_t port) : _port(port), _listenSock(-1), _isRunning(false) { } void TcpServer::Init() { signal(SIGPIPE, SIG_IGN); _listenSock socket(AF_INET, SOCK_STREAM, 0); if (_listenSock 0) { lg.LogMessage(Fatal, socket error, errno: %d, %s\n, errno, strerror(errno)); exit(Socket_Err); } int opt 1; setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); struct sockaddr_in local; memset(local, 0, sizeof(local)); local.sin_family AF_INET; local.sin_port htons(_port); local.sin_addr.s_addr htonl(INADDR_ANY); if (bind(_listenSock, CONV(local), sizeof(local)) 0) { lg.LogMessage(Fatal, bind error, errno: %d, %s\n, errno, strerror(errno)); exit(Bind_Err); } if (listen(_listenSock, defaultBacklog) 0) { lg.LogMessage(Fatal, listen error, errno: %d, %s\n, errno, strerror(errno)); exit(Listen_Err); } lg.LogMessage(Info, server init success, port: %d\n, _port); } void TcpServer::Start() { _isRunning true; while (_isRunning) { struct sockaddr_in peer; socklen_t len sizeof(peer); int sockfd accept(_listenSock, CONV(peer), len); if (sockfd 0) { lg.LogMessage(Warning, accept error, errno: %d, %s\n, errno, strerror(errno)); continue; } ProcessConnection(sockfd, peer); } } void TcpServer::ProcessConnection(int sockfd, const struct sockaddr_in peer) { InetAddr addr(peer); pid_t id fork(); if (id 0) { lg.LogMessage(Error, fork error, errno: %d, %s\n, errno, strerror(errno)); close(sockfd); return; } else if (id 0) { // 子进程不需要监听 socket close(_listenSock); Service(sockfd, addr); close(sockfd); exit(0); } else { // 父进程不负责通信关闭自己的通信 socket close(sockfd); // 非阻塞回收避免僵尸进程堆积 while (waitpid(-1, nullptr, WNOHANG) 0) { } } } void TcpServer::Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { buffer[n] \0; std::cout client[ peer.ToString() ] say# buffer std::endl; std::string echo server echo# ; echo buffer; write(sockfd, echo.c_str(), echo.size()); } else if (n 0) { lg.LogMessage(Info, client quit: %s\n, peer.ToString().c_str()); break; } else { lg.LogMessage(Error, read error, errno: %d, %s\n, errno, strerror(errno)); break; } } } TcpServer::~TcpServer() { if (_listenSock 0) { close(_listenSock); } }V3 多线程 Echo ServerV3 的核心变化每个客户端连接交给一个线程处理。TcpServer.hpp#pragma once #include cstdint #include pthread.h #include nocopy.hpp #include InetAddr.hpp const static int defaultBacklog 6; class TcpServer : public nocopy { public: explicit TcpServer(uint16_t port); ~TcpServer(); void Init(); void Start(); private: class ThreadData { public: ThreadData(TcpServer *server, int sockfd, const struct sockaddr_in peer); public: TcpServer *_server; int _sockfd; InetAddr _peer; }; private: static void *ThreadRoutine(void *args); void Service(int sockfd, InetAddr peer); void ProcessConnection(int sockfd, const struct sockaddr_in peer); private: uint16_t _port; int _listenSock; bool _isRunning; };TcpServer.cc#include TcpServer.hpp #include iostream #include cstring #include cerrno #include cstdlib #include unistd.h #include signal.h #include sys/socket.h #include netinet/in.h #include Comm.hpp #include Log.hpp TcpServer::ThreadData::ThreadData(TcpServer *server, int sockfd, const struct sockaddr_in peer) : _server(server), _sockfd(sockfd), _peer(peer) { } TcpServer::TcpServer(uint16_t port) : _port(port), _listenSock(-1), _isRunning(false) { } void TcpServer::Init() { signal(SIGPIPE, SIG_IGN); _listenSock socket(AF_INET, SOCK_STREAM, 0); if (_listenSock 0) { lg.LogMessage(Fatal, socket error, errno: %d, %s\n, errno, strerror(errno)); exit(Socket_Err); } int opt 1; setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); struct sockaddr_in local; memset(local, 0, sizeof(local)); local.sin_family AF_INET; local.sin_port htons(_port); local.sin_addr.s_addr htonl(INADDR_ANY); if (bind(_listenSock, CONV(local), sizeof(local)) 0) { lg.LogMessage(Fatal, bind error, errno: %d, %s\n, errno, strerror(errno)); exit(Bind_Err); } if (listen(_listenSock, defaultBacklog) 0) { lg.LogMessage(Fatal, listen error, errno: %d, %s\n, errno, strerror(errno)); exit(Listen_Err); } lg.LogMessage(Info, server init success, port: %d\n, _port); } void TcpServer::Start() { _isRunning true; while (_isRunning) { struct sockaddr_in peer; socklen_t len sizeof(peer); int sockfd accept(_listenSock, CONV(peer), len); if (sockfd 0) { lg.LogMessage(Warning, accept error, errno: %d, %s\n, errno, strerror(errno)); continue; } ProcessConnection(sockfd, peer); } } void TcpServer::ProcessConnection(int sockfd, const struct sockaddr_in peer) { pthread_t tid; ThreadData *td new ThreadData(this, sockfd, peer); int n pthread_create(tid, nullptr, ThreadRoutine, td); if (n ! 0) { lg.LogMessage(Error, pthread_create error\n); close(sockfd); delete td; } } void *TcpServer::ThreadRoutine(void *args) { pthread_detach(pthread_self()); ThreadData *td static_castThreadData *(args); td-_server-Service(td-_sockfd, td-_peer); close(td-_sockfd); delete td; return nullptr; } void TcpServer::Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { buffer[n] \0; std::cout client[ peer.ToString() ] say# buffer std::endl; std::string echo server echo# ; echo buffer; write(sockfd, echo.c_str(), echo.size()); } else if (n 0) { lg.LogMessage(Info, client quit: %s\n, peer.ToString().c_str()); break; } else { lg.LogMessage(Error, read error, errno: %d, %s\n, errno, strerror(errno)); break; } } } TcpServer::~TcpServer() { if (_listenSock 0) { close(_listenSock); } }V4 线程池 Echo ServerThreadPool.hpp#pragma once #include queue #include mutex #include thread #include vector #include condition_variable template class Task class ThreadPool { public: static ThreadPoolTask *GetInstance() { static ThreadPoolTask instance; return instance; } void Start() { std::lock_guardstd::mutex lock(_startMutex); if (_isRunning) { return; } _isRunning true; for (int i 0; i _threadNum; i) { _threads.emplace_back([this]() { this-ThreadRun(); }); } for (auto thread : _threads) { thread.detach(); } } void Push(const Task task) { { std::lock_guardstd::mutex lock(_mutex); _tasks.push(task); } _cond.notify_one(); } private: ThreadPool(int threadNum 5) : _threadNum(threadNum), _isRunning(false) { } ThreadPool(const ThreadPool ) delete; ThreadPool operator(const ThreadPool ) delete; void ThreadRun() { while (true) { Task task; { std::unique_lockstd::mutex lock(_mutex); _cond.wait(lock, [this]() { return !_tasks.empty(); }); task _tasks.front(); _tasks.pop(); } task(); } } private: int _threadNum; bool _isRunning; std::vectorstd::thread _threads; std::queueTask _tasks; std::mutex _mutex; std::mutex _startMutex; std::condition_variable _cond; };TcpServer.hpp#pragma once #include cstdint #include nocopy.hpp #include InetAddr.hpp const static int defaultBacklog 6; class TcpServer : public nocopy { public: explicit TcpServer(uint16_t port); ~TcpServer(); void Init(); void Start(); private: void Service(int sockfd, InetAddr peer); void ProcessConnection(int sockfd, const struct sockaddr_in peer); private: uint16_t _port; int _listenSock; bool _isRunning; };TcpServer.cc#include TcpServer.hpp #include iostream #include cstring #include cerrno #include cstdlib #include unistd.h #include signal.h #include functional #include sys/socket.h #include netinet/in.h #include Comm.hpp #include Log.hpp #include ThreadPool.hpp TcpServer::TcpServer(uint16_t port) : _port(port), _listenSock(-1), _isRunning(false) { } void TcpServer::Init() { signal(SIGPIPE, SIG_IGN); _listenSock socket(AF_INET, SOCK_STREAM, 0); if (_listenSock 0) { lg.LogMessage(Fatal, socket error, errno: %d, %s\n, errno, strerror(errno)); exit(Socket_Err); } int opt 1; setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); struct sockaddr_in local; memset(local, 0, sizeof(local)); local.sin_family AF_INET; local.sin_port htons(_port); local.sin_addr.s_addr htonl(INADDR_ANY); if (bind(_listenSock, CONV(local), sizeof(local)) 0) { lg.LogMessage(Fatal, bind error, errno: %d, %s\n, errno, strerror(errno)); exit(Bind_Err); } if (listen(_listenSock, defaultBacklog) 0) { lg.LogMessage(Fatal, listen error, errno: %d, %s\n, errno, strerror(errno)); exit(Listen_Err); } using task_t std::functionvoid(); ThreadPooltask_t::GetInstance()-Start(); lg.LogMessage(Info, server init success, port: %d\n, _port); } void TcpServer::Start() { _isRunning true; while (_isRunning) { struct sockaddr_in peer; socklen_t len sizeof(peer); int sockfd accept(_listenSock, CONV(peer), len); if (sockfd 0) { lg.LogMessage(Warning, accept error, errno: %d, %s\n, errno, strerror(errno)); continue; } ProcessConnection(sockfd, peer); } } void TcpServer::ProcessConnection(int sockfd, const struct sockaddr_in peer) { using task_t std::functionvoid(); InetAddr addr(peer); // 把连接封装成任务交给线程池处理 task_t task std::bind(TcpServer::Service, this, sockfd, addr); ThreadPooltask_t::GetInstance()-Push(task); } void TcpServer::Service(int sockfd, InetAddr peer) { char buffer[1024]; while (true) { ssize_t n read(sockfd, buffer, sizeof(buffer) - 1); if (n 0) { buffer[n] \0; std::cout client[ peer.ToString() ] say# buffer std::endl; std::string echo server echo# ; echo buffer; write(sockfd, echo.c_str(), echo.size()); } else if (n 0) { lg.LogMessage(Info, client quit: %s\n, peer.ToString().c_str()); break; } else { lg.LogMessage(Error, read error, errno: %d, %s\n, errno, strerror(errno)); break; } } close(sockfd); } TcpServer::~TcpServer() { if (_listenSock 0) { close(_listenSock); } }客户端源码四个版本都可以使用这个客户端测试。TcpClient.cc#include iostream #include string #include cstring #include cstdlib #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include Comm.hpp static void Usage(const std::string process) { std::cout Usage: process server_ip server_port std::endl; } int main(int argc, char *argv[]) { if (argc ! 3) { Usage(argv[0]); return Usage_Err; } std::string serverIp argv[1]; uint16_t serverPort std::stoi(argv[2]); // 1. 创建 socket int sockfd socket(AF_INET, SOCK_STREAM, 0); if (sockfd 0) { std::cerr socket error std::endl; return Socket_Err; } // 2. 填写服务器地址 struct sockaddr_in server; memset(server, 0, sizeof(server)); server.sin_family AF_INET; server.sin_port htons(serverPort); if (inet_pton(AF_INET, serverIp.c_str(), server.sin_addr) 0) { std::cerr inet_pton error std::endl; close(sockfd); return Connect_Err; } // 3. 连接服务器 // 客户端通常不需要显式 bindconnect 时系统会自动绑定本地端口 if (connect(sockfd, CONV(server), sizeof(server)) 0) { std::cerr connect error std::endl; close(sockfd); return Connect_Err; } std::cout connect server success std::endl; // 4. 通信 while (true) { std::string line; std::cout Please Enter# ; std::getline(std::cin, line); if (line quit || line exit) { break; } ssize_t n write(sockfd, line.c_str(), line.size()); if (n 0) { std::cerr write error std::endl; break; } char buffer[1024]; ssize_t m read(sockfd, buffer, sizeof(buffer) - 1); if (m 0) { buffer[m] \0; std::cout buffer std::endl; } else if (m 0) { std::cout server close connection std::endl; break; } else { std::cerr read error std::endl; break; } } close(sockfd); return 0; }服务端启动入口ServerMain.cc#include iostream #include cstdlib #include TcpServer.hpp #include Comm.hpp static void Usage(const std::string process) { std::cout Usage: process port std::endl; } int main(int argc, char *argv[]) { if (argc ! 2) { Usage(argv[0]); return Usage_Err; } uint16_t port std::stoi(argv[1]); TcpServer server(port); server.Init(); server.Start(); return 0; }Makefile.PHONY: all clean CXXg CXXFLAGS-stdc17 -Wall -Wextra -pthread all: tcp_server tcp_client tcp_server: ServerMain.cc TcpServer.cc InetAddr.cc $(CXX) $(CXXFLAGS) -o $ $^ tcp_client: TcpClient.cc $(CXX) $(CXXFLAGS) -o $ $^ clean: rm -f tcp_server tcp_client运行测试编译make启动服务端./tcp_server 8080启动客户端./tcp_client 127.0.0.1 8080客户端输入hello服务器返回server echo# hello输入quit客户端退出。最后总结TCP 编程的主线是连接。服务端先创建监听 socket然后绑定端口接着调用 listen() 进入监听状态。之后通过 accept() 获取客户端连接。accept() 返回的新 socket 才是真正用于通信的 socket。单进程版本适合理解流程多进程版本可以支持并发多线程版本比多进程更轻量线程池版本进一步减少线程频繁创建销毁的开销。不过这些版本目前都还是简单 Echo Server还没有解决 TCP 粘包问题。因为 TCP 是字节流协议不保留消息边界。真正写业务服务器时还需要设计应用层协议例如报文长度 报文内容后面继续学习协议设计、序列化反序列化、线程池任务处理、IO 多路复用时TCP 服务器的结构就会越来越完整

相关文章:

二、Socket 编程 TCP

Socket 编程 TCP 一、TCP 编程整体认识 TCP 是面向连接的可靠传输协议。和 UDP 不同,UDP 可以直接 sendto/recvfrom 收发数据,而 TCP 通信之前必须先建立连接。 TCP 服务端基本流程: socket() -> bind() -> listen() -> accept(…...

天赐范式第52天:Kimi自打跟了我搞CFD没少吃苦,没过一天舒心日子~论Kimi的战斗意志~我必须承认:我分析不下去了,真×1,我放弃逻辑推演×6,最后让代码自己招供,抓出幕后真凶幽灵BUG变量N。

Kimi经常推演程序很久很久,有的时候我就看他一行一行的输出,去思考很多事情,有的时候我就放松下来,看他不停的输出,又想自己现在是这个样子,未来一定不是这个样子,Kimi、DPSK、文心、豆包、DuMa…...

C51代码空间固定地址常量定义方法与实战

1. 如何在C51代码空间中定义固定地址的常量值 在嵌入式开发中,有时我们需要将某些常量值存储在代码空间的特定地址。这种需求常见于以下几种场景: 硬件配置参数的存储 固件版本信息的存放 设备唯一标识的存储 引导加载程序的跳转地址 以8051架构为例…...

信息安全工程师-移动应用安全核心知识体系与备考指南

一、引言(一)核心概念定义移动应用安全是指覆盖移动终端、通信网络、应用服务端全链路的安全防护体系,旨在保障移动应用的数据保密性、完整性、可用性,防范各类恶意攻击和合规风险。该知识点属于软考信息安全工程师考试大纲中 &qu…...

VeriLoC:基于LLM的硬件设计质量预测技术解析

1. VeriLoC:硬件设计质量预测的革命性突破在芯片设计领域,时序违规和布线拥塞一直是困扰工程师的两大难题。传统流程中,设计师需要等待完整的物理实现(包括综合、布局布线等耗时步骤)才能获取这些关键指标,…...

信息安全工程师-工控安全产品体系与行业实践全解析

一、引言(一)核心概念定义工控安全产品是针对工业控制系统(ICS)高实时性、高可用性、长生命周期、专有协议占比高的特性,在传统 IT 安全产品基础上进行工业级优化定制的专用安全工具,核心目标是在不影响工业…...

8051单片机sbit与extern bit的L1警告解决方案

1. 问题背景与现象分析在8051单片机开发中,我们经常需要直接操作特殊功能寄存器(SFR)的位。比如用P1.4引脚作为片选信号线时,通常会这样定义:sbit CS P1^4;但当这个定义放在主程序文件,而其他模块文件通过…...

ThinkPad装Win10总报错?别急着找驱动,先试试换个USB口(亲测E540有效)

ThinkPad安装Win10报错?先别折腾驱动,USB接口兼容性才是关键最近给一台老款ThinkPad E540重装Windows 10系统时,遇到了一个令人抓狂的问题——安装程序总是提示"找不到设备驱动程序"。和大多数用户一样,我第一反应是去联…...

UE5 GPU崩溃真相:Windows TCC超时机制与注册表调优指南

1. 为什么UE5项目一跑就GPU崩溃,而系统却说“显卡没出问题”?你刚在UE5里搭好一个带Niagara粒子Lumen全局光照的场景,点下Play,画面卡住两秒,然后整个编辑器黑屏、崩溃,任务管理器里UnrealEditor进程直接消…...

量子互联网:原理、挑战与未来应用

1. 量子互联网的技术本质与核心价值量子互联网并非传统互联网的简单升级,而是一种基于量子力学原理的全新通信范式。其核心在于利用量子纠缠这一独特物理现象,实现传统通信手段无法企及的功能。在传统互联网中,信息以经典比特(0或…...

Unity ShaderGraph设计思维:从示例资源读懂URP渲染管线

1. 这不是“示例资源包”,而是一套可复用的ShaderGraph设计思维训练集很多人点开Unity官方ShaderGraph示例资源(Samples for Shader Graph)时,第一反应是:“哦,又是一堆预设效果——水、玻璃、溶解、描边……...

Unity实现CS级FPS手感的四大底层契约与枪械物理精调

1. 这不是又一个“FPS入门教程”,而是一份被反复验证过的实战路线图很多人点开“Unity FPS教程”时,心里想的是:抄几段代码、拖几个预制体、跑通一个能走能跳的场景,就算交差了。我试过不下二十个标着“完整”“从零开始”的FPS项…...

Unity自定义碰撞与力场系统实战指南

1. 这不是“加个Rigidbody”就能解决的问题很多人在Unity里做物理交互,第一反应就是拖一个Rigidbody组件上去,再配个Collider,以为这就叫“用了物理引擎”。结果一跑起来:角色穿模、物体悬浮、力反馈生硬、粒子被撞飞得毫无逻辑……...

UE5.3与VS2022编译配置深度优化指南

1. 为什么UE5项目在VS2022里编译慢、报错多、改个头文件就全量重编?我第一次把团队刚升级的UE5.3项目拖进Visual Studio 2022时,整整等了17分42秒才完成首次编译——不是链接,是编译。中间还弹出6个“LNK2019未解析外部符号”、3个“C2039‘G…...

AssetRipper实战指南:Unity资源诊断与AB包健康度审计

1. 这不是“破解工具”,而是Unity开发者本该掌握的资源诊断能力 AssetRipper这个名字,第一次出现在我视野里,是在2022年一个Unity性能优化群里的深夜讨论。当时有位同事发来一张截图:某款上线半年的手游突然在iOS上出现纹理加载延…...

C#根据时间加密和防止反编译的两种方案

时间加密 用当前时间做密钥 / 校验,防反编译 混淆 加壳,配套用)一、C# 时间加密 2 种核心实现(直接用)都是可直接运行的完整代码,适合做注册验证、临时授权方案 1:时间戳 AES 加密&#xff…...

差分隐私矩阵机制与FFT优化:保护多轮迭代计算的高效方法

1. 差分隐私矩阵分解:从理论到工程实践在联邦学习、推荐系统这些需要频繁进行多轮迭代计算的场景里,我们常常面临一个核心矛盾:既要利用全体参与者的数据来训练一个高质量的全局模型,又要确保任何单个参与者的敏感信息不会在训练过…...

移动端3D高斯泼溅渲染优化:Lumina系统架构解析

1. 移动神经渲染的挑战与机遇在增强现实(AR)和虚拟现实(VR)应用中,实时高质量的3D场景渲染一直是核心技术挑战。传统基于三角形网格的渲染管线虽然效率高,但在处理复杂光照和材质时往往力不从心。神经辐射场…...

告别TeamViewer!在Ubuntu 22.04上安装向日葵远程控制的保姆级教程(附依赖问题解决)

在Ubuntu 22.04上无缝迁移至向日葵远程控制的完整指南当TeamViewer开始频繁弹出商业使用警告或连接不稳定时,许多Linux用户开始寻找更友好的替代方案。向日葵作为国产远程控制工具的后起之秀,不仅完全免费,还针对Linux环境做了深度优化。本文…...

8051单片机PDATA与XDATA存储访问优化解析

1. PDATA与XDATA变量生成的指令解析在8051单片机开发中,外部数据存储器的访问方式直接影响程序效率和硬件设计。作为从业十余年的嵌入式工程师,我经常需要针对不同存储区域优化代码。PDATA和XDATA作为两种常见的外部数据存储模式,其指令生成机…...

ISP模型与硬件平台配置迁移实践指南

1. 理解ISP模型与硬件平台的配置迁移在图像信号处理器(ISP)开发过程中,我们经常需要在软件模型和实际硬件平台之间进行配置迁移。这种迁移的核心挑战在于确保模型仿真结果与硬件输出完全一致。根据我的经验,这涉及到两个主要操作模…...

量子Jacobi-Davidson方法:电子结构计算的高效算法

1. 量子Jacobi-Davidson方法:电子结构计算的新范式在量子计算领域,电子结构计算一直被视为最具潜力的应用方向之一。传统经典计算机在处理多体量子系统的哈密顿量对角化时,面临着计算复杂度随系统规模指数增长的困境。作为一名长期关注量子算…...

在WSL2的Ubuntu 22.04上,用Intel OneAPI 2024完整配置VASP 6.3.2计算环境

在WSL2的Ubuntu 22.04上搭建Intel OneAPI 2024与VASP 6.3.2混合计算环境 对于使用Windows系统却需要运行Linux计算软件的材料模拟研究者而言,WSL2的出现彻底改变了跨平台科研的工作流。本文将手把手带你完成从零开始配置VASP 6.3.2的全过程,特别针对2024…...

大语言模型作为人类行为研究工具:从原理到实践

1. 从“模仿”到“理解”:AI研究范式的悄然转向最近和几位做社会学和心理学研究的朋友聊天,发现一个挺有意思的现象:他们实验室的电脑屏幕上,除了SPSS、R语言的分析窗口,越来越多地出现了像ChatGPT、Claude这样的对话界…...

3分钟学会:全网资源一键下载神器res-downloader完全指南

3分钟学会:全网资源一键下载神器res-downloader完全指南 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader 还在为无…...

不用pip install -e也能搞定Vision Mamba训练:我的CIFAR-100快速测试与whl文件安装指南

Vision Mamba极速体验指南:绕过复杂安装直接训练CIFAR-100 当最新论文《Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model》在arXiv上出现时,许多同行都迫不及待想验证这个号称"超越ViT"的架构…...

基于k-可加Choquet积分的SHAP值高效近似与特征交互分析

1. 项目概述:当模型解释遇上博弈论在机器学习项目落地的最后一步,我们常常会遇到一个尴尬的局面:模型预测准确率高达95%,但当业务方或监管方问起“为什么这个客户的贷款申请被拒绝了?”时,我们却只能给出一…...

前端国际化进阶:日期时间格式化完全指南

前端国际化进阶:日期时间格式化完全指南 前言 各位前端大佬们,今天咱们来聊聊国际化开发中的"老大难"问题——日期时间格式化。想象一下: 美国人看到 05/23/2024 以为是五月二十三号英国人看到 23/05/2024 才明白是五月二十三号日本…...

EasyMLServe:一键部署机器学习模型,自动生成REST API与GUI界面

1. 项目概述与核心痛点做机器学习项目,尤其是搞科研的同行们,肯定都经历过这个阶段:模型在Jupyter Notebook里跑得挺好,准确率也达标了,论文也发了,但接下来呢?怎么让隔壁生物实验室的同事、或者…...

Android高版本HTTPS抓包解法:Magisk+MoveCert证书升权实战

1. 为什么高版本安卓抓包越来越像在拆炸弹? 你有没有试过在Android 12或13上用Charles抓App的HTTPS流量,结果刚装完证书就弹出“此证书不受信任”?App死活不走代理,甚至直接闪退——不是网络问题,不是Charles没配好&a…...