千家信息网

C++如何实现多人聊天室

发表于:2024-11-12 作者:千家信息网编辑
千家信息网最后更新 2024年11月12日,本篇内容介绍了"C++如何实现多人聊天室"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!UDP服务端代
千家信息网最后更新 2024年11月12日C++如何实现多人聊天室

本篇内容介绍了"C++如何实现多人聊天室"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

UDP

服务端代码:

// Test_Console.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include #include #include #include #include #include using namespace std;#pragma region 全局变量SOCKET server;     // 服务端套接字sockaddr_in sai_server;   // 服务端信息(ip、端口)// 消息格式struct umsg { int type;    // 协议(1:加入 2:退出 3:发消息) char name[64];   // 用户名字 char text[512];   // 文本信息};// 客户端链表typedef struct ucnode { sockaddr_in addr;  // 客户端的地址和端口号 umsg msg;    // 客户端传来的消息 ucnode* next;} *ucnode_t;#pragma endregion#pragma region 依赖函数// 链表插入数据ucnode* insertNode(ucnode* head, sockaddr_in addr,umsg msg) { ucnode* newNode = new ucnode(); newNode->addr = addr; newNode->msg = msg; ucnode* p = head; if (p == nullptr) {  head = newNode; } else {  while (p->next != nullptr) {   p = p->next;  }  p->next = newNode; } return head;}// 链表删除数据ucnode* deleteNode(ucnode* head, umsg msg) { ucnode* p = head; if (p == nullptr) {  return head; } if (strcmp(p->msg.name, msg.name) == 0){  head = p->next;  delete p;  return head; } while (p->next != nullptr && strcmp(p->next->msg.name, msg.name) != 0) {  p = p->next; } if (p->next == nullptr) {  return head; } ucnode* deleteNode = p->next; p->next = deleteNode->next; delete deleteNode; return head;}#pragma endregionint main(){ cout << "我是服务端" << endl; // 初始化 WSA ,激活 socket WSADATA wsaData; if (WSAStartup(  MAKEWORD(2, 2),   // 规定 socket 版本为 2.2  &wsaData    // 接收关于套接字的更多信息  )) {  cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、服务器信息 server = socket(  AF_INET,   // IPV4  SOCK_DGRAM,  // UDP  0    // 不指定协议  ); sai_server.sin_addr.S_un.S_addr = 0; // IP地址 sai_server.sin_family = AF_INET;  // IPV4 sai_server.sin_port = htons(8090);  // 传输协议端口 // 本地地址关联套接字 if (bind(  server,      // 要与本地地址绑定的套接字  (sockaddr*)&sai_server,  // 用来接收客户端消息的 sockaddr_in 结构体指针  sizeof(sai_server)     )) {  cout << "bind failed : " << GetLastError() << endl;  WSACleanup(); } // 初始化客户端链表 ucnode* listHead = new ucnode(); listHead->next = nullptr; ucnode* lp = listHead; // 监听消息 while (1) {  // 接收来自客户端的消息  umsg msg;  int len_client = sizeof(sockaddr);  recvfrom(   server,      // 本地套接字   (char*)&msg,     // 存放接收到的消息   sizeof(msg),        0,        // 不修改函数调用行为   (sockaddr*)&sai_server,  // 接收客户端的IP、端口   &len_client     // 接收消息的长度,必须初始化,否则默认为0 收不到消息      );    // sin_addr 转 char[](char[] 转 sin_addr 使用 inet_top)  char arr_ip[20];  inet_ntop(AF_INET, &sai_server.sin_addr, arr_ip, 16);    // 处理消息(1:用户登录,2:用户退出,3:普通会话)  switch (msg.type) {  case 1:    insertNode(listHead, sai_server, msg);    cout << "[" << arr_ip << ":" << ntohs(sai_server.sin_port) << "] " << msg.name << ":" << "---登录---" << endl;   break;  case 2:   deleteNode(listHead, msg);   cout << "[" << arr_ip << ":" << ntohs(sai_server.sin_port) << "] " << msg.name << ":" << "---退出---" << endl;   break;  case 3:   cout << "[" << arr_ip << ":" << ntohs(sai_server.sin_port) << "] " << msg.name << ":" << msg.text << endl;   // 更新 msg.text   lp = listHead;   while (lp) {    if (strcmp(lp->msg.name, msg.name) == 0) {     strncpy(lp->msg.text, msg.text, sizeof(msg.text));     lp->msg.type = msg.type;     break;    }    lp = lp->next;   }   // 向其他客户端广播(除自己之外)   lp = listHead;   while (lp) {    if (strcmp(lp->msg.name,"") != 0 && strcmp(lp->msg.name, msg.name) != 0) {     sendto(      server,      // 本地套接字      (char*)&msg,     // 消息结构体      sizeof(msg),           0,        // 不修改函数调用行为      (sockaddr*) & lp->addr,  // 目标客户端地址      sizeof(lp->addr)     );    }    lp = lp->next;   }   break;  } } // 禁用 socket WSACleanup(); getchar();    return 0;}

