C/C++实现聊天室(详解版) 您所在的位置:网站首页 迭代的三个步骤包括什么内容 C/C++实现聊天室(详解版)

C/C++实现聊天室(详解版)

2024-06-05 22:46| 来源: 网络整理| 查看: 265

文章目录 一、程序演示二、项目介绍三、代码详解服务器客户端

欢迎前往我的个人站点查看更多精彩内容:酷编程

一、程序演示

在这里插入图片描述 虽然最开始是打算写个局域网就好了的,但其实如果你有云服务器,可以向微信、QQ一样与相隔甚远的朋友聊天,只需要将客户端IP修改为云服务器的IP,并将服务器程序运行到云服务器上,端口可自行确定。

因为我原本就租了一个云服务器,所以项目里也有我已经改好了的Linux服务器代码,在Ubuntu上可正常运行。

注意: 本文只详解介绍各个功能模块代码, 如果你想要一步一步从头写出该软件, 可以看我的这篇文章:MFG开发多人聊天室

进阶项目:C++ 实现聊天室(单聊、群聊、文件传输)

该项目使用WTL界面库以及boost asio网络库进行开发,是本文的升级版本,服务器代码完全跨平台,客户端最终生成的可执行文件只有160kb

注意本项目可能存在的问题:

由于linux系统默认采用的utf-8编码,而windows系统采用的一般为GB2312或GBK编码,为了客户端能够简单方便的处理两种平台服务器的信息,我便将我的linux系统编码调整到了GBK,所以想要直接使用我编译好的这个linux服务器,需要你调整你的linux系统编码为GBK或GB2312,否则应该是启动不了的。又或者你可以重新将该linux服务器源码在你的linux上编译一次,应该就好了,这个问题比较复杂,我并没有打算去修补。

二、项目介绍

项目下载点这里

或者到本文章的最后 , 扫码进入微信公众号, 回复LANChat , 即可免费下载:

文件解压后: 在这里插入图片描述 文件介绍:

LANClient:客户端源代码LANSever:Windows服务端源代码Sever:Linux服务端源代码LANChat.sln :项目文件,用vs打开即可LANClient.exe:客户端程序LANSever.exe:windows服务端程序Sever.out:Linux服务端程序

因我使用的当前最新版本VS2022,如果你为低版本,编译可能会出现部分问题,如vs2019需进行以下设置: 在这里插入图片描述 在这里插入图片描述

三、代码详解

因考虑到初次学习网络编程的同学,所以源代码并没有进行任何封装,只是按着逻辑一步一步写的。

最简单的一个网络程序,点这里查看

服务器 #define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include #include #include #include #pragma comment(lib,"ws2_32.lib") using namespace std; map m_clients; //存储socket和名称的映射关系 unsigned __stdcall RecvMSG(void* param) { SOCKET* cli = (SOCKET*)param; //通知所有客户端 for (auto i : m_clients) { if (i.first == cli) continue; string tm = "1:"; tm += (m_clients[cli] + ":加入聊天室"); send(*i.first, tm.data(), tm.size(), 0); } //向新客户端发送已有用户 string tn = "4:"; for (auto i : m_clients) { if (i.first == cli) continue; tn +=(i.second+":"); } send(*cli, tn.data(), tn.size(), 0); for (auto i : m_clients) { if (i.first == cli) continue; int len = send(*cli, i.second.data(), i.second.size(), 0); if (len != i.second.size()) { cout for (auto i : m_clients) { if (i.first == cli) continue; string tm = "3:"+m_clients[cli] + ':'; tm += msg; send(*i.first, tm.data(),tm.size(), 0); } continue; } //客户端断连,通知 for (auto i : m_clients) { if (i.first == cli) continue; string exitMsg = "2:"; exitMsg+= (m_clients[cli] + ":退出聊天室"); send(*i.first, exitMsg.data(), exitMsg.size(), 0); } cout cout cout //判断当前是否连接,入如果已经连接,则断开连接 closesocket(m_client); m_client = -1; isCon = false; AfxMessageBox(L"成功断开连接!"); SetDlgItemText(IDC_BTN_CNT, _T("连接")); m_Member.DeleteAllItems(); return; } if (m_client == -1) { m_client = socket(AF_INET, SOCK_STREAM, 0); } UpdateData(); //更新控件中的数据到变量中 if (m_name.IsEmpty()) { AfxMessageBox(L"请输入昵称!"); return; } SOCKADDR_IN addrSev; addrSev.sin_family = AF_INET; addrSev.sin_port = htons(GetDlgItemInt(IDC_ET_PORT)); DWORD ip; m_ip.GetAddress(ip); addrSev.sin_addr.S_un.S_addr = htonl(ip); int res = connect(m_client, (sockaddr*)&addrSev, sizeof(addrSev)); //连接 if (res == -1) { AfxMessageBox(L"连接服务器失败!"); return; } m_Member.InsertItem(0, m_name); //加入当前在线成员列表 SetDlgItemText(IDC_BTN_CNT, _T("连接成功!")); _beginthreadex(0, 0, RecvMsg, &m_client, 0, 0); //开启一个线程接收来自服务器的消息 Sleep(500); SetDlgItemText(IDC_BTN_CNT, _T("断开连接!")); isCon = true; std::string na = WtoA(m_name); //将宽字符转化为窄字符 send(m_client, na.data(), na.size(), 0); //发送昵称

