记得加-lws2_32!
#include <iostream>
#include <string>
#include <cstring>
#include <winsock2.h>
#include <windows.h>
#include <process.h>
#include <shellapi.h> // 用于系统通知
// 你需要在 Dev-C++ 的项目选项 -> 参数 -> 链接器中添加 "-lws2_32" 来链接此库
// #pragma comment(lib, "ws2_32") // 已移除,GCC 不支持
using namespace std;
// --- 全局变量 ---
bool g_running = true;
SOCKET g_sock = INVALID_SOCKET;
sockaddr_in g_remote_addr;
HANDLE hStdOut;
int g_message_count = 0; // 用于追踪聊天内容的行数
// --- 通知功能所需的结构体和常量 ---
#define WM_TRAY_NOTIFY (WM_USER + 1001)
#define IDI_INFORMATION 7
typedef struct tagNOTIFY_PARAM {
char title[256];
char message[512];
} NOTIFY_PARAM, *PNOTIFY_PARAM;
// --- 辅助函数:获取控制台窗口的总行数 ---
int GetConsoleHeight() {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hStdOut, &csbi)) {
return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
return 25;
}
// --- 通知功能实现 ---
DWORD WINAPI NotificationThread(LPVOID lpParam) {
PNOTIFY_PARAM pParam = (PNOTIFY_PARAM)lpParam;
if (pParam == NULL) return 1;
NOTIFYICONDATA nid = {0};
#ifdef _WIN32_WINNT
nid.cbSize = NOTIFYICONDATA_V2_SIZE;
#else
nid.cbSize = sizeof(NOTIFYICONDATA);
#endif
nid.hWnd = GetForegroundWindow();
nid.uID = 1001;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_INFO;
nid.uCallbackMessage = WM_TRAY_NOTIFY;
nid.hIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_INFORMATION), IMAGE_ICON, 0, 0, LR_SHARED);
strncpy(nid.szInfoTitle, pParam->title, _countof(nid.szInfoTitle) - 1);
nid.szInfoTitle[_countof(nid.szInfoTitle) - 1] = '\0'; // 手动添加结束符
strncpy(nid.szInfo, pParam->message, _countof(nid.szInfo) - 1);
nid.szInfo[_countof(nid.szInfo) - 1] = '\0';
nid.uTimeout = 3000;
nid.dwInfoFlags = NIIF_INFO;
Shell_NotifyIcon(NIM_ADD, &nid);
Shell_NotifyIcon(NIM_MODIFY, &nid);
Sleep(4000);
Shell_NotifyIcon(NIM_DELETE, &nid);
if (nid.hIcon) {
DestroyIcon(nid.hIcon);
}
delete pParam;
return 0;
}
void ShowSystemNotification(const char* title, const char* message) {
if (!title || !message) return;
PNOTIFY_PARAM pParam = new NOTIFY_PARAM;
// C++98 兼容的安全字符串复制
size_t title_len = strlen(title);
if(title_len >= 256) title_len = 255;
memcpy(pParam->title, title, title_len);
pParam->title[title_len] = '\0';
size_t message_len = strlen(message);
if(message_len >= 512) message_len = 511;
memcpy(pParam->message, message, message_len);
pParam->message[message_len] = '\0';
HANDLE hThread = CreateThread(
NULL,
0,
NotificationThread,
pParam,
0,
NULL
);
if (hThread != NULL) {
CloseHandle(hThread);
} else {
delete pParam;
cout << "创建通知线程失败,错误码:" << GetLastError() << endl;
}
}
// --- 通知功能结束 ---
// --- 接收线程函数 ---
unsigned int __stdcall ReceiveThreadFunc(void* pArg) {
char buffer[1024];
sockaddr_in senderAddr;
int senderAddrLen = sizeof(senderAddr);
while (g_running) {
memset(buffer, 0, sizeof(buffer));
int result = recvfrom(g_sock, buffer, sizeof(buffer) - 1, 0,
(sockaddr*)&senderAddr, &senderAddrLen);
if (result > 0) {
// --- 核心修复部分开始 ---
g_message_count++;
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hStdOut, &csbi);
int console_height = GetConsoleHeight();
int target_y = 6 + g_message_count;
if (target_y > csbi.srWindow.Bottom) {
SMALL_RECT scroll_rect;
scroll_rect.Left = 0;
scroll_rect.Top = csbi.srWindow.Top + 1;
scroll_rect.Right = csbi.srWindow.Right;
scroll_rect.Bottom = csbi.srWindow.Bottom + 1;
COORD scroll_origin = {0, csbi.srWindow.Top + 1};
CHAR_INFO fill;
fill.Char.AsciiChar = ' ';
fill.Attributes = csbi.wAttributes;
ScrollConsoleScreenBuffer(hStdOut, &scroll_rect, NULL, scroll_origin, &fill);
}
cout << "\r\n>> [收到] " << buffer << "\r\n";
cout << "[发送] ";
fflush(stdout);
GetConsoleScreenBufferInfo(hStdOut, &csbi);
COORD new_pos;
new_pos.X = csbi.dwCursorPosition.X;
new_pos.Y = target_y;
SetConsoleCursorPosition(hStdOut, new_pos);
// --- 核心修复部分结束 ---
// --- 新增:触发系统通知 ---
ShowSystemNotification("来自聊天室的新消息", buffer);
MessageBox(NULL,buffer,"来自聊天室的新消息",MB_OK);
// --- 通知代码结束 ---
}
else if (result == SOCKET_ERROR) {
int errCode = WSAGetLastError();
if (errCode == WSAECONNRESET) {
continue;
}
else if (errCode == WSAENOTSOCK) {
break;
}
}
Sleep(100);
}
return 0;
}
int main() {
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
cout << "=== 局域网UDP聊天室 (C++98版) ===" << endl;
// 1. 初始化 Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "WSA初始化失败!" << endl;
return -1;
}
// 2. 创建 Socket
g_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (g_sock == INVALID_SOCKET) {
cout << "Socket创建失败!" << endl;
WSACleanup();
return -1;
}
// 3. 绑定本地端口
sockaddr_in localAddr;
memset(&localAddr, 0, sizeof(localAddr));
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(60001);
localAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(g_sock, (sockaddr*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) {
cout << "绑定端口失败! 可能是端口被占用。" << endl;
closesocket(g_sock);
WSACleanup();
return -1;
}
// 4. 设置目标IP
string targetIP;
int targetPort = 60001;
cout << "请输入对方IP地址: ";
cin >> targetIP;
cin.ignore();
memset(&g_remote_addr, 0, sizeof(g_remote_addr));
g_remote_addr.sin_family = AF_INET;
g_remote_addr.sin_port = htons(targetPort);
g_remote_addr.sin_addr.s_addr = inet_addr(targetIP.c_str());
if (g_remote_addr.sin_addr.s_addr == INADDR_NONE) {
cout << "无效的IP地址!" << endl;
closesocket(g_sock);
WSACleanup();
return -1;
}
cout << "已连接到 " << targetIP << ":" << targetPort << endl;
cout << "开始聊天 (输入 'exit' 退出):" << endl;
// 5. 启动线程
unsigned int threadID;
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ReceiveThreadFunc, NULL, 0, &threadID);
// 6. 主循环
string msg;
while (g_running) {
cout << "[发送] ";
getline(cin, msg);
if (msg == "exit") {
g_running = false;
break;
}
if (!msg.empty()) {
int sendResult = sendto(g_sock, msg.c_str(), msg.length(), 0,
(sockaddr*)&g_remote_addr, sizeof(g_remote_addr));
if (sendResult == SOCKET_ERROR) {
int err = WSAGetLastError();
if(err == WSAECONNRESET) {
cout << "\r[系统] 警告:发送失败,对方可能未开启或未响应(10054)" << endl;
cout << "[发送] ";
}
}
}
}
// 7. 清理
g_running = false; // 通知接收线程停止
closesocket(g_sock);
if (hThread) {
WaitForSingleObject(hThread, 2000);
CloseHandle(hThread);
}
WSACleanup();
return 0;
}