客户端代码:

// Test_Console_2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include #include #include #include #include #include #include #pragma comment(lib,"ws2_32.lib")using namespace std;#pragma region 全局变量SOCKET client;     // 客户端套接字  sockaddr_in sai_client;   // 存放客户端地址、端口sockaddr_in sai_server;   // 存放服务端发送的消息// 发送和接收的信息体struct umsg { int type;     // 协议(1:登录,2:退出,3:发消息) char name[64];    // 用户名字 char text[512];    // 文本};#pragma endregion#pragma region 依赖函数// 监听服务器消息void recvMessage(){ while (1){  umsg msg;  int len_server = sizeof(sockaddr);  int len = recvfrom(client, (char*)&msg,sizeof(msg),0,(sockaddr*)&sai_server,&len_server);    cout << msg.name << ": " << msg.text << endl; }}#pragma endregionint main(){ cout << "我是客户端" << endl; // 初始化 WSA ,激活 socket WSADATA wsaData; if (WSAStartup(  MAKEWORD(2, 2),  // 规定 socket 版本  &wsaData   // 接收 socket 的更多信息  )) {  cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、客户端信息 client = socket(  AF_INET,  // IPV4  SOCK_DGRAM,  // UDP  0    // 不指定协议  ); sai_client.sin_family = AF_INET;         // IPV4 inet_pton(AF_INET, "192.168.1.105", &sai_client.sin_addr);   // 服务器 IP地址 sai_client.sin_port = htons(8090);         // 端口 // 输入用户名 string name; getline(cin, name); // 发送登录消息 umsg msg; msg.type = 1; strncpy_s(msg.name, sizeof(msg.name), name.c_str(), 64); strncpy_s(msg.text, sizeof(msg.text), "", 512); sendto(  client,       // 本地套接字  (char*)&msg,      // 发送的消息  sizeof(msg),   0,         // 不修改函数调用行为  (sockaddr*) & sai_client,  // 消息目标  sizeof(sai_client) ); // 接收服务器消息 HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, 0, 0, 0); if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; }     // 发送消息 while (1) {  string content;  getline(cin, content);    // 如果是退出消息  if (content == "quit") {   msg.type = 2;   sendto(client, (char*)&msg, sizeof msg, 0, (struct sockaddr*) & sai_client, sizeof(sai_client));   closesocket(client);   WSACleanup();   return 0;  }  // 如果是会话消息  msg.type = 3;  strncpy_s(msg.text, sizeof(msg.text), content.c_str(), 512);  sendto(   client,       // 本地套接字   (char*)&msg,      // 要发送的消息   sizeof(msg),    0,         // 不修改函数调用行为   (sockaddr*) & sai_client,   // 发送目标   sizeof(sai_client)  ); }    getchar();    return 0;}

效果图:

TCP

服务器代码:

