C++實(shí)現(xiàn)多人聊天室
更新時(shí)間:2021年06月30日 15:53:14 作者:(-: LYSM :-)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)多人聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
本文實(shí)例為大家分享了C++實(shí)現(xiàn)多人聊天室的具體代碼,供大家參考,具體內(nèi)容如下
UDP
服務(wù)端代碼:
// Test_Console.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。 // #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; // 服務(wù)端套接字 sockaddr_in sai_server; // 服務(wù)端信息(ip、端口) // 消息格式 struct umsg { int type; // 協(xié)議(1:加入 2:退出 3:發(fā)消息) char name[64]; // 用戶名字 char text[512]; // 文本信息 }; // 客戶端鏈表 typedef struct ucnode { sockaddr_in addr; // 客戶端的地址和端口號(hào) umsg msg; // 客戶端傳來(lái)的消息 ucnode* next; } *ucnode_t; #pragma endregion #pragma region 依賴函數(shù) // 鏈表插入數(shù)據(jù) 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; } // 鏈表刪除數(shù)據(jù) 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 << "我是服務(wù)端" << endl; // 初始化 WSA ,激活 socket WSADATA wsaData; if (WSAStartup( MAKEWORD(2, 2), // 規(guī)定 socket 版本為 2.2 &wsaData // 接收關(guān)于套接字的更多信息 )) { cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、服務(wù)器信息 server = socket( AF_INET, // IPV4 SOCK_DGRAM, // UDP 0 // 不指定協(xié)議 ); sai_server.sin_addr.S_un.S_addr = 0; // IP地址 sai_server.sin_family = AF_INET; // IPV4 sai_server.sin_port = htons(8090); // 傳輸協(xié)議端口 // 本地地址關(guān)聯(lián)套接字 if (bind( server, // 要與本地地址綁定的套接字 (sockaddr*)&sai_server, // 用來(lái)接收客戶端消息的 sockaddr_in 結(jié)構(gòu)體指針 sizeof(sai_server) )) { cout << "bind failed : " << GetLastError() << endl; WSACleanup(); } // 初始化客戶端鏈表 ucnode* listHead = new ucnode(); listHead->next = nullptr; ucnode* lp = listHead; // 監(jiān)聽(tīng)消息 while (1) { // 接收來(lái)自客戶端的消息 umsg msg; int len_client = sizeof(sockaddr); recvfrom( server, // 本地套接字 (char*)&msg, // 存放接收到的消息 sizeof(msg), 0, // 不修改函數(shù)調(diào)用行為 (sockaddr*)&sai_server, // 接收客戶端的IP、端口 &len_client // 接收消息的長(zhǎng)度,必須初始化,否則默認(rèn)為0 收不到消息 ); // sin_addr 轉(zhuǎn) char[](char[] 轉(zhuǎn) sin_addr 使用 inet_top) char arr_ip[20]; inet_ntop(AF_INET, &sai_server.sin_addr, arr_ip, 16); // 處理消息(1:用戶登錄,2:用戶退出,3:普通會(huì)話) 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, // 消息結(jié)構(gòu)體 sizeof(msg), 0, // 不修改函數(shù)調(diào)用行為 (sockaddr*) & lp->addr, // 目標(biāo)客戶端地址 sizeof(lp->addr) ); } lp = lp->next; } break; } } // 禁用 socket WSACleanup(); getchar(); return 0; }
客戶端代碼:
// Test_Console_2.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開(kāi)始并結(jié)束。 // #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; // 存放服務(wù)端發(fā)送的消息 // 發(fā)送和接收的信息體 struct umsg { int type; // 協(xié)議(1:登錄,2:退出,3:發(fā)消息) char name[64]; // 用戶名字 char text[512]; // 文本 }; #pragma endregion #pragma region 依賴函數(shù) // 監(jiān)聽(tīng)服務(wù)器消息 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), // 規(guī)定 socket 版本 &wsaData // 接收 socket 的更多信息 )) { cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、客戶端信息 client = socket( AF_INET, // IPV4 SOCK_DGRAM, // UDP 0 // 不指定協(xié)議 ); sai_client.sin_family = AF_INET; // IPV4 inet_pton(AF_INET, "192.168.1.105", &sai_client.sin_addr); // 服務(wù)器 IP地址 sai_client.sin_port = htons(8090); // 端口 // 輸入用戶名 string name; getline(cin, name); // 發(fā)送登錄消息 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, // 發(fā)送的消息 sizeof(msg), 0, // 不修改函數(shù)調(diào)用行為 (sockaddr*) & sai_client, // 消息目標(biāo) sizeof(sai_client) ); // 接收服務(wù)器消息 HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, 0, 0, 0); if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; } // 發(fā)送消息 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; } // 如果是會(huì)話消息 msg.type = 3; strncpy_s(msg.text, sizeof(msg.text), content.c_str(), 512); sendto( client, // 本地套接字 (char*)&msg, // 要發(fā)送的消息 sizeof(msg), 0, // 不修改函數(shù)調(diào)用行為 (sockaddr*) & sai_client, // 發(fā)送目標(biāo) sizeof(sai_client) ); } getchar(); return 0; }
效果圖:
TCP
服務(wù)器代碼:
// Test_Console.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。 // #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; // 存放服務(wù)器IP、端口 // 消息格式 struct umsg { int type; // 協(xié)議(1:登錄,2:退出,3:發(fā)消息) 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 依賴函數(shù) // 鏈表插入數(shù)據(jù) 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; } // 鏈表刪除數(shù)據(jù) 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; } // 接收客戶端消息(某個(gè)) void recvMessage(PVOID pParam) { clientInfo* cInfo = (clientInfo*)pParam; while (1) { // 接收來(lái)自客戶端的消息 umsg msg; int len_client = sizeof(sockaddr); int ret_recv = recv( cInfo->client, // 本地套接字 (char*)&msg, // 存放接收的消息 sizeof(msg), // 消息大小 0 // 不修改函數(shù)調(diào)用行為 ); if (ret_recv <= 0) { cout << msg.name << "斷開(kāi)連接: " << GetLastError() << endl; break; } cInfo->msg = msg; // sin_addr 轉(zhuǎn) char[](char[] 轉(zhuǎn) sin_addr 使用 inet_top) char arr_ip[20]; inet_ntop(AF_INET, &cInfo->saddr.sin_addr, arr_ip, 16); // 處理消息(1:登錄,2:退出,3:會(huì)話) switch (cInfo->msg.type) { case 1: // 插入數(shù)據(jù)到鏈表 insertNode(listHead,cInfo->client, cInfo->saddr,cInfo->msg); // 打印消息 cout << "[" << arr_ip << ":" << ntohs(cInfo->saddr.sin_port) << "] " << msg.name << ":" << "---登錄---" << endl; break; case 2: // 從鏈表刪除數(shù)據(jù) 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, // 發(fā)送的消息 sizeof(cInfo->msg), // 消息大小 0 // 不指定調(diào)用方式 ); int error_send = GetLastError(); if (error_send != 0) { cout << "send failed:" << error_send << endl; } } lp = lp->next; } break; } } } #pragma endregion int main() { cout << "我是服務(wù)端" << endl; // 初始化 WSA ,激活 socket WSADATA wsaData; if (WSAStartup( MAKEWORD(2, 2), // 規(guī)定 socket 版本為 2.2 &wsaData // 接收關(guān)于套接字的更多信息 )) { cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、服務(wù)器信息 server = socket( AF_INET, // IPV4 SOCK_STREAM, // TCP 0 // 不指定協(xié)議 ); sai_server.sin_addr.S_un.S_addr = 0; // IP地址 sai_server.sin_family = AF_INET; // IPV4 sai_server.sin_port = htons(8090); // 傳輸協(xié)議端口 // 本地地址關(guān)聯(lián)套接字 if (bind( server, // 要與本地地址綁定的套接字 (sockaddr*)&sai_server, // 用來(lái)接收客戶端消息的 sockaddr_in 結(jié)構(gòu)體指針 sizeof(sai_server) )) { cout << "bind failed : " << GetLastError() << endl; WSACleanup(); } // 套接字進(jìn)入監(jiān)聽(tīng)狀態(tài) listen( server, // 本地套接字 SOMAXCONN // 掛起連接隊(duì)列的最大長(zhǎng)度,SOMAXCONN:最大合理值 ); // 初始化客戶端鏈表 listHead = new ucnode(); listHead->next = nullptr; lp = listHead; // 接收消息 while (1) { // 接收登錄消息(首次連接是觸發(fā),之后發(fā)送消息不觸發(fā)) clientInfo* cInfo = new clientInfo(); int len_client = sizeof(sockaddr); cInfo->client = accept(server, (sockaddr*) &cInfo->saddr, &len_client); if (GetLastError() != 0) { continue; } // 接收登錄者的消息(每個(gè)客戶端對(duì)應(yīng)一個(gè)線程) 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" 函數(shù)。程序執(zhí)行將在此處開(kāi)始并結(jié)束。 // #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; // 協(xié)議(1:登錄,2:退出,3:發(fā)消息) char name[64]; // 用戶名字 char text[512]; // 文本 }; #pragma endregion #pragma region 依賴函數(shù) // 監(jiān)聽(tīng)服務(wù)器消息 void recvMessage() { while (1){ umsg msg; int ret_recv = recv( client, // 本地套接字 (char*)&msg, // 存放接收的消息 sizeof(msg), // 消息大小 0 // 不指定調(diào)用方式 ); 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), // 規(guī)定 socket 版本 &wsaData // 接收 socket 的更多信息 )) { cout << "WSAStartup failed : " << GetLastError() << endl; } // 初始化 socket、客戶端信息 client = socket( AF_INET, // IPV4 SOCK_STREAM, // TCP 0 // 不指定協(xié)議 ); sai_client.sin_family = AF_INET; // IPV4 inet_pton(AF_INET, "192.168.1.100", &sai_client.sin_addr); // 服務(wù)器 IP地址 sai_client.sin_port = htons(8090); // 端口 // 連接服務(wù)器 int ret_connect = connect( client, // 本地套接字 (sockaddr*) &sai_client, // 目標(biāo) 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); // 發(fā)送登錄消息 send( client, // 本地套接字 (char*)&msg, // 發(fā)送的消息 sizeof(msg), // 消息大小 0 // 不指定調(diào)用方式 ); int error_send = GetLastError(); if (error_send != 0) { cout << "send failed:" << error_send << endl; } // 接收服務(wù)器消息 HANDLE h_recvMes = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)recvMessage, 0, 0, 0); if (!h_recvMes) { cout << "CreateThread failed :" << GetLastError() << endl; } // 發(fā)送消息 while (1) { string content; getline(cin, content); // 退出消息 if (content == "quit") { msg.type = 2; send( client, // 本地套接字 (char*)&msg, // 發(fā)送的消息 sizeof(msg), // 消息大小 0 // 不指定調(diào)用方式 ); error_send = GetLastError(); if (error_send != 0) { cout << "send failed:" << error_send << endl; } closesocket(client); WSACleanup(); return 0; } // 會(huì)話消息 msg.type = 3; strncpy_s(msg.text, sizeof(msg.text), content.c_str(), 512); send( client, // 本體套接字 (char*)&msg, // 發(fā)送的消息 sizeof(msg), // 消息大小 0 // 不指定調(diào)用方式 ); error_send = GetLastError(); if (error_send != 0) { cout << "send failed:" << error_send << endl; } } getchar(); return 0; }
效果圖:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
c語(yǔ)言實(shí)現(xiàn)數(shù)組循環(huán)左移m位
這篇文章主要介紹了c語(yǔ)言實(shí)現(xiàn)數(shù)組循環(huán)左移m位,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07Android App仿微信界面切換時(shí)Tab圖標(biāo)變色效果的制作方法
這篇文章主要介紹了Android App仿微信界面切換時(shí)Tab圖標(biāo)變色效果的制作方法,重點(diǎn)講解了圖標(biāo)的繪制技巧,需要的朋友可以參考下2016-04-04C++類的靜態(tài)成員變量與靜態(tài)成員函數(shù)詳解
下面小編就為大家?guī)?lái)一篇C++類的靜態(tài)成員變量與靜態(tài)成員函數(shù)的文章。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-11-11C++、python和go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)單客戶端服務(wù)器代碼示例
這篇文章主要介紹了C++、python和go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)單客戶端服務(wù)器代碼示例,本文分別給出了3種語(yǔ)言的客戶端服務(wù)器通信代碼實(shí)例,需要的朋友可以參考下2015-03-03