基于MFC和WinSock的简易聊天室程序(附完整源码)
本文使用Windows C网络编程和多线程编程实现一个简单的聊天室程序。该程序由服务器端和客户端两部分组成。服务器端负责客户端的上线、离线以及转发各个客户端发来的消息。客户端向服务器发送信息从服务器接收信息以及显示信息到客户端界面。1.创建服务器端界面首先创建项目。打开Vistual Stdio创建一个MFCMicrosoft Foundation Classes项目应用程序类型选择基于对话框在高级功能中勾选Windows套接字完成Winsock DLL库的初始化。项目名称改为ChatServer。创建好项目后在菜单栏中选择视图其它窗口找到资源视图。打开下图所示文件IDD_CHATSERVER_DIALOG就是我们创建的服务器端的界面。删除原来的所有控件。选择对话框右键打开属性将文字描述对话框标题改为“聊天室服务器端”边框选择“对话框外框”。再次打开视图选择工具箱为聊天界面添加控件。下面使用到的几个控件。Button是按钮Edit Control是文本编辑框Static Text是静态的文本IP Address Control用于编辑Ip地址。依照下图完成控件的添加除基本功能外可以自由发挥。除了静态的控件外其余控件的ID及属性见下表。控件名ID其它属性编辑框已连接客户端右侧IDC_ECIT_CLIENTS只读true编辑框中间较大IDC_EDIT_MSG只读true多行true垂直滚动trueIP地址IDC_IPADDRESS编辑框PORT右侧IDC_EDIT_PORT按钮启动服务器IDC_START文字描述按钮退出程序IDC_EXIT文字描述为了方便获取和设置各个控件的值我们为控件添加关联变量。在解决方案资源管理器中右键点击项目ChatServer打开“类向导”。确认项目名称为ChatServer在“类名”中选择CChatServerDlg单击“成员变量”标签。可以看到之前创建的控件及其ID。选择控件点击“添加变量”为控件添加关联变量。弹出的“添加控件变量”对话框如下。分别注意类型名称和变量类型。类别分为值和控件值的作用是同步控件显示的数据可以选择如CStringintdouble等基本变量类型它通过UpdateData()函数实现与控件的通信UpdateData(TRUE)将控件中的数据读取并保存到关联的变量中。UpdateData(FALSE)将变量中的值写入并显示在关联的控件上。控件的作用则是直接操控控件本身例如启用、禁用、修改文字等它通过调用控件的成员函数来实现对控件的操控。名称和变量类型则分别对应创建的变量名称和数据类型。下表为需要添加的关联变量。ID类别变量名变量类型IDC_IPADDRESS控件m_ipServerCIPAddressCtrlIDC_EDIT_PORT值m_nPortunsigned intIDC_EDIT_CIENTS值m_nClientsunsigned intIDC_EDIT_MSG控件m_edtMsgCEdit添加结束后我们可以在对话框类CChatServerDlg的定义中找到相应变量如下。unsigned int m_nClients; CIPAddressCtrl m_ipServer; unsigned int m_nPort; CEdit m_edtMsg;同时在ChatServerDlg.cpp中也可以找到如下函数。它将相应的关联变量和控件建立联系。void CChatServerDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_CLIENTS, m_nClients); DDX_Control(pDX, IDC_IPADDRESS, m_ipServer); DDX_Text(pDX, IDC_EDIT_PORT, m_nPort); DDX_Control(pDX, IDC_EDIT_MSG, m_edtMsg); }最后为了避免每次启动程序都要重新填写IP地址、端口等数据可以为这两个控件设置初始值。找到并打开文件ChatServerDlg.cpp找到对话框初始化函数OnInitDialog()在//TODO一行下添加如下代码。// TODO: 在此添加额外的初始化代码 m_ipServer.SetAddress(127, 0, 0, 1); m_nPort 8888; m_nClients 0; UpdateData(false);将IP地址初始化为127.0.0.1端口号初始化为8888用户数量初始化为0最后将变量值显示到对应的控件上。运行程序如下。2.创建客户端界面客户端界面的创建方法和服务器端基本一致。创建MFC项目选择基于对话框勾选Windows套接字项目名称为ChatClient。对话框界面和控件如下。控件名ID其它属性编辑框中间较大IDC_EDIT_MSG只读true多行true垂直滚动true编辑框左下方IDC_EDIT_SENDIP地址IDC_IPADDRESS编辑框服务器端口IDC_EDIT_PORT按钮登录IDC_LOGIN文字描述按钮退出程序IDC_EXIT文字描述按钮发送IDC_SEND文字描述为控件添加关联变量如下。ID类别变量名变量类型IDC_IPADDRESS控件m_ipServerCIPAddressCtrlIDC_EDIT_PORT值m_nPortunsigned intIDC_EDIT_SEND值CStringm_strSendIDC_EDIT_MSG控件CEditm_edtMsg对话框初始化代码如下。// TODO: 在此添加额外的初始化代码 m_ipServer.SetAddress(127, 0, 0, 1); m_nPort 8888; UpdateData(false);运行程序如下。3.服务器端编程1.对话框类中添加数据成员需要在对话框类CChatServerDlg中使用向量vector在头文件ChatServerDlg.h文件开始部分加入下面两行代码。包含相应的头文件和命名空间。#includevector using namespace std;服务器要存储每个连接的客户端的信息首先定义一个保存客户端信息的类ClientItem之后使用vector保存所有客户端。在ChatServerDlg.h文件中找到对话框类CChatServerDlg的定义处在上方添加ClientItem的定义。class CChatServerDlg; class ClientItem { public: SOCKET cltSocket; CString cltIP; int cltPort; CChatServerDlg* pChatDlg; ClientItem() { cltPort 8888; cltSocket INVALID_SOCKET; pChatDlg NULL; } };注意到ClientItem中使用了CChatServerDlg类将其声明提前。可以看出保存的客户端信息有对应的socket、IP地址、端口。为了能在ClientItem类中访问对话框类的成员保存了一个对话框类的地址pChatDlg。在CChatServerDlg类末尾加入以下两个属性。SOCKET m_listenSocket; vectorClientItem* m_clients;分别为监听套接字和保存客户端信息的向量。同时在ChatServerDlg.cpp中找到CChatServerDlg类的构造函数的定义加入以下代码。m_listenSocket INVALID_SOCKET;2.为“启动服务器”按钮添加功能。打开“类向导”对话框选择项目ChatServer选择类CChatServerDlg选择“命令”标签找到按钮ID——IDC_START选中在“消息”中选择BN_CLICKED该通知码在用户点击对应按钮时向父窗口发送对应的按钮ID和通知码点击“添加处理程序”添加按钮的消息响应函数OnClickedStart()。同关联变量一样可以在对话框类CChatServerDlg的定义里找到函数声明。afx_msg void OnClickedStart();afx_msg是一个宏表明这是一个消息响应函数。在ChatServerDlg.cpp中也可以找到如下代码。第五行表明IDC_STRAT控件在收到BN_CLICKED消息时会执行相应的函数即建立联系。BEGIN_MESSAGE_MAP(CChatServerDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_START, CChatServerDlg::OnClickedStart) ON_MESSAGE(WM_UPDATE_MSG, OnUpdateMsg) ON_BN_CLICKED(IDC_EXIT, CChatServerDlg::OnClickedExit) END_MESSAGE_MAP()那么接下来我们来完成消息响应函数OnClickedStart()的定义将其添加在ChatServerDlg.cpp的末尾。参见注释理解代码。void CChatServerDlg::OnClickedStart() { // TODO: 在此添加控件通知处理程序代码 UpdateData(true); //将对话框中的值保存到相应的关联变量里 DWORD dwIP; //IP地址 m_ipServer.GetAddress(dwIP); //获取控件中的IP地址 //创建监听套接字 m_listenSocket socket(AF_INET, SOCK_STREAM, 0); if (m_listenSocket INVALID_SOCKET) { AfxMessageBox(_T(创建socket失败!!!)); //弹出一个提示框 return; } //绑定IP和端口 sockaddr_in serverAddr { 0 }; serverAddr.sin_family AF_INET; serverAddr.sin_port htons(m_nPort); serverAddr.sin_addr.s_addr htonl(dwIP); if (bind(m_listenSocket, (sockaddr*)serverAddr, sizeof(serverAddr)) -1) { AfxMessageBox(_T(绑定失败!!!)); return; } //开始监听 if (INVALID_SOCKET listen(m_listenSocket, 5)) { AfxMessageBox(_T(监听失败!!!)); return; } //调用控件变量的成员函数在窗口上显示文本 m_edtMsg.SetWindowTextW(_T(服务器已启动!!!\r\n)); //GetDlgItem()函数获得指向参数(控件ID)控件的临时指针 //EnableWindow()函数用于启用/禁用控件,false禁用,true启用 GetDlgItem(IDC_START)-EnableWindow(false); //创建监听线程this是对话框的指针 HANDLE h CreateThread(NULL, 0, AccpThreadProc, this, 0, NULL); if (h ! NULL) CloseHandle(h); return; }3.创建监听线程函数编写监听线程的函数首先在CChatServerDlg类中声明。static DWORD WINAPI AccpThreadProc(LPVOID pParam);然后在ChatServerDlg.cpp中给出定义。DWORD WINAPI CChatServerDlg::AccpThreadProc(LPVOID pParam) { CChatServerDlg* thisDlg (CChatServerDlg*)pParam; while (true) { sockaddr_in clientAddr { 0 }; int iLen sizeof(sockaddr_in); //取出队列头部的请求,并创建专门与该客户端通信的套接字 SOCKET accSock accept(thisDlg-m_listenSocket, (sockaddr*)clientAddr, iLen); if (accSock INVALID_SOCKET) { AfxMessageBox(_T(监听失败!!!)); return 0; } //创建ClientItem对象,保存客户端信息 ClientItem* pClient new ClientItem(); pClient-cltSocket accSock; char IP[20]; //点分十进制格式IP地址 inet_ntop(AF_INET, (void*)clientAddr.sin_addr, IP, 16); //转换IP地址格式 pClient-cltIP.Format(_T(%S), IP); //保存IP地址 pClient-cltPort clientAddr.sin_port; //保存端口号 pClient-pChatDlg thisDlg; //保存服务器对话框 thisDlg-m_clients.push_back(pClient); //添加到向量中 thisDlg-m_nClients; //增加登录客户端数 CString msgIP(pClient-cltIP); CString msgPort; msgPort.Format(_T(:%d), pClient-cltPort); CString msg msgIP msgPort 登录; //编辑即将显示的文本 //向对话框发送消息,更新界面 //::(全局作用域符):强制指定标识符变量、函数、类型属于全局命名空间Global Namespace而非当前局部作用域或某个特定的命名空间/类。 //SendMessage:将对应消息WM_UPDATE_MSG添加进目标窗口thisDlg并等待 //GetSafeHwnd:安全地获取对应窗口的句柄 //WM_UPDATE_MSG:自定义的消息ID //后面两个为该消息对应相应函数的参数 ::SendMessage(thisDlg-GetSafeHwnd(), WM_UPDATE_MSG, NULL, (LPARAM)msg); //创建一个接收用户消息的线程 HANDLE h CreateThread(NULL, 0, RecvThreadProc, pClient, 0, NULL); if (h ! NULL) CloseHandle(h); return 0; } }4.创建接收数据线程函数为接收用户消息的线程编写线程函数先在CChatServerDlg类中声明。static DWORD WINAPI RecvThreadProc(LPVOID pParam);在ChatServer.cpp给出定义。DWORD WINAPI CChatServerDlg::RecvThreadProc(LPVOID pParam) { ClientItem* thisClient (ClientItem*)pParam; while (true) { TCHAR buffer[256] { 0 }; int nRecv recv(thisClient-cltSocket, (char*)buffer, sizeof(buffer), 0); if (nRecv 0) { //-1:网络出错;0:对方关闭连接 removeClient(thisClient-pChatDlg-m_clients, thisClient); return 0; } //在消息前加上用户的IP地址和端口 CString strPort; strPort.Format(_T(%d), thisClient-cltPort); CString msg thisClient-cltIP - strPort : buffer; TCHAR sendBuf[256] { 0 }; _tcscpy_s(sendBuf, 256, msg); vectorClientItem*::iterator it thisClient-pChatDlg-m_clients.begin(); //挨个发送消息 for (; it ! thisClient-pChatDlg-m_clients.end(); it) { ClientItem* pClient *it; int bufLen _tcslen(sendBuf) * sizeof(TCHAR); int len send(pClient-cltSocket, (char*)sendBuf, bufLen, 0); if (len 0) { removeClient(thisClient-pChatDlg-m_clients, pClient); } } } return 0; }5.添加removeClient()函数添加上面用到的removeClient()函数为了使用方便也定义为类的静态函数。static void removeClient(vectorClientItem*clients, ClientItem* theClient);void CChatServerDlg::removeClient(vectorClientItem*clients, ClientItem* theClient) { --(theClient-pChatDlg-m_nClients); //客户端数量减1 //向对话框发送消息,通知该客户端离线 CString msgIP(theClient-cltIP); CString msgPort; msgPort.Format(_T(:%d), theClient-cltPort); CString msg msgIP msgPort 离开; ::SendMessage(theClient-pChatDlg-GetSafeHwnd(), WM_UPDATE_MSG, NULL, (LPARAM)msg); vectorClientItem*::iterator it; for (it clients.begin(); it ! clients.end(); it) { ClientItem* pClient *it; if (pClient-cltSocket theClient-cltSocket) { clients.erase(it); break; } } delete theClient; //删除客户端 }6.对话框自定义消息响应函数想要发送自定义消息需要定义消息宏、添加消息响应函数以及完成消息映射。在ChatServerDlg.cpp文件开始部分加入以下消息宏定义。#define WM_UPDATE_MSG WM_USER100WM_USER1024是一个界限小于WM_USER的ID是系统使用的因此我们使用WM_USER100作为自定义的消息ID。在CChatServerDlg类中给出消息响应函数的声明。afx_msg LRESULT OnUpdateMsg(WPARAM wParam, LPARAM lParam);函数格式是固定的。定义如下。LRESULT CChatServerDlg::OnUpdateMsg(WPARAM wParam, LPARAM lParam) { CString* strMsg (CString*)lParam; int len m_edtMsg.GetWindowTextLength(); //原来编辑框中文本长度 m_edtMsg.SetSel(len, len); //表示选中(len,len)之间的文字,并将光标挪到那里,同时保证光标可见 m_edtMsg.ReplaceSel(*strMsg _T(\r\n)); //用新文本替换当前被选中的文本,这里是直接添加 UpdateData(false); return 0; }最后要完成消息映射在ChatServerDlg.cpp中找到如下代码并完成添加如下。BEGIN_MESSAGE_MAP(CChatServerDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_START, CChatServerDlg::OnClickedStart) ON_MESSAGE(WM_UPDATE_MSG, OnUpdateMsg) //添加的内容 ON_BN_CLICKED(IDC_EXIT, CChatServerDlg::OnClickedExit) END_MESSAGE_MAP()ON_MESSAGE是一个通用消息映射宏绑定消息和对应的处理函数。7.添加“退出程序”按钮的消息响应函数使用类向导添加消息响应函数在函数中添加一下代码。void CChatServerDlg::OnClickedExit() { // TODO: 在此添加控件通知处理程序代码 CDialogEx::OnCancel(); }这里直接调用基类的成员函数来关闭对话框。8.添加析构函数在CChatServerDlg类的构造函数声明下添加析构函数的声明。再添加函数定义如下。CChatServerDlg::~CChatServerDlg() { if (m_listenSocket ! INVALID_SOCKET) closesocket(m_listenSocket); vectorClientItem*::iterator it; for (it m_clients.begin(); it ! m_clients.end(); it) { ClientItem* pClient *it; delete pClient; } m_clients.clear(); }4.客户端编程1.对话框类中添加数据成员在CChatClientDlg类中添加负责与服务器通信的socket。SOCKET m_Socket在构造函数里完成初始化。m_Socket INVALIDE_SOCKET;2.添加“登录”按钮的消息相应函数在类向导中添加消息相应函数完善函数定义如下。void CChatClientDlg::OnClickedLogin() { // TODO: 在此添加控件通知处理程序代码 UpdateData(true); DWORD dwIP; m_ipServer.GetAddress(dwIP); m_Socket socket(AF_INET, SOCK_STREAM, 0); if (m_Socket INVALID_SOCKET) { AfxMessageBox(_T(创建socket失败!!!)); return; } sockaddr_in serverAddr; serverAddr.sin_family AF_INET; serverAddr.sin_port htons(8888); serverAddr.sin_addr.s_addr htonl(dwIP); if (connect(m_Socket, (sockaddr*)serverAddr, sizeof(serverAddr)) -1) { AfxMessageBox(_T(连接失败!!!)); return; } m_edtMsg.SetWindowTextW(_T(登录成功!!!\r\n)); GetDlgItem(IDC_LOGIN)-EnableWindow(false); GetDlgItem(IDC_SEND)-EnableWindow(true); HANDLE h CreateThread(NULL, 0, RecvThreadProc, this, 0, NULL); if (h ! NULL) CloseHandle(h); }3.创建接收数据线程的函数在CChatClientDlg类中给出声明。static DWORD WINAPI RecvThreadProc(LPVOID pParam);定义如下。DWORD WINAPI CChatClientDlg::RecvThreadProc(LPVOID pParam) { CChatClientDlg* thisDlg (CChatClientDlg*)pParam; while (true) { TCHAR buffer[256] { 0 }; int nRecv recv(thisDlg-m_Socket, (char*)buffer, sizeof(buffer), 0); if (nRecv 0) { closesocket(thisDlg-m_Socket); thisDlg-GetDlgItem(IDC_LOGIN)-EnableWindow(true); thisDlg-GetDlgItem(IDC_SEND)-EnableWindow(false); AfxMessageBox(_T(服务器失去联系请稍后再试)); break; } CString msg(buffer); ::SendMessage(thisDlg-GetSafeHwnd(), WM_UPDATE_MSG, NULL, (LPARAM)msg); } return 0; }4.对话框自定义消息响应函数消息的宏定义如下。#define WM_UPDATE_MSG WM_USER100在CChatClientDlg类中给出声明如下。LRESULT OnUpdateMsg(WPARAM w, LPARAM l);定义如下。LRESULT CChatClientDlg::OnUpdateMsg(WPARAM w, LPARAM l) { CString* strMsg (CString*)l; int len m_edtMsg.GetWindowTextLength(); m_edtMsg.SetSel(len, len); m_edtMsg.ReplaceSel(*strMsg _T(\r\n)); return 0; }完成消息映射。BEGIN_MESSAGE_MAP(CChatClientDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_LOGIN, CChatClientDlg::OnClickedLogin) ON_MESSAGE(WM_UPDATE_MSG, OnUpdateMsg) //添加行 ON_BN_CLICKED(IDC_SEND, CChatClientDlg::OnClickedSend) ON_BN_CLICKED(IDC_EXIT, CChatClientDlg::OnClickedExit) END_MESSAGE_MAP()5.为“发送”按钮添加消息响应函数在类向导中添加消息响应函数完善定义如下。void CChatClientDlg::OnClickedSend() { // TODO: 在此添加控件通知处理程序代码 UpdateData(true); TCHAR buffer[256] { 0 }; _tcscpy_s(buffer, 256, m_strSend); if (_tcslen(buffer) 0) { return; } int len send(m_Socket, (char*)buffer, _tcslen(buffer) * sizeof(TCHAR), 0); if (len 0) { closesocket(m_Socket); GetDlgItem(IDC_LOGIN)-EnableWindow(true); GetDlgItem(IDC_SEND)-EnableWindow(false); AfxMessageBox(_T(服务器失去连接请稍后再试)); } m_strSend ; UpdateData(false); }6.添加“退出程序”按钮的消息响应函数与服务器端的操作方法一致。7.添加析构函数CChatClientDlg::~CChatClientDlg() { if (m_Socket ! INVALID_SOCKET) closesocket(m_Socket); }写在最后非常感谢您能耐心读完这篇文章。如果文中有任何表述不准确、代码不够优雅或逻辑有误之处恳请各位大佬在评论区不吝指正我一定虚心学习、及时修正。如果您觉得这篇文章对您有所帮助不妨点个赞 并关注我您的每一次鼓励都是我继续输出的动力。后续我还会分享更多的内容欢迎常来一起交流一起成长 ——不想干活的十一