在这里插入图片描述 发送消息按钮:

if (m_client == -1) { //还未连接服务器 SetDlgItemText(IDC_BTN_SEND, _T("网络错误!")); Sleep(500); SetDlgItemText(IDC_BTN_SEND, _T("发送")); return; } CString msg; GetDlgItemText(IDC_ET_MSG, msg); if (msg.IsEmpty()) { SetDlgItemText(IDC_BTN_SEND, _T("消息为空!")); Sleep(500); SetDlgItemText(IDC_BTN_SEND, _T("发送")); return; } UpdateData(); //将控件数据更新到变量中 std::string str = WtoA(msg); int len = send(m_client, str.data(), str.size(), 0); //发送消息 if (len == str.size()) { msg = _T("@你:") + msg + _T("\r\n"); m_et_Msg.Append(msg); SetDlgItemText(IDC_ET_MSG, _T("")); UpdateData(false); } m_showMSg.LineScroll(m_showMSg.GetLineCount() - 10); //滚动历史消息,保证显示最新消息

在这里插入图片描述 接收消息的线程:

unsigned __stdcall CLANClientDlg::RecvMsg(void* param) { SOCKET* cli = (SOCKET*)param; while (1) { char* buf = new char[0xFF]{}; int len = recv(*cli, buf, 0xFF, 0); if (len //接受消息发送错误 char* msg = (char*)lParam; delete[] msg; UpdateData(); m_et_Msg.Append(_T("你已经断线!\r\n")); UpdateData(false); m_Member.DeleteAllItems(); return -1; } char* msg = (char*)lParam; if (msg[0] == '1' && msg[1] == ':') { //1:有新成员加入 USES_CONVERSION; CString s = A2W(&msg[2]); UpdateData(); m_et_Msg.Append(s + L"\r\n"); UpdateData(false); int index = s.Find(L':'); s.GetBuffer()[index] = L'\0'; m_Member.InsertItem(0,s); } else if (msg[0] == '2' && msg[1] == ':') { //2:有成员退出 USES_CONVERSION; CString s = A2W(&msg[2]); UpdateData(); m_et_Msg.Append(s + L"\r\n"); UpdateData(false); int index = s.Find(L':'); s.GetBuffer()[index] = L'\0'; for (int i = 0; i m_Member.DeleteItem(i); break; } } } else if (msg[0] == '3' && msg[1] == ':') { //3:正常接收消息 USES_CONVERSION; CString s = A2W(&msg[2]); UpdateData(); m_et_Msg.Append(s + L"\r\n"); UpdateData(false); } else if (msg[0] == '4' && msg[1] == ':') { //4:更新已有的成员 USES_CONVERSION; CString s = A2W(&msg[2]); int index = 0; for (int i = 0; i s.GetBuffer()[i] = L'\0'; m_Member.InsertItem(0, &s.GetBuffer()[index]); index = i + 1; } } } m_showMSg.LineScroll(m_showMSg.GetLineCount() - 10); //滚动消息列表到最新 delete[] msg; //删除分配的内存,避免内存泄露 return 0;

在这里插入图片描述 设置按钮:

if (isSet) { RECT rect; GetWindowRect(&rect); rect.right += 360; MoveWindow(&rect); } else { RECT rect; GetWindowRect(&rect); rect.right -= 360; MoveWindow(&rect); } isSet = !isSet;

在这里插入图片描述 该按钮作用就是隐藏或展示右边的内容



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有