// Test_Console.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include #include #include #include #include #include using namespace std;#pragma region 全局变量SOCKET server;    // 本地套接字sockaddr_in sai_server;  // 存放服务器IP、端口// 消息格式struct umsg { int type;    // 协议(1:登录,2:退出,3:发消息) char name[64];   // 用户名字 char text[512];   // 文本信息};// 客户端信息struct clientInfo { SOCKET client; sockaddr_in saddr; umsg msg;};// 客户端链表typedef struct ucnode { clientInfo cInfo; ucnode* next;} *ucnode_t;ucnode* listHead;  // 客户端链表头ucnode* lp;    // 客户端链表指针#pragma endregion#pragma region 依赖函数// 链表插入数据ucnode* insertNode(ucnode* head,SOCKET client, sockaddr_in addr, umsg msg) { ucnode* newNode = new ucnode(); newNode->cInfo.client = client; newNode->cInfo.saddr = addr;  newNode->cInfo.msg = msg; ucnode* p = head; if (p == nullptr) {  head = newNode; } else {  while (p->next != nullptr) {   p = p->next;  }  p->next = newNode; } return head;}// 链表删除数据ucnode* deleteNode(ucnode* head, SOCKET client) { ucnode* p = head; if (p == nullptr) {  return head; } if (p->cInfo.client == client) {  head = p->next;  delete p;  return head; } while (p->next != nullptr && p->next->cInfo.client != client) {  p = p->next; } if (p->next == nullptr) {  return head; } ucnode* deleteNode = p->next; p->next = deleteNode->next; delete deleteNode; return head;}// 接收客户端消息(某个)void recvMessage(PVOID pParam) { clientInfo* cInfo = (clientInfo*)pParam; while (1) {  // 接收来自客户端的消息  umsg msg;  int len_client = sizeof(sockaddr);  int ret_recv = recv(   cInfo->client, // 本地套接字    (char*)&msg, // 存放接收的消息   sizeof(msg), // 消息大小   0    // 不修改函数调用行为  );   if (ret_recv <= 0) { cout << msg.name << "断开连接: " << GetLastError() << endl; break; }  cInfo->msg = msg;  // sin_addr 转 char[](char[] 转 sin_addr 使用 inet_top)  char arr_ip[20];  inet_ntop(AF_INET, &cInfo->saddr.sin_addr, arr_ip, 16);  // 处理消息(1:登录,2:退出,3:会话)  switch (cInfo->msg.type) {  case 1:   // 插入数据到链表   insertNode(listHead,cInfo->client, cInfo->saddr,cInfo->msg);   // 打印消息   cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << "---登录---" << endl;   break;  case 2:   // 从链表删除数据   deleteNode(listHead, /*cInfo->msg*/cInfo->client);   // 打印消息   cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << "---退出---" << endl;   break;  case 3:   // 打印消息   cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << cInfo->msg.text << endl;   // 向其他客户端广播(除自己之外)   lp = listHead;   while (lp) {    if (strcmp(lp->cInfo.msg.name, "") != 0 && strcmp(lp->cInfo.msg.name, cInfo->msg.name) != 0) {     send(      lp->cInfo.client,  // 本地套接字      (char*)&cInfo->msg,  // 发送的消息      sizeof(cInfo->msg),  // 消息大小      0      // 不指定调用方式     );     int error_send = GetLastError();     if (error_send != 0) { cout << "send failed:" << error_send << endl; }    }    lp = lp->next;   }   break;  } }}#pragma endregionint main(){ cout << "我是服务端" << endl; // 初始化 WSA ,激活 socket WSADATA wsaData; if (WSAStartup(  MAKEWORD(2, 2),   // 规定 socket 版本为 2.2  &wsaData    // 接收关于套接字的更多信息 )) {  cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、服务器信息 server = socket(  AF_INET,   // IPV4  SOCK_STREAM, // TCP  0    // 不指定协议 ); sai_server.sin_addr.S_un.S_addr = 0; // IP地址 sai_server.sin_family = AF_INET;  // IPV4 sai_server.sin_port = htons(8090);  // 传输协议端口 // 本地地址关联套接字 if (bind(  server,      // 要与本地地址绑定的套接字  (sockaddr*)&sai_server,  // 用来接收客户端消息的 sockaddr_in 结构体指针  sizeof(sai_server) )) {  cout << "bind failed : " << GetLastError() << endl;  WSACleanup(); } // 套接字进入监听状态 listen(  server,  // 本地套接字  SOMAXCONN // 挂起连接队列的最大长度,SOMAXCONN:最大合理值 ); // 初始化客户端链表 listHead = new ucnode(); listHead->next = nullptr; lp = listHead;   // 接收消息 while (1) {  // 接收登录消息(首次连接是触发,之后发送消息不触发)  clientInfo* cInfo = new clientInfo();  int len_client = sizeof(sockaddr);  cInfo->client = accept(server, (sockaddr*) &cInfo->saddr, &len_client);   if (GetLastError() != 0) { continue; }    // 接收登录者的消息(每个客户端对应一个线程)  HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, cInfo, 0, 0);  if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; } } // 禁用 socket WSACleanup(); getchar();    return 0;}

