#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; }