相关新闻

如何在Windows上优雅地管理Android应用?APK Installer场景化使用指南

如何在Windows上优雅地管理Android应用?APK Installer场景化使用指南

如何在Windows上优雅地管理Android应用?APK Installer场景化使用指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否经常需要在Windows电脑上测试An…

2026/7/24 14:57:26 阅读更多 →
DeepSeek V4 Pro 新手极速上手指南

DeepSeek V4 Pro 新手极速上手指南

在本地运行大语言模型曾经是很多开发者的“梦想项目”,但往往止步于复杂的依赖配置和令人头秃的环境报错。随着模型量化技术的成熟和推理引擎的优化,如今在消费级显卡甚至纯 CPU 环境下部署高性能模型已成为可能。当你不再依赖云端 API,而是将…

2026/7/24 6:28:35 阅读更多 →
如何通过Wand-Enhancer实现WeMod本地功能增强与远程控制

如何通过Wand-Enhancer实现WeMod本地功能增强与远程控制

如何通过Wand-Enhancer实现WeMod本地功能增强与远程控制 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer Wand-Enhancer是一个开源的本地客户端增强…

2026/7/24 7:01:51 阅读更多 →

最新新闻

【IIoT边缘计算实战】从底层协议到云端解耦:基于 MQTT 与 Python 的智能仪表数据链路构建及自动化智能仪表厂家能力评估维度

