WSAStartup()和 WSACleanup()两个函数除外,其他不是强制性的

本文详细介绍了Windows Sockets规范中的Windows扩展部分如何为应用程序开发者提供开发具有Windows应用软件的功能,有助于编写更加稳定和高效的程序,并在多任务环境下使多个应用程序更好地运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

例如 Windows 3.0 )这个术语是指 Windows Sockets 进程.
Windows Sockets 规范中的针对 Windows 的扩展部分为应用程序开发者提供了开发具有
Windows 应用软件的功能。它有利于使程序员写出更加稳定并且更加高效的程序,也有助于在
非占先 Windows 版本中使多个应用程序在多任务情况下更好地运作。除了 WSAStartup()和
WSACleanup()两个函数除外,其他的 Windows 扩展函数的使用不是强制性的。
include <iostream> #include <winsock2.h> #include <thread> #include <mutex> #include <vector> #include <string> #include <conio.h> // 按键检测(Windows专用) using namespace std; #pragma comment(lib, "ws2_32.lib") const int PORT = 12345; const char* SERVER_IP = "127.0.0.1"; // 本地测试,局域网需改实际IP struct Message { // 与服务器一致的消息结构 int type; // 0:公聊 1:私聊 2:上线 3:离线 4:用户列表 char sender[64]; char receiver[64]; char content[256]; }; vector<string> onlineUsers; // 在线用户列表(排除自己) string myNickname; // 自己的昵称 bool inPrivateChat = false; // 是否在私聊模式 string privateTarget; // 私聊对象 mutex mtx; // 保护onlineUsers的锁 // 发送消息到服务器 bool sendMsg(SOCKET sock, const Message& msg) { int sent = send(sock, (const char*)&msg, sizeof(msg), 0); return sent == sizeof(msg); } // 接收线程:处理服务器消息 void recvThread(SOCKET clientSock) { Message msg; while (true) { int recvLen = recv(clientSock, (char*)&msg, sizeof(msg), 0); if (recvLen <= 0) { // 服务器断开 cout << "\n服务器连接中断!" << endl; closesocket(clientSock); exit(0); } lock_guard<mutex> lock(mtx); switch (msg.type) { case 0: // 公聊消息 cout << "\n" << msg.sender << ": " << msg.content << endl; break; case 1: // 私聊消息 cout << "\n[私聊][" << msg.sender << "]: " << msg.content << endl; break; case 2: // 他人上线 onlineUsers.push_back(msg.sender); cout << "\n" << msg.sender << " 上线了!" << endl; break; case 3: // 他人离线 for (auto it = onlineUsers.begin(); it != onlineUsers.end(); ++it) { if (*it == msg.sender) { onlineUsers.erase(it); break; } } cout << "\n" << msg.sender << " 离线了!" << endl; break; case 4: // 在线用户列表 onlineUsers.clear(); char* p = msg.content; while (*p) { onlineUsers.push_back(p); p += strlen(p) + 1; // 跳过换行符 } // 移除自己的昵称 for (auto it = onlineUsers.begin(); it != onlineUsers.end(); ++it) { if (*it == myNickname) { onlineUsers.erase(it); break; } } break; } // 提示当前模式 if (inPrivateChat) { cout << "[私聊模式:" << privateTarget << "] 输入消息(ESC退出):"; } else { cout << "[公聊模式] 输入消息(Tab选私聊对象):"; } cout.flush(); } } int main() { // 1. 输入昵称 cout << "请输入你的昵称:"; cin >> myNickname; // 2. 初始化Winsock WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { cerr << "WSAStartup失败: " << WSAGetLastError() << endl; return 1; } // 3. 创建套接字并连接服务器 SOCKET clientSock = socket(AF_INET, SOCK_STREAM, 0); if (clientSock == INVALID_SOCKET) { cerr << "socket创建失败: " << WSAGetLastError() << endl; WSACleanup(); return 1; } sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); serverAddr.sin_port = htons(PORT); if (connect(clientSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { cerr << "连接服务器失败: " << WSAGetLastError() << endl; closesocket(clientSock); WSACleanup(); return 1; } // 4. 发送上线消息(告知服务器昵称) Message onlineMsg; onlineMsg.type = 2; strcpy(onlineMsg.sender, myNickname.c_str()); sendMsg(clientSock, onlineMsg); // 5. 启动接收线程 thread recvThrd(recvThread, clientSock); recvThrd.detach(); // 6. 处理用户输入(公聊/私聊切换) cout << "\n连接成功!进入聊天室:" << endl; cout << "[公聊模式] 输入消息(Tab选私聊对象,ESC退出私聊):"; while (true) { if (_kbhit()) { // 检测按键 int key = _getch(); // 读取按键(不回显) if (key == 9) { // Tab键:进入私聊模式,切换目标 if (!onlineUsers.empty() && !inPrivateChat) { static int index = 0; privateTarget = onlineUsers[index]; index = (index + 1) % onlineUsers.size(); // 循环切换 inPrivateChat = true; cout << "\r[私聊模式:" << privateTarget << "] 输入消息(ESC退出):"; } } else if (key == 27) { // ESC键:退出私聊模式 inPrivateChat = false; privateTarget.clear(); cout << "\r[公聊模式] 输入消息(Tab选私聊对象):"; } else { // 普通字符:继续输入消息 _putch(key); // 回显按键 string content; getline(cin, content); // 读取完整行 if (content.empty()) continue; Message msg; if (inPrivateChat) { // 私聊消息 msg.type = 1; strcpy(msg.sender, myNickname.c_str()); strcpy(msg.receiver, privateTarget.c_str()); strcpy(msg.content, content.c_str()); } else { // 公聊消息 msg.type = 0; strcpy(msg.sender, myNickname.c_str()); strcpy(msg.content, content.c_str()); } sendMsg(clientSock, msg); } } } closesocket(clientSock); WSACleanup(); return 0; } 这个是client #include <iostream> #include <winsock2.h> #include <thread> #include <mutex> #include <vector> #include <string> using namespace std; #pragma comment(lib, "ws2_32.lib") // 强制链接Winsock库 const int PORT = 12345; const int BUFFER_SIZE = 1024; // 消息结构(协议) struct Message { int type; // 0:公聊 1:私聊 2:上线 3:离线 4:用户列表 char sender[64]; char receiver[64]; char content[256]; }; // 客户端信息 struct Client { SOCKET sock; string nickname; bool online; }; vector<Client> clients; // 在线客户端列表 mutex mtx; // 线程安全锁 // 发送消息到指定套接字 bool sendMsg(SOCKET sock, const Message& msg) { int sent = send(sock, (const char*)&msg, sizeof(msg), 0); return sent == sizeof(msg); } // 广播消息(可排除发送者) void broadcast(const Message& msg, SOCKET excludeSock = INVALID_SOCKET) { lock_guard<mutex> lock(mtx); for (auto& client : clients) { if (client.online && client.sock != excludeSock) { sendMsg(client.sock, msg); } } } // 发送在线用户列表给指定客户端 void sendUserList(SOCKET sock) { Message msg; msg.type = 4; lock_guard<mutex> lock(mtx); string list; for (const auto& client : clients) { if (client.online) list += client.nickname + "\n"; } strcpy(msg.content, list.c_str()); sendMsg(sock, msg); } // 处理单个客户端的线程函数 void handleClient(SOCKET clientSock) { Message msg; Client newClient; newClient.sock = clientSock; newClient.online = true; // 1. 接收客户端昵称(上线消息) int recvLen = recv(clientSock, (char*)&msg, sizeof(msg), 0); if (recvLen <= 0 || msg.type != 2) { closesocket(clientSock); return; } newClient.nickname = msg.sender; // 2. 添加到在线列表,广播上线通知 { lock_guard<mutex> lock(mtx); clients.push_back(newClient); } Message onlineMsg; onlineMsg.type = 2; strcpy(onlineMsg.sender, newClient.nickname.c_str()); strcpy(onlineMsg.content, "已上线"); broadcast(onlineMsg); // 3. 发送当前用户列表给新客户端 sendUserList(clientSock); // 4. 循环处理消息 while (true) { recvLen = recv(clientSock, (char*)&msg, sizeof(msg), 0); if (recvLen <= 0) { // 客户端断开 // 广播离线通知 Message offlineMsg; offlineMsg.type = 3; strcpy(offlineMsg.sender, newClient.nickname.c_str()); strcpy(offlineMsg.content, "已离线"); broadcast(offlineMsg); // 标记为离线 lock_guard<mutex> lock(mtx); for (auto& c : clients) { if (c.sock == clientSock) { c.online = false; break; } } break; } // 处理消息类型 if (msg.type == 0) { // 公聊:广播给所有人 broadcast(msg, clientSock); } else if (msg.type == 1) { // 私聊:转发给目标用户 lock_guard<mutex> lock(mtx); for (auto& c : clients) { if (c.online && c.nickname == msg.receiver) { sendMsg(c.sock, msg); break; } } } } closesocket(clientSock); } int main() { // 初始化Winsock WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { cerr << "WSAStartup失败: " << WSAGetLastError() << endl; return 1; } // 创建监听套接字 SOCKET listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock == INVALID_SOCKET) { cerr << "socket创建失败: " << WSAGetLastError() << endl; WSACleanup(); return 1; } // 绑定端口 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; // 监听所有IP serverAddr.sin_port = htons(PORT); if (bind(listenSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { cerr << "bind失败: " << WSAGetLastError() << endl; closesocket(listenSock); WSACleanup(); return 1; } // 开始监听 if (listen(listenSock, 5) == SOCKET_ERROR) { cerr << "listen失败: " << WSAGetLastError() << endl; closesocket(listenSock); WSACleanup(); return 1; } cout << "服务器启动,监听端口 " << PORT << " ..." << endl; // 接受客户端连接(循环) while (true) { sockaddr_in clientAddr; int clientAddrLen = sizeof(clientAddr); SOCKET clientSock = accept(listenSock, (sockaddr*)&clientAddr, &clientAddrLen); if (clientSock == INVALID_SOCKET) { cerr << "accept失败: " << WSAGetLastError() << endl; continue; } // 启动线程处理客户端 thread clientThread(handleClient, clientSock); clientThread.detach(); // 分离线程(自动回收) } closesocket(listenSock); WSACleanup(); return 0; } 这个是Server 为什么料天室中输入的消息的第一个必须要是符号,比如我打C太好了,别人只能看到太好了,以及打太厉害,我只能输出一个奇怪的符号加上厉害,别人也看不到这个奇怪的符号 私聊也有问题,我画出圈的地方到底是在谁私聊,为什么谁也收不到这些消息 第三四张图中为什么我一直尝试用A去私聊B,但无论如何B都收不到呢 请你分析一下原因,之后输出改进代码
06-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值