網絡編程基礎_5.3聊天室-多人聊天室

聊天室-多人聊天室

 

 

#include <stdio.h>

// 1. 包含必要的頭文件和庫, 必須位於 windows以前
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#include <windows.h>
#include <ws2tcpip.h>

// 動態數組
#include <vector>
using namespace std;

// 用於保存全部鏈接的客戶端
vector<SOCKET> ClientList;


// 工具函數,用於判斷是否執行成功
VOID CheckResult(BOOL Value, LPCWSTR ErrMsg)
{
    // 若是 Value 非空,就表示執行成功
    if (Value == FALSE)
    {
        printf("ErrMsg: %ls\n", ErrMsg);
        system("pause"); ExitProcess(0);
    }
}


// 用於執行接收數據的線程
DWORD WINAPI ThreadRoutine(LPVOID lpThreadParameter)
{
    // 用於保存接收的數據
    CHAR Buffer[0x100] = { 0 };

    // 1. 獲取到套接字的句柄
    SOCKET Socket = (SOCKET)lpThreadParameter;

    // 2. 在循環中不斷的接收數據,若是返回值爲 > 0 表示成功
    while (recv(Socket, Buffer, 0x100, 0) > 0)
    {
        // 轉發給當前在線的除本身之外的全部客戶端
        for (size_t i = 0; i < ClientList.size(); ++i)
        {
            // 排除掉本身,不會發消息給本身
            if (ClientList[i] == Socket)
                continue;

            // 直接轉發消息
            send(ClientList[i], Buffer, 0x100, 0);
        }
    }

    // 3. 將當前的套接字關閉並從列表移除
    for (int i = 0; i < ClientList.size(); ++i)
    {
        // 找到當前對應的套接字
        if (ClientList[i] == Socket)
        {
            // 收尾工做
            closesocket(Socket);
            printf("%08X 退出了聊天室\n", Socket);
            ClientList.erase(ClientList.begin() + i);
            break;
        }
    }

    return 0;
}

int main()
{
    // 2. 初始化網絡環境並判斷是否成功[ 搜索信號(2G?3G?4G?) ]
    WSAData WsaData = { 0 };
    if (!WSAStartup(MAKEWORD(2, 2), &WsaData))
        CheckResult(WsaData.wVersion == 0x0202, L"初始化網絡環境失敗");

    // 3. 建立套接字(IP+PORT) [ 買手機 ]
    SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    CheckResult(ServerSocket != INVALID_SOCKET, L"套接字建立失敗");

    // 4. 綁定套接字,提供IP和端口 (辦手機卡)
    sockaddr_in ServerAddr = { 0 };
    ServerAddr.sin_port = htons(0x1515);        // 端口
    ServerAddr.sin_family = AF_INET;            // 協議類型
    inet_pton(AF_INET, "127.0.0.1", &ServerAddr.sin_addr.S_un);
    BOOL Result = bind(ServerSocket,    // 要綁定的套接字
        (SOCKADDR*)& ServerAddr,        // 服務器的地址和IP對應的結構體
        sizeof(sockaddr_in));            // 必需要指定大小
    CheckResult(Result != SOCKET_ERROR, L"套接字綁定失敗");

    // 5. 監聽套接字 (開機,等待鏈接)
    // -  監聽誰,最多等待多少個客戶端的連接
    Result = listen(ServerSocket, SOMAXCONN);

    // 6. 循環等待客戶端的鏈接(接電話)
    while (true)
    {
        // 接收客戶端
        int dwSize = sizeof(sockaddr_in);
        sockaddr_in ClientAddr = { 0 };        // 接收的客戶端ip和端口
        SOCKET ClientSocket = accept(ServerSocket,
            (SOCKADDR*)& ClientAddr, &dwSize);

        // 添加到容器中
        printf("%08X 進入了聊天室\n", ClientSocket);
        ClientList.push_back(ClientSocket);

        // 由於要接受每個客戶端發送的消息,因此須要分別建立線程
        CreateThread(NULL, NULL, ThreadRoutine, (LPVOID)ClientSocket, NULL, NULL);
    }

    // 9. 關閉套接字執行清理工做
    closesocket(ServerSocket);

    // 10. 清理網絡環境
    WSACleanup();

    system("pause");
    return 0;
}
相關文章
相關標籤/搜索