客户端代码:

// Test_Console_2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include #include #include #include #include #include #include #pragma comment(lib,"ws2_32.lib")using namespace std;#pragma region 全局变量SOCKET client;     // 本地套接字sockaddr_in sai_client;   // 存放客户端IP地址、端口// 消息格式struct umsg { int type;     // 协议(1:登录,2:退出,3:发消息) char name[64];    // 用户名字 char text[512];    // 文本};#pragma endregion#pragma region 依赖函数// 监听服务器消息void recvMessage(){ while (1){  umsg msg;  int ret_recv = recv(   client,   // 本地套接字   (char*)&msg, // 存放接收的消息   sizeof(msg), // 消息大小   0    // 不指定调用方式  );  if (ret_recv <= 0) { cout << "recv failed: " << GetLastError() << endl; break; }    // 打印消息  cout << msg.name << ": " << msg.text << endl; }}#pragma endregionint main(){ cout << "我是客户端" << endl; // 初始化 WSA ,激活 socket WSADATA wsaData; if (WSAStartup(  MAKEWORD(2, 2),  // 规定 socket 版本  &wsaData   // 接收 socket 的更多信息 )) {  cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、客户端信息 client = socket(  AF_INET,  // IPV4  SOCK_STREAM, // TCP  0    // 不指定协议 ); sai_client.sin_family = AF_INET;         // IPV4 inet_pton(AF_INET, "192.168.1.100", &sai_client.sin_addr);   // 服务器 IP地址 sai_client.sin_port = htons(8090);         // 端口 // 连接服务器 int ret_connect = connect(  client,      // 本地套接字  (sockaddr*) &sai_client,  // 目标  sizeof(sai_client) );if (ret_connect != 0) { cout << "connect failed:" << GetLastError() << endl; } // 输入用户名 umsg msg; msg.type = 1; string name; getline(cin, name); strncpy_s(msg.name, sizeof(msg.name), name.c_str(), 64); strncpy_s(msg.text, sizeof(msg.text), "", 512); // 发送登录消息 send(  client,   // 本地套接字  (char*)&msg, // 发送的消息  sizeof(msg), // 消息大小  0    // 不指定调用方式 );  int error_send = GetLastError(); if (error_send != 0) { cout << "send failed:" << error_send << endl; }  // 接收服务器消息 HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, 0, 0, 0); if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; } // 发送消息 while (1) {  string content;  getline(cin, content);  // 退出消息  if (content == "quit") {   msg.type = 2;   send(    client,   // 本地套接字    (char*)&msg, // 发送的消息    sizeof(msg), // 消息大小    0    // 不指定调用方式   );   error_send = GetLastError();   if (error_send != 0) { cout << "send failed:" << error_send << endl; }   closesocket(client);   WSACleanup();   return 0;  }    // 会话消息  msg.type = 3;  strncpy_s(msg.text, sizeof(msg.text), content.c_str(), 512);  send(   client,   // 本体套接字   (char*)&msg, // 发送的消息   sizeof(msg), // 消息大小   0    // 不指定调用方式  );  error_send = GetLastError();  if (error_send != 0) { cout << "send failed:" << error_send << endl; } }    getchar();    return 0;}

效果图:

"C++如何实现多人聊天室"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0