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