【IIoT边缘计算实战】从底层协议到云端解耦:基于 MQTT 与 Python 的智能仪表数据链路构建及自动化智能仪表厂家能力评估维度

在工业4.0与智能制造的浪潮下,OT(操作技术)与 IT(信息技术)的融合已成为必然趋势。作为连接物理世界与数字世界的桥梁,现场自动化仪表的角色正在发生深刻的演变。过去,我们关注的是仪表的机械精…

2026/7/24 14:57:12 阅读更多 →
PID控制与强化学习的融合优化实践

PID控制与强化学习的融合优化实践

1. 项目概述:当PID控制遇上强化学习在工业控制领域,PID控制器就像老司机手中的方向盘——三个参数(比例、积分、微分)的配合决定了整个系统的响应特性。但传统PID调参就像考驾照时死记硬背的"方向盘打几圈",…

2026/7/24 14:57:12 阅读更多 →
DCMTK:医学影像DICOM标准开发实战指南与架构解析

DCMTK:医学影像DICOM标准开发实战指南与架构解析

1. 项目概述:为什么DCMTK是医学影像开发的基石 如果你在医疗软件、影像处理或者医学影像设备相关的公司待过,或者自己尝试过处理CT、MRI这类影像文件,那你大概率听说过DICOM这个标准。DICOM,全称是医学数字成像和通信,…

