记得加-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;
}