C++实现多人聊天室
程序员文章站
2022-03-17 19:31:59
本文实例为大家分享了c++实现多人聊天室的具体代码,供大家参考,具体内容如下udp服务端代码:// test_console.cpp : 定义控制台应用程序的入口点。//#include "stdaf...
本文实例为大家分享了c++实现多人聊天室的具体代码,供大家参考,具体内容如下
udp
服务端代码:
// test_console.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <thread> #include <cstdio> 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 endregion int 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 <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <thread> #include <cstdio> #include <string> #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 endregion int 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 <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <thread> #include <cstdio> 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 endregion int 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 <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <thread> #include <cstdio> #include <string> #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 endregion int 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; }
效果图:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: MP3内置锂电池延寿大法六招
下一篇: pycharm代码删除恢复的方法