2026/7/24 14:57:12 阅读更多 →
深度学习核心概念与神经网络架构详解

深度学习核心概念与神经网络架构详解

1. 深度学习基础概念全景解析深度学习作为机器学习的重要分支,其核心在于通过多层神经网络模拟人脑的认知机制。与传统机器学习相比,深度学习最大的特点在于能够自动从原始数据中提取多层次的特征表示,无需依赖人工设计的特征工程。这种特性使…

2026/7/24 14:57:12 阅读更多 →
AI角色生成工具在短视频创作中的应用与技术解析

AI角色生成工具在短视频创作中的应用与技术解析

1. AI角色生成工具的市场现状与需求分析 短视频创作领域正在经历一场由AI驱动的生产力革命。根据行业调研数据显示,2023年短视频创作者对角色生成工具的需求同比增长了237%,其中72%的创作者表示传统角色制作流程耗时过长。这种需求催生了一批以V2Fun为代…

2026/7/24 14:57:12 阅读更多 →
114、运动相机影像调优:高动态与电子防抖实战

114、运动相机影像调优:高动态与电子防抖实战

114、运动相机影像调优:高动态与电子防抖实战 去年夏天帮某户外品牌调一款运动相机,在川西海拔4500米的碎石路上跑测试。机器绑在越野自行车把手上,下午三点阳光直射,路面扬尘和阴影交替出现。回放素材时发现两个致命问题:一是过曝的云层和死黑的岩石之间没有任何过渡,二…

2026/7/24 14:56:11 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 17:49:47 阅读更多 →

月新闻