├── .gitattributes ├── .gitignore ├── README.md ├── chat_client_code ├── addfriend.cpp ├── addfriend.h ├── addfriend.ui ├── addgroup.cpp ├── addgroup.h ├── addgroup.ui ├── chat_server.sln ├── chat_soft.pro ├── chatlist.cpp ├── chatlist.h ├── chatlist.ui ├── creategroup.cpp ├── creategroup.h ├── creategroup.ui ├── groupchat.cpp ├── groupchat.h ├── groupchat.ui ├── main.cpp ├── privatechat.cpp ├── privatechat.h ├── privatechat.ui ├── recvthread.cpp ├── recvthread.h ├── sendthread.cpp ├── sendthread.h ├── widget.cpp ├── widget.h └── widget.ui ├── chat_database.cpp ├── chat_database.h ├── chat_server_project.sln ├── chat_server_project.vcxproj ├── chatlist.cpp ├── chatlist.h ├── main.cpp ├── myassets ├── image-20220912180434913.png └── image-20220912180641606.png ├── server.cpp ├── server.h ├── 在阿里云服务器上搭建开发环境.txt ├── 服务器与客户端接口文档.txt └── 测试客户端.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # liuzhensen 2022/8/3 2 | ############################################################################### 3 | # Set default behavior to automatically normalize line endings. 4 | ############################################################################### 5 | * text=auto 6 | 7 | ############################################################################### 8 | # Set default behavior for command prompt diff. 9 | # 10 | # This is need for earlier builds of msysgit that does not have it on by 11 | # default for csharp files. 12 | # Note: This is only used by command line 13 | ############################################################################### 14 | #*.cs diff=csharp 15 | 16 | ############################################################################### 17 | # Set the merge driver for project and solution files 18 | # 19 | # Merging from the command prompt will add diff markers to the files if there 20 | # are conflicts (Merging from VS is not affected by the settings below, in VS 21 | # the diff markers are never inserted). Diff markers may cause the following 22 | # file extensions to fail to load in VS. An alternative would be to treat 23 | # these files as binary and thus will always conflict and require user 24 | # intervention with every merge. To do so, just uncomment the entries below 25 | ############################################################################### 26 | #*.sln merge=binary 27 | #*.csproj merge=binary 28 | #*.vbproj merge=binary 29 | #*.vcxproj merge=binary 30 | #*.vcproj merge=binary 31 | #*.dbproj merge=binary 32 | #*.fsproj merge=binary 33 | #*.lsproj merge=binary 34 | #*.wixproj merge=binary 35 | #*.modelproj merge=binary 36 | #*.sqlproj merge=binary 37 | #*.wwaproj merge=binary 38 | 39 | ############################################################################### 40 | # behavior for image files 41 | # 42 | # image files are treated as binary by default. 43 | ############################################################################### 44 | #*.jpg binary 45 | #*.png binary 46 | #*.gif binary 47 | 48 | ############################################################################### 49 | # diff behavior for common document formats 50 | # 51 | # Convert binary document formats to text before diffing them. This feature 52 | # is only available from the command line. Turn it on by uncommenting the 53 | # entries below. 54 | ############################################################################### 55 | #*.doc diff=astextplain 56 | #*.DOC diff=astextplain 57 | #*.docx diff=astextplain 58 | #*.DOCX diff=astextplain 59 | #*.dot diff=astextplain 60 | #*.DOT diff=astextplain 61 | #*.pdf diff=astextplain 62 | #*.PDF diff=astextplain 63 | #*.rtf diff=astextplain 64 | #*.RTF diff=astextplain 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | ## liuzhensen 2022/8/3 6 | 7 | # User-specific files 8 | *.rsuser 9 | *.suo 10 | *.user 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | [Aa][Rr][Mm]/ 25 | [Aa][Rr][Mm]64/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015/2017 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # Visual Studio 2017 auto generated files 37 | Generated\ Files/ 38 | 39 | # MSTest test Results 40 | [Tt]est[Rr]esult*/ 41 | [Bb]uild[Ll]og.* 42 | 43 | # NUNIT 44 | *.VisualState.xml 45 | TestResult.xml 46 | 47 | # Build Results of an ATL Project 48 | [Dd]ebugPS/ 49 | [Rr]eleasePS/ 50 | dlldata.c 51 | 52 | # Benchmark Results 53 | BenchmarkDotNet.Artifacts/ 54 | 55 | # .NET Core 56 | project.lock.json 57 | project.fragment.lock.json 58 | artifacts/ 59 | 60 | # StyleCop 61 | StyleCopReport.xml 62 | 63 | # Files built by Visual Studio 64 | *_i.c 65 | *_p.c 66 | *_h.h 67 | *.ilk 68 | *.meta 69 | *.obj 70 | *.iobj 71 | *.pch 72 | *.pdb 73 | *.ipdb 74 | *.pgc 75 | *.pgd 76 | *.rsp 77 | *.sbr 78 | *.tlb 79 | *.tli 80 | *.tlh 81 | *.tmp 82 | *.tmp_proj 83 | *_wpftmp.csproj 84 | *.log 85 | *.vspscc 86 | *.vssscc 87 | .builds 88 | *.pidb 89 | *.svclog 90 | *.scc 91 | 92 | # Chutzpah Test files 93 | _Chutzpah* 94 | 95 | # Visual C++ cache files 96 | ipch/ 97 | *.aps 98 | *.ncb 99 | *.opendb 100 | *.opensdf 101 | *.sdf 102 | *.cachefile 103 | *.VC.db 104 | *.VC.VC.opendb 105 | 106 | # Visual Studio profiler 107 | *.psess 108 | *.vsp 109 | *.vspx 110 | *.sap 111 | 112 | # Visual Studio Trace Files 113 | *.e2e 114 | 115 | # TFS 2012 Local Workspace 116 | $tf/ 117 | 118 | # Guidance Automation Toolkit 119 | *.gpState 120 | 121 | # ReSharper is a .NET coding add-in 122 | _ReSharper*/ 123 | *.[Rr]e[Ss]harper 124 | *.DotSettings.user 125 | 126 | # JustCode is a .NET coding add-in 127 | .JustCode 128 | 129 | # TeamCity is a build add-in 130 | _TeamCity* 131 | 132 | # DotCover is a Code Coverage Tool 133 | *.dotCover 134 | 135 | # AxoCover is a Code Coverage Tool 136 | .axoCover/* 137 | !.axoCover/settings.json 138 | 139 | # Visual Studio code coverage results 140 | *.coverage 141 | *.coveragexml 142 | 143 | # NCrunch 144 | _NCrunch_* 145 | .*crunch*.local.xml 146 | nCrunchTemp_* 147 | 148 | # MightyMoose 149 | *.mm.* 150 | AutoTest.Net/ 151 | 152 | # Web workbench (sass) 153 | .sass-cache/ 154 | 155 | # Installshield output folder 156 | [Ee]xpress/ 157 | 158 | # DocProject is a documentation generator add-in 159 | DocProject/buildhelp/ 160 | DocProject/Help/*.HxT 161 | DocProject/Help/*.HxC 162 | DocProject/Help/*.hhc 163 | DocProject/Help/*.hhk 164 | DocProject/Help/*.hhp 165 | DocProject/Help/Html2 166 | DocProject/Help/html 167 | 168 | # Click-Once directory 169 | publish/ 170 | 171 | # Publish Web Output 172 | *.[Pp]ublish.xml 173 | *.azurePubxml 174 | # Note: Comment the next line if you want to checkin your web deploy settings, 175 | # but database connection strings (with potential passwords) will be unencrypted 176 | *.pubxml 177 | *.publishproj 178 | 179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 180 | # checkin your Azure Web App publish settings, but sensitive information contained 181 | # in these scripts will be unencrypted 182 | PublishScripts/ 183 | 184 | # NuGet Packages 185 | *.nupkg 186 | # The packages folder can be ignored because of Package Restore 187 | **/[Pp]ackages/* 188 | # except build/, which is used as an MSBuild target. 189 | !**/[Pp]ackages/build/ 190 | # Uncomment if necessary however generally it will be regenerated when needed 191 | #!**/[Pp]ackages/repositories.config 192 | # NuGet v3's project.json files produces more ignorable files 193 | *.nuget.props 194 | *.nuget.targets 195 | 196 | # Microsoft Azure Build Output 197 | csx/ 198 | *.build.csdef 199 | 200 | # Microsoft Azure Emulator 201 | ecf/ 202 | rcf/ 203 | 204 | # Windows Store app package directories and files 205 | AppPackages/ 206 | BundleArtifacts/ 207 | Package.StoreAssociation.xml 208 | _pkginfo.txt 209 | *.appx 210 | 211 | # Visual Studio cache files 212 | # files ending in .cache can be ignored 213 | *.[Cc]ache 214 | # but keep track of directories ending in .cache 215 | !?*.[Cc]ache/ 216 | 217 | # Others 218 | ClientBin/ 219 | ~$* 220 | *~ 221 | *.dbmdl 222 | *.dbproj.schemaview 223 | *.jfm 224 | *.pfx 225 | *.publishsettings 226 | orleans.codegen.cs 227 | 228 | # Including strong name files can present a security risk 229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 230 | #*.snk 231 | 232 | # Since there are multiple workflows, uncomment next line to ignore bower_components 233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 234 | #bower_components/ 235 | 236 | # RIA/Silverlight projects 237 | Generated_Code/ 238 | 239 | # Backup & report files from converting an old project file 240 | # to a newer Visual Studio version. Backup files are not needed, 241 | # because we have git ;-) 242 | _UpgradeReport_Files/ 243 | Backup*/ 244 | UpgradeLog*.XML 245 | UpgradeLog*.htm 246 | ServiceFabricBackup/ 247 | *.rptproj.bak 248 | 249 | # SQL Server files 250 | *.mdf 251 | *.ldf 252 | *.ndf 253 | 254 | # Business Intelligence projects 255 | *.rdl.data 256 | *.bim.layout 257 | *.bim_*.settings 258 | *.rptproj.rsuser 259 | *- Backup*.rdl 260 | 261 | # Microsoft Fakes 262 | FakesAssemblies/ 263 | 264 | # GhostDoc plugin setting file 265 | *.GhostDoc.xml 266 | 267 | # Node.js Tools for Visual Studio 268 | .ntvs_analysis.dat 269 | node_modules/ 270 | 271 | # Visual Studio 6 build log 272 | *.plg 273 | 274 | # Visual Studio 6 workspace options file 275 | *.opt 276 | 277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 278 | *.vbw 279 | 280 | # Visual Studio LightSwitch build output 281 | **/*.HTMLClient/GeneratedArtifacts 282 | **/*.DesktopClient/GeneratedArtifacts 283 | **/*.DesktopClient/ModelManifest.xml 284 | **/*.Server/GeneratedArtifacts 285 | **/*.Server/ModelManifest.xml 286 | _Pvt_Extensions 287 | 288 | # Paket dependency manager 289 | .paket/paket.exe 290 | paket-files/ 291 | 292 | # FAKE - F# Make 293 | .fake/ 294 | 295 | # JetBrains Rider 296 | .idea/ 297 | *.sln.iml 298 | 299 | # CodeRush personal settings 300 | .cr/personal 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Local History for Visual Studio 338 | .localhistory/ 339 | 340 | # BeatPulse healthcheck temp database 341 | healthchecksdb -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## chat-software 2 | > 项目框架 3 | 4 | image-20220912180434913 5 | 6 | > 项目难点及解决办法 7 | 8 | - 使用**加盐`salt`+哈希加密**解决**用户注册、登录时的密码泄露**的问题; 9 | - 使用**多线程**解决**文件传输造成高并发下聊天事件堵塞及传输数据丢失**的问题 10 | 11 | > 项目介绍 12 | 13 | - 基于阿里云服务器+libevent+qt+mysql+Json等实现仿qq聊天软件,主要功能包括注册、登录、添加好友、聊天(私聊和群聊)、文件传输、建群、加群、好友上下线提醒。 14 | 15 | - 主要分为两大部分: 16 | - 第一,在linux上实现服务器端chat_server开发 17 | - 第二,在qt上实现客户端chat_client开发 18 | 19 | ## 1. chat_server 20 | 21 | ### 1.1 配置服务器端开发环境 22 | 23 | 购买阿里云服务器,配置环境,详见文件——在阿里云服务器上搭建开发环境.txt 24 | 25 | ### 1.2 创建服务器与客户端接口文档 26 | 27 | 首先创建一个满足Json数据格式的服务器与客户端传输信息或者交互接口文档,方便查看重要信息且有助于接下来实现各种功能。详见文件——服务器与客户端接口文档.txt 28 | 29 | ### 1.3 封装数据库类和链表类,用于存储用户和群聊信息 30 | 31 | 在mysql中,创建user和chatgroup两个数据库, 32 | 其中user存储用户信息,为一个用户创建一个表名为用户名的表,表中包含用户的密码password、friend好友、groupchat所在群聊 33 | chatgroup存储群聊信息,为每个群聊创建一张表名为群聊名的表,表中包含群聊的群主owner、群聊成员member 34 | 35 | ![](D:\MyCode_C++\C++资料\chat_server板书+接口文档\数据库.png) 36 | 37 | 创建online_user和group_info两个链表————————链表访问速度比数据库快,且方便后续功能的实现 38 | 其中,online_user存储在线用户的信息,节点为,包括在线用户名和在线用户的缓冲区对象bev; 39 | group_info存储群聊信息,节点为,包括群聊名称和群成员链表,其中群成员链表的节点为,只包含群成员姓名 40 | 41 | ![](D:\MyCode_C++\C++资料\chat_server板书+接口文档\链表.png) 42 | 43 | ### 1.4 封装一个服务器类class Server 44 | 45 | 首先初始化服务器,使其可以监听待连接的客户端:在Server的构造函数中,基于libevent创建事件集合,创建并绑定监听对象,设置监听队列的长度,即最多同时与多少客户端建立TCP连接,开始循环监听,一旦有客户端发起连接,则调用回调函数,在回调函数中创建工作线程来处理该客户端。在某一工作线程中,创建该连接客户端的事件集合,基于该事件集合创建bufferevent缓存区对象,给bufferevent设置回调函数,循环监听集合(监听客户端是否有数据发送过来),一旦从客户端读取到数据,调用回调函数,处理客户端发送的满足Json格式的数据,接下来根据解析到的不同”cmd“,实现服务器端不同的功能,比如注册功能对应server_register(bev, val)函数,当客户端退出或连接断开,释放该连接客户端的事件集合,该工作线程随之退出分离,释放资源。在Server的成员变量中,分别创建一个链表对象static ChatInfo* chatlist和一个数据库对象static ChatDataBase* chatdb,用于访问用户和群聊的相关信息。 46 | 47 | > Sever类 48 | 49 | ![image-20220912180641606](myassets/image-20220912180641606.png) 50 | 51 | ### 1.5 实现注册功能 52 | 53 | server_register(bev, val); // 参数1为服务器与该客户端之间的缓存区对象,参数2为客户端发送的信息 54 | 55 | - 首先连接上user数据库,chatdb->my_database_connect("user"); 56 | 57 | - 判断注册用户是否已经存在,chatdb->my_database_user_exist(val["user"].asString())) 58 | 59 | - 若已经存在,回复客户端注册失败; 60 | 61 | - 若不存在,执行以下函数,将注册用户的用户名和密码,加入到user数据库中;然后回复客户端注册成功。 62 | 63 | ```c++ 64 | chatdb->my_database_user_password(val["user"].asString(), val["password"].asString()); 65 | ``` 66 | 67 | - 关闭user数据库,chatdb->my_database_disconnect(); 68 | 69 | ### 1.6 实现用户登录功能 70 | 71 | - 首先连接上user数据库,最后再断开连接,同上,接下来涉及到访问user数据库,均需要这两步,不再赘述; 72 | 73 | - 判断登录用户是否已经存在于user数据库中: 74 | 75 | - 若不存在,回复客户端该登录用户不存在; 76 | 77 | - 若存在,判断用户登录密码是否正确: 78 | 79 | ```c++ 80 | chatdb->my_database_password_correct(val["user"].asString(), 81 | val["password"].asString() 82 | ``` 83 | 84 | - 若不正确,回复客户端,输入登录失败,密码不正确,将登录函数结束掉return 85 | 86 | - 若正确: 87 | 88 | - 向在线用户链表中加入该登录用户,chatlist->online_user->push_back(u); 89 | 90 | - 获取该登录用户好友friend_list和群聊列表group_list并且返回给客户端,便于qt客户端开发时,显示登录用户的好友和群聊列表,以实现接下来的好友上线提醒、聊天等功能; 91 | 92 | ``` 93 | chatdb->my_database_get_friend_group(val["user"].asString(), friend_list, group_list) 94 | ``` 95 | 96 | - 登录成功后,向该登录用户的所有在线好友,发送自己上线的提醒:遍历friend_list,与 chatlist->online_user的一一匹配,若某好友在线,则向其bev/客户端发送自己上线的提醒 97 | 98 | ### 1.7 实现添加好友功能 99 | 100 | - 首先判断用户user——小明将要添加的好友friend小华——是否存在于用户数据库中 101 | 102 | - 若不存在,回复用户小明,你要添加的好友小华不存在,添加好友操作结束return 103 | 104 | - 若存在,再判断两人是否已经是好友关系 105 | 106 | ```c++ 107 | chatdb->my_database_is_friend(val["user"].asString(), val["friend"].asString()) 108 | ``` 109 | 110 | - 若已经是好友关系,回复用户小明,你与添加的好友小华已经是好友关系,添加好友操作结束return 111 | 112 | - 若还不是好友关系: 113 | 114 | - 修改双方的用户表的friend好友字符串,互相添加为好友(注意,此处未考虑好友验证通过的功能,后期优化再实现); 115 | 116 | ```c++ 117 | chatdb->my_database_add_new_friend(val["user"].asString(), val["friend"].asString()); 118 | chatdb->my_database_add_new_friend(val["friend"].asString(), val["user"].asString()); 119 | ``` 120 | 121 | - 回复执行添加好友的用户小明,添加好友成功 122 | - 遍历在线用户链表,判断添加的好友小华是否在线,若在线,回复小华,小明已将你添加为好友。 123 | 124 | ### 1.8 实现创建群聊功能 125 | 126 | * 首先连接上`chatgroup`数据库,使用结束再断开连接,接下来涉及到访问`chatgroup`数据库,均需要这两步,不再赘述; 127 | 128 | * 判断群是否已存在`chatdb->my_database_group_exist(val["group"].asString())` 129 | 130 | * 若已经存在,回复客户端用户,该群已存在 131 | 132 | * 若不存在, 133 | 134 | * 在`chatgroup`中创建该群聊,初始化群聊名,群主,群成员信息 135 | 136 | ```c++ 137 | chatdb->my_database_add_new_group(val["group"].asString(), val["user"].asString()) 138 | ``` 139 | 140 | * 在`user`中,将新建群聊加入到创建该群聊用户的群聊字符串中 141 | 142 | ```c++ 143 | chatdb->my_database_user_add_group(val["user"].asString(), val["group"].asString()); 144 | ``` 145 | 146 | * 将新建群聊加入到群信息链表中 147 | 148 | ```C++ 149 | chatlist->info_add_new_group(val["group"].asString(), val["user"].asString()); 150 | ``` 151 | 152 | * 回复客户端用户,群聊创建成功 153 | 154 | ### 1.9 添加群聊 `server_add_group` 155 | 156 | * 首先判断该群聊是否存在于`chatgroup`数据库中 157 | 158 | * 如果不存在,回复客户端用户,你要添加的群聊不存在,`return`; 159 | 160 | * 如果存在,判断该用户是否已经在该群聊中,此时使用群聊信息链表,访问速度相比于数据库更快 161 | 162 | ```C++ 163 | chatlist->info_user_in_group(val["group"].asString(), val["user"].asString()) 164 | ``` 165 | 166 | * 如果该用户已经存在于该群聊中,回复客户端用户,你已经存在于该群聊中,`return`; 167 | 168 | * 若不在该群聊中 169 | 170 | * 在`user`中,将该群聊加入到该用户的群聊字符串中; 171 | 172 | 在`chatgroup`中,将该用户加入到该群聊的群成员字符串中; 173 | 174 | * 修改群聊信息链表,将该用户加入到该群聊节点的群成员链表中; 175 | 176 | ```c++ 177 | chatlist->info_group_add_user(val["group"].asString(), val["user"].asString()); 178 | ``` 179 | 180 | * 回复客户端用户,添加群聊成功 181 | 182 | ### 1.10 私聊 `server_private_chat` 183 | 184 | * 首先判断好友是否在线————遍历在线用户链表,获得其缓存区对象`to_bev` 185 | 186 | ```c++ 187 | struct bufferevent* to_bev = chatlist->info_get_friend_bev(val["user_to"].asString()); 188 | ``` 189 | 190 | * 如果`NULL == to_bev`,说明好友不在线,回复发送方,return 191 | * 如果好友在线,将发送方用户缓存区对象bev中的内容val转换为字符串s,再转发给好友的缓存区对象to_bev,再回复发送方,私聊发送成功。 192 | 193 | ### 1.11 群聊 `server_group_chat` 194 | 195 | * 首先遍历群聊信息链表找到该群聊 196 | * 遍历该群聊的群成员链表 197 | * 遍历在线用户链表,找到每个群成员(包括发送方自己)的缓存区对象`to_bev`,若不为空,转发发送方的bev,最后回复发送方,群聊发送成功 198 | 199 | ### 1.12 获取群聊成员 `server_get_group_member` 200 | 201 | * 遍历群聊信息链表,获取某群聊的群聊成员字符串member,将其返回给调用该函数的客户端 202 | 203 | ```c++ 204 | string member = chatlist->info_get_group_member(val["group"].asString()); 205 | ``` 206 | 207 | ### 1.13 用户下线 `server_user_offline` 208 | 209 | * 首先从用户在线链表`online_user`中删除该用户 210 | * 获取该用户好友friend_list 211 | * 向在线好友,发送该用户的下线提醒 212 | 213 | ### 1.14 发送文件 server_send_file 214 | 215 | * 首先判断对方是否在线 216 | 217 | * 若不在线,回复发送方,接收方不在线,return; 218 | 219 | * 若在线: 220 | 221 | * 启动新线程,创建文件服务器,处理该文件传输任务(不影响聊天等功能),引用方式获得发送方客户端和接收方客户端相对于文件服务器的fd 222 | 223 | ```c++ 224 | thread send_file_thread(send_file_handler, val["length"].asInt(), port, &from_fd, &to_fd); 225 | ``` 226 | 227 | * 将文件服务器端口号返回给发送客户端,发送客户端根据端口号向文件服务器发起连接; 228 | 229 | * 判断发送客户端连接文件服务器是否成功,文件服务器中的`accept()`一旦被执行,对应的`fd`就会被修改(传入时为0),设置最长连接时间为`10s`,即等待10秒之后,`from_fd<=0`,说明仍未连接成功,判定为连接超时,取消文件服务器所在的线程,返回发送客户端连接文件服务器超时;相同方法连接接收客户端与文件服务器。 230 | 231 | * 文件服务器一边从`*f_fd`接收,一边向`*t_fd`发送,文件发送结束后,关闭发送与接收客户端与文件服务器之间的描述字,关闭该文件服务器,线程分离,释放资源 232 | 233 | ``` c++ 234 | send_file_thread.detach(); 235 | ``` 236 | 237 | ## 2. chat_client 238 | 未完待续...... 239 | 240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /chat_client_code/addfriend.cpp: -------------------------------------------------------------------------------- 1 | #include "addfriend.h" 2 | #include "ui_addfriend.h" 3 | 4 | Addfriend::Addfriend(QTcpSocket *s, QString u, QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::Addfriend) 7 | { 8 | ui->setupUi(this); 9 | this->socket = s; 10 | userName = u; 11 | } 12 | 13 | Addfriend::~Addfriend() 14 | { 15 | delete ui; 16 | } 17 | 18 | void Addfriend::on_cancelButton_clicked() 19 | { 20 | this->close(); 21 | } 22 | 23 | void Addfriend::on_addButton_clicked() 24 | { 25 | QString friendName = ui->lineEdit->text(); 26 | QJsonObject obj; 27 | obj.insert("cmd", "add"); 28 | obj.insert("user", userName); 29 | obj.insert("friend", friendName); 30 | 31 | QByteArray ba = QJsonDocument(obj).toJson(); 32 | socket->write(ba); 33 | 34 | this->close(); 35 | } 36 | -------------------------------------------------------------------------------- /chat_client_code/addfriend.h: -------------------------------------------------------------------------------- 1 | #ifndef ADDFRIEND_H 2 | #define ADDFRIEND_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Ui { 10 | class Addfriend; 11 | } 12 | 13 | class Addfriend : public QWidget 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit Addfriend(QTcpSocket *s, QString u, QWidget *parent = 0); 19 | ~Addfriend(); 20 | 21 | private slots: 22 | void on_cancelButton_clicked(); 23 | 24 | void on_addButton_clicked(); 25 | 26 | private: 27 | Ui::Addfriend *ui; 28 | QTcpSocket *socket; 29 | QString userName; 30 | }; 31 | 32 | #endif // ADDFRIEND_H 33 | -------------------------------------------------------------------------------- /chat_client_code/addfriend.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Addfriend 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 210 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 90 20 | 50 21 | 71 22 | 31 23 | 24 | 25 | 26 | <html><head/><body><p align="center"><span style=" font-size:12pt;">好友账号</span></p></body></html> 27 | 28 | 29 | 30 | 31 | 32 | 170 33 | 50 34 | 151 35 | 31 36 | 37 | 38 | 39 | 40 | 41 | 42 | 90 43 | 120 44 | 75 45 | 23 46 | 47 | 48 | 49 | 取消 50 | 51 | 52 | 53 | 54 | 55 | 220 56 | 120 57 | 75 58 | 23 59 | 60 | 61 | 62 | 添加 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /chat_client_code/addgroup.cpp: -------------------------------------------------------------------------------- 1 | #include "addgroup.h" 2 | #include "ui_addgroup.h" 3 | 4 | AddGroup::AddGroup(QTcpSocket *s, QString u, QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::AddGroup) 7 | { 8 | ui->setupUi(this); 9 | socket = s; 10 | userName = u; 11 | } 12 | 13 | AddGroup::~AddGroup() 14 | { 15 | delete ui; 16 | } 17 | 18 | void AddGroup::on_cancelButton_clicked() 19 | { 20 | this->close(); 21 | } 22 | 23 | void AddGroup::on_addButton_clicked() 24 | { 25 | QString groupName = ui->lineEdit->text(); 26 | QJsonObject obj; 27 | obj.insert("cmd", "add_group"); 28 | obj.insert("user", userName); 29 | obj.insert("group", groupName); 30 | QByteArray ba = QJsonDocument(obj).toJson(); 31 | socket->write(ba); 32 | this->close(); 33 | } 34 | -------------------------------------------------------------------------------- /chat_client_code/addgroup.h: -------------------------------------------------------------------------------- 1 | #ifndef ADDGROUP_H 2 | #define ADDGROUP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Ui { 10 | class AddGroup; 11 | } 12 | 13 | class AddGroup : public QWidget 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit AddGroup(QTcpSocket *s, QString u, QWidget *parent = 0); 19 | ~AddGroup(); 20 | 21 | private slots: 22 | void on_cancelButton_clicked(); 23 | 24 | void on_addButton_clicked(); 25 | 26 | private: 27 | Ui::AddGroup *ui; 28 | QTcpSocket *socket; 29 | QString userName; 30 | }; 31 | 32 | #endif // ADDGROUP_H 33 | -------------------------------------------------------------------------------- /chat_client_code/addgroup.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | AddGroup 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 212 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 90 20 | 70 21 | 81 22 | 31 23 | 24 | 25 | 26 | <html><head/><body><p align="center"><span style=" font-size:12pt;">群名</span></p></body></html> 27 | 28 | 29 | 30 | 31 | 32 | 160 33 | 70 34 | 161 35 | 31 36 | 37 | 38 | 39 | 40 | 41 | 42 | 120 43 | 140 44 | 75 45 | 23 46 | 47 | 48 | 49 | 取消 50 | 51 | 52 | 53 | 54 | 55 | 230 56 | 140 57 | 75 58 | 23 59 | 60 | 61 | 62 | 添加 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /chat_client_code/chat_server.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30907.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chat_server", "chat_server.vcxproj", "{376D12C2-B058-4FDA-93B9-2C2F81C440AB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM.Build.0 = Debug|ARM 22 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x64.ActiveCfg = Debug|x64 27 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x64.Build.0 = Debug|x64 28 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x64.Deploy.0 = Debug|x64 29 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x86.ActiveCfg = Debug|x86 30 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x86.Build.0 = Debug|x86 31 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x86.Deploy.0 = Debug|x86 32 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM.ActiveCfg = Release|ARM 33 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM.Build.0 = Release|ARM 34 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM.Deploy.0 = Release|ARM 35 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM64.Build.0 = Release|ARM64 37 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x64.ActiveCfg = Release|x64 39 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x64.Build.0 = Release|x64 40 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x64.Deploy.0 = Release|x64 41 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x86.ActiveCfg = Release|x86 42 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x86.Build.0 = Release|x86 43 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x86.Deploy.0 = Release|x86 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {EA6647A8-C091-45F0-8AFF-D0632C712335} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /chat_client_code/chat_soft.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2021-03-01T17:08:37 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui network 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = chat_soft 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which as been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | 26 | SOURCES += main.cpp\ 27 | widget.cpp \ 28 | chatlist.cpp \ 29 | addfriend.cpp \ 30 | creategroup.cpp \ 31 | addgroup.cpp \ 32 | privatechat.cpp \ 33 | groupchat.cpp \ 34 | sendthread.cpp \ 35 | recvthread.cpp 36 | 37 | HEADERS += widget.h \ 38 | chatlist.h \ 39 | addfriend.h \ 40 | creategroup.h \ 41 | addgroup.h \ 42 | privatechat.h \ 43 | groupchat.h \ 44 | sendthread.h \ 45 | recvthread.h 46 | 47 | FORMS += widget.ui \ 48 | chatlist.ui \ 49 | addfriend.ui \ 50 | creategroup.ui \ 51 | addgroup.ui \ 52 | privatechat.ui \ 53 | groupchat.ui 54 | -------------------------------------------------------------------------------- /chat_client_code/chatlist.cpp: -------------------------------------------------------------------------------- 1 | #include "chatlist.h" 2 | #include "ui_chatlist.h" 3 | 4 | Chatlist::Chatlist(QTcpSocket *s, QString fri, QString group, QString u, QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::Chatlist) 7 | { 8 | ui->setupUi(this); 9 | userName = u; 10 | socket = s; 11 | connect(socket, &QTcpSocket::readyRead, this, &Chatlist::server_reply); 12 | 13 | QStringList friList = fri.split('|'); 14 | for (int i = 0; i < friList.size(); i++) 15 | { 16 | if (friList.at(i) != "") 17 | { 18 | ui->friendList->addItem(friList.at(i)); 19 | } 20 | } 21 | 22 | QStringList groList = group.split('|'); 23 | for (int i = 0; i < groList.size(); i++) 24 | { 25 | if (groList.at(i) != "") 26 | { 27 | ui->groupList->addItem(groList.at(i)); 28 | } 29 | } 30 | 31 | connect(ui->friendList, &QListWidget::itemDoubleClicked, this, &Chatlist::on_friendList_double_clicked); 32 | connect(ui->groupList, &QListWidget::itemDoubleClicked, this, &Chatlist::on_groupList_double_clicked); 33 | } 34 | 35 | Chatlist::~Chatlist() 36 | { 37 | delete ui; 38 | } 39 | 40 | void Chatlist::server_reply() 41 | { 42 | QByteArray ba = socket->readAll(); 43 | QJsonObject obj = QJsonDocument::fromJson(ba).object(); 44 | QString cmd = obj.value("cmd").toString(); 45 | if (cmd == "friend_login") 46 | { 47 | client_login_reply(obj.value("friend").toString()); 48 | } 49 | else if (cmd == "add_reply") 50 | { 51 | client_add_friend_reply(obj); 52 | } 53 | else if (cmd == "add_friend_reply") 54 | { 55 | QString str = QString("%1把你添加为好友").arg(obj.value("result").toString()); 56 | QMessageBox::information(this, "添加好友提醒", str); 57 | ui->friendList->addItem(obj.value("result").toString()); 58 | } 59 | else if (cmd == "create_group_reply") 60 | { 61 | client_create_group_reply(obj); 62 | } 63 | else if (cmd == "add_group_reply") 64 | { 65 | client_add_group_reply(obj); 66 | } 67 | else if (cmd == "private_chat_reply") 68 | { 69 | client_private_chat_reply(obj.value("result").toString()); 70 | } 71 | else if (cmd == "private_chat") 72 | { 73 | client_chat_reply(obj); 74 | } 75 | else if (cmd == "get_group_member_reply") 76 | { 77 | client_get_group_member_reply(obj); 78 | } 79 | else if (cmd == "group_chat") 80 | { 81 | client_group_chat_reply(obj); 82 | } 83 | else if (cmd == "send_file_reply") 84 | { 85 | client_send_file_reply(obj.value("result").toString()); 86 | } 87 | else if (cmd == "send_file_port_reply") 88 | { 89 | client_send_file_port_reply(obj); 90 | } 91 | else if (cmd == "recv_file_port_reply") 92 | { 93 | client_recv_file_port_reply(obj); 94 | } 95 | else if (cmd == "friend_offline") 96 | { 97 | client_friend_offline(obj.value("friend").toString()); 98 | } 99 | } 100 | 101 | void Chatlist::client_login_reply(QString fri) 102 | { 103 | QString str = QString("%1好友上线").arg(fri); 104 | QMessageBox::information(this, "好友上线提醒", str); 105 | } 106 | 107 | void Chatlist::client_add_friend_reply(QJsonObject &obj) 108 | { 109 | if (obj.value("result").toString() == "user_not_exist") 110 | { 111 | QMessageBox::warning(this, "添加好友提醒", "好友不存在"); 112 | } 113 | else if (obj.value("result").toString() == "already_friend") 114 | { 115 | QMessageBox::warning(this, "添加好友提醒", "已经是好友关系"); 116 | } 117 | else if (obj.value("result").toString() == "success") 118 | { 119 | QMessageBox::information(this, "添加好友提醒", "好友添加成功"); 120 | ui->friendList->addItem(obj.value("friend").toString()); 121 | } 122 | } 123 | 124 | void Chatlist::client_create_group_reply(QJsonObject &obj) 125 | { 126 | if (obj.value("result").toString() == "group_exist") 127 | { 128 | QMessageBox::warning(this, "创建群提示", "群已经存在"); 129 | } 130 | else if (obj.value("result").toString() == "success") 131 | { 132 | ui->groupList->addItem(obj.value("group").toString()); 133 | } 134 | } 135 | 136 | void Chatlist::client_add_group_reply(QJsonObject &obj) 137 | { 138 | if (obj.value("result").toString() == "group_not_exist") 139 | { 140 | QMessageBox::warning(this, "添加群提示", "群不存在"); 141 | } 142 | else if (obj.value("result").toString() == "user_in_group") 143 | { 144 | QMessageBox::warning(this, "添加群提示", "已经在群里面"); 145 | } 146 | else if (obj.value("result").toString() == "success") 147 | { 148 | ui->groupList->addItem(obj.value("group").toString()); 149 | } 150 | } 151 | 152 | void Chatlist::client_private_chat_reply(QString res) 153 | { 154 | if (res == "offline") 155 | { 156 | QMessageBox::warning(this, "发送提醒", "对方不在线"); 157 | } 158 | } 159 | 160 | void Chatlist::client_chat_reply(QJsonObject &obj) 161 | { 162 | int flag = 0; 163 | for (int i = 0; i < chatWidgetList.size(); i++) 164 | { 165 | if (chatWidgetList.at(i).name == obj.value("user_from").toString()) 166 | { 167 | flag = 1; 168 | break; 169 | } 170 | } 171 | 172 | if (flag == 0) //聊天窗口没有打开过 173 | { 174 | QString friendName = obj.value("user_from").toString(); 175 | PrivateChat *privateChatWidget = new PrivateChat(socket, userName, friendName, this, &chatWidgetList); 176 | privateChatWidget->setWindowTitle(friendName); 177 | privateChatWidget->show(); 178 | 179 | ChatWidgetInfo c = {privateChatWidget, friendName}; 180 | chatWidgetList.push_back(c); 181 | } 182 | 183 | emit signal_to_sub_widget(obj); 184 | } 185 | 186 | void Chatlist::client_get_group_member_reply(QJsonObject obj) 187 | { 188 | emit signal_to_sub_widget_member(obj); 189 | } 190 | 191 | void Chatlist::client_group_chat_reply(QJsonObject obj) 192 | { 193 | int flag = 0; 194 | for (int i = 0; i < groupWidgetList.size(); i++) 195 | { 196 | if (groupWidgetList.at(i).name == obj.value("group").toString()) 197 | { 198 | flag = 1; 199 | break; 200 | } 201 | } 202 | 203 | if(flag == 0) 204 | { 205 | QString groupName = obj.value("group").toString(); 206 | GroupChat *groupChatWidget = new GroupChat(socket, groupName, userName, this, &groupWidgetList); 207 | groupChatWidget->setWindowTitle(groupName); 208 | groupChatWidget->show(); 209 | 210 | groupWidgetInfo g = {groupChatWidget, groupName}; 211 | groupWidgetList.push_back(g); 212 | } 213 | 214 | emit signal_to_sub_widget_group(obj); 215 | } 216 | 217 | void Chatlist::client_send_file_reply(QString res) 218 | { 219 | if (res == "offline") 220 | { 221 | QMessageBox::warning(this, "发送文件提醒", "对方不在线"); 222 | } 223 | else if (res == "timeout") 224 | { 225 | QMessageBox::warning(this, "发送文件提醒", "连接超时"); 226 | } 227 | } 228 | 229 | void Chatlist::client_send_file_port_reply(QJsonObject obj) 230 | { 231 | SendThread *mySendthread = new SendThread(obj); 232 | mySendthread->start(); 233 | } 234 | 235 | void Chatlist::client_recv_file_port_reply(QJsonObject obj) 236 | { 237 | RecvThread *myRecvThread = new RecvThread(obj); 238 | myRecvThread->start(); 239 | } 240 | 241 | void Chatlist::client_friend_offline(QString fri) 242 | { 243 | QString str = QString("%1下线").arg(fri); 244 | QMessageBox::information(this, "下线提醒", str); 245 | } 246 | 247 | void Chatlist::on_addButton_clicked() 248 | { 249 | Addfriend *addFriendWidget = new Addfriend(socket, userName); 250 | addFriendWidget->show(); 251 | } 252 | 253 | void Chatlist::on_createGroupButton_clicked() 254 | { 255 | CreateGroup *createGroupWidget = new CreateGroup(socket, userName); 256 | createGroupWidget->show(); 257 | } 258 | 259 | void Chatlist::on_addGroupButton_clicked() 260 | { 261 | AddGroup *addGroupWidget = new AddGroup(socket, userName); 262 | addGroupWidget->show(); 263 | } 264 | 265 | void Chatlist::on_friendList_double_clicked() 266 | { 267 | QString friendName = ui->friendList->currentItem()->text(); 268 | PrivateChat *privateChatWidget = new PrivateChat(socket, userName, friendName, this, &chatWidgetList); 269 | privateChatWidget->setWindowTitle(friendName); 270 | privateChatWidget->show(); 271 | 272 | ChatWidgetInfo c = {privateChatWidget, friendName}; 273 | chatWidgetList.push_back(c); 274 | } 275 | 276 | void Chatlist::on_groupList_double_clicked() 277 | { 278 | QString groupName = ui->groupList->currentItem()->text(); 279 | GroupChat *groupChatWidget = new GroupChat(socket, groupName, userName, this, &groupWidgetList); 280 | groupChatWidget->setWindowTitle(groupName); 281 | groupChatWidget->show(); 282 | 283 | groupWidgetInfo g = {groupChatWidget, groupName}; 284 | groupWidgetList.push_back(g); 285 | } 286 | 287 | void Chatlist::closeEvent(QCloseEvent *event) 288 | { 289 | QJsonObject obj; 290 | obj.insert("cmd", "offline"); 291 | obj.insert("user", userName); 292 | QByteArray ba = QJsonDocument(obj).toJson(); 293 | socket->write(ba); 294 | socket->flush(); 295 | 296 | event->accept(); 297 | } 298 | -------------------------------------------------------------------------------- /chat_client_code/chatlist.h: -------------------------------------------------------------------------------- 1 | #ifndef CHATLIST_H 2 | #define CHATLIST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "addfriend.h" 11 | #include "creategroup.h" 12 | #include "addgroup.h" 13 | #include "sendthread.h" 14 | #include "recvthread.h" 15 | 16 | class GroupChat; 17 | #include "groupchat.h" 18 | 19 | class PrivateChat; 20 | #include "privatechat.h" 21 | 22 | namespace Ui { 23 | class Chatlist; 24 | } 25 | 26 | struct ChatWidgetInfo 27 | { 28 | PrivateChat *w; 29 | QString name; 30 | }; 31 | 32 | struct groupWidgetInfo 33 | { 34 | GroupChat *w; 35 | QString name; 36 | }; 37 | 38 | class Chatlist : public QWidget 39 | { 40 | Q_OBJECT 41 | 42 | public: 43 | explicit Chatlist(QTcpSocket *, QString, QString, QString, QWidget *parent = 0); 44 | void closeEvent(QCloseEvent *event); 45 | ~Chatlist(); 46 | 47 | private slots: 48 | void server_reply(); 49 | 50 | void on_addButton_clicked(); 51 | 52 | void on_createGroupButton_clicked(); 53 | 54 | void on_addGroupButton_clicked(); 55 | 56 | void on_friendList_double_clicked(); 57 | 58 | void on_groupList_double_clicked(); 59 | 60 | signals: 61 | void signal_to_sub_widget(QJsonObject); 62 | void signal_to_sub_widget_member(QJsonObject); 63 | void signal_to_sub_widget_group(QJsonObject); 64 | 65 | private: 66 | void client_login_reply(QString); 67 | void client_add_friend_reply(QJsonObject &obj); 68 | void client_create_group_reply(QJsonObject &obj); 69 | void client_add_group_reply(QJsonObject &obj); 70 | void client_private_chat_reply(QString); 71 | void client_chat_reply(QJsonObject &); 72 | void client_get_group_member_reply(QJsonObject); 73 | void client_group_chat_reply(QJsonObject); 74 | void client_send_file_reply(QString); 75 | void client_send_file_port_reply(QJsonObject); 76 | void client_recv_file_port_reply(QJsonObject); 77 | void client_friend_offline(QString fri); 78 | 79 | Ui::Chatlist *ui; 80 | QTcpSocket *socket; 81 | QString userName; 82 | QList chatWidgetList; 83 | QList groupWidgetList; 84 | }; 85 | 86 | #endif // CHATLIST_H 87 | -------------------------------------------------------------------------------- /chat_client_code/chatlist.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Chatlist 4 | 5 | 6 | 7 | 0 8 | 0 9 | 277 10 | 476 11 | 12 | 13 | 14 | 15 | 277 16 | 476 17 | 18 | 19 | 20 | 21 | 277 22 | 476 23 | 24 | 25 | 26 | Form 27 | 28 | 29 | 30 | 31 | 32 | QTabBar::tab{width:120} 33 | 34 | 35 | 0 36 | 37 | 38 | 39 | 好友 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 添加好友 63 | 64 | 65 | 66 | 67 | 68 | 69 | 创建群聊 70 | 71 | 72 | 73 | 74 | 75 | 76 | 添加群 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /chat_client_code/creategroup.cpp: -------------------------------------------------------------------------------- 1 | #include "creategroup.h" 2 | #include "ui_creategroup.h" 3 | 4 | CreateGroup::CreateGroup(QTcpSocket *s, QString u, QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::CreateGroup) 7 | { 8 | ui->setupUi(this); 9 | socket = s; 10 | userName = u; 11 | } 12 | 13 | CreateGroup::~CreateGroup() 14 | { 15 | delete ui; 16 | } 17 | 18 | void CreateGroup::on_cancelButton_clicked() 19 | { 20 | this->close(); 21 | } 22 | 23 | void CreateGroup::on_pushButton_2_clicked() 24 | { 25 | QString groupName = ui->lineEdit->text(); 26 | QJsonObject obj; 27 | obj.insert("cmd", "create_group"); 28 | obj.insert("user", userName); 29 | obj.insert("group", groupName); 30 | QByteArray ba = QJsonDocument(obj).toJson(); 31 | socket->write(ba); 32 | this->close(); 33 | } 34 | -------------------------------------------------------------------------------- /chat_client_code/creategroup.h: -------------------------------------------------------------------------------- 1 | #ifndef CREATEGROUP_H 2 | #define CREATEGROUP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Ui { 10 | class CreateGroup; 11 | } 12 | 13 | class CreateGroup : public QWidget 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit CreateGroup(QTcpSocket *s, QString u, QWidget *parent = 0); 19 | ~CreateGroup(); 20 | 21 | private slots: 22 | void on_cancelButton_clicked(); 23 | 24 | void on_pushButton_2_clicked(); 25 | 26 | private: 27 | Ui::CreateGroup *ui; 28 | QString userName; 29 | QTcpSocket *socket; 30 | }; 31 | 32 | #endif // CREATEGROUP_H 33 | -------------------------------------------------------------------------------- /chat_client_code/creategroup.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CreateGroup 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 217 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 70 20 | 70 21 | 81 22 | 31 23 | 24 | 25 | 26 | <html><head/><body><p align="center"><span style=" font-size:12pt;">创建群名称</span></p></body></html> 27 | 28 | 29 | 30 | 31 | 32 | 160 33 | 70 34 | 161 35 | 31 36 | 37 | 38 | 39 | 40 | 41 | 42 | 80 43 | 140 44 | 75 45 | 23 46 | 47 | 48 | 49 | 取消 50 | 51 | 52 | 53 | 54 | 55 | 220 56 | 140 57 | 75 58 | 23 59 | 60 | 61 | 62 | 创建 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /chat_client_code/groupchat.cpp: -------------------------------------------------------------------------------- 1 | #include "groupchat.h" 2 | #include "ui_groupchat.h" 3 | 4 | GroupChat::GroupChat(QTcpSocket *s, QString g, QString u, Chatlist *c, QList *l, QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::GroupChat) 7 | { 8 | ui->setupUi(this); 9 | socket = s; 10 | groupName = g; 11 | userName = u; 12 | mainWidget = c; 13 | groupWidgetList = l; 14 | 15 | QJsonObject obj; 16 | obj.insert("cmd", "get_group_member"); 17 | obj.insert("group", groupName); 18 | QByteArray ba = QJsonDocument(obj).toJson(); 19 | socket->write(ba); 20 | 21 | connect(mainWidget, &Chatlist::signal_to_sub_widget_member, this, &GroupChat::show_group_member); 22 | connect(mainWidget, &Chatlist::signal_to_sub_widget_group, this, &GroupChat::show_group_text); 23 | } 24 | 25 | GroupChat::~GroupChat() 26 | { 27 | delete ui; 28 | } 29 | 30 | void GroupChat::show_group_member(QJsonObject obj) 31 | { 32 | if (obj.value("cmd").toString() == "get_group_member_reply") 33 | { 34 | if (obj.value("group").toString() == groupName) 35 | { 36 | QStringList strList = obj.value("member").toString().split("|"); 37 | for (int i = 0; i < strList.size(); i++) 38 | { 39 | ui->listWidget->addItem(strList.at(i)); 40 | } 41 | } 42 | } 43 | } 44 | 45 | void GroupChat::on_sendButton_clicked() 46 | { 47 | QString text = ui->lineEdit->text(); 48 | QJsonObject obj; 49 | obj.insert("cmd", "group_chat"); 50 | obj.insert("user", userName); 51 | obj.insert("group", groupName); 52 | obj.insert("text", text); 53 | QByteArray ba = QJsonDocument(obj).toJson(); 54 | socket->write(ba); 55 | 56 | ui->lineEdit->clear(); 57 | ui->textEdit->append(text); 58 | ui->textEdit->append("\n"); 59 | } 60 | 61 | void GroupChat::show_group_text(QJsonObject obj) 62 | { 63 | if (obj.value("cmd").toString() == "group_chat") 64 | { 65 | if (obj.value("group").toString() == groupName) 66 | { 67 | if (this->isMinimized()) 68 | { 69 | this->showNormal(); 70 | } 71 | this->activateWindow(); 72 | ui->textEdit->append(obj.value("text").toString()); 73 | ui->textEdit->append("\n"); 74 | } 75 | } 76 | } 77 | 78 | void GroupChat::closeEvent(QCloseEvent * event) 79 | { 80 | for (int i = 0; i < groupWidgetList->size(); i++) 81 | { 82 | if (groupWidgetList->at(i).name == groupName) 83 | { 84 | groupWidgetList->removeAt(i); 85 | } 86 | } 87 | event->accept(); 88 | } 89 | -------------------------------------------------------------------------------- /chat_client_code/groupchat.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUPCHAT_H 2 | #define GROUPCHAT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Chatlist; 11 | struct groupWidgetInfo; 12 | #include "chatlist.h" 13 | 14 | namespace Ui { 15 | class GroupChat; 16 | } 17 | 18 | class GroupChat : public QWidget 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit GroupChat(QTcpSocket *s, QString g, QString u, Chatlist *c, QList *l, QWidget *parent = 0); 24 | ~GroupChat(); 25 | void closeEvent(QCloseEvent *); 26 | 27 | private slots: 28 | void show_group_member(QJsonObject); 29 | 30 | void on_sendButton_clicked(); 31 | 32 | void show_group_text(QJsonObject); 33 | 34 | private: 35 | Ui::GroupChat *ui; 36 | QTcpSocket *socket; 37 | QString userName; 38 | QString groupName; 39 | Chatlist *mainWidget; 40 | QList *groupWidgetList; 41 | }; 42 | 43 | #endif // GROUPCHAT_H 44 | -------------------------------------------------------------------------------- /chat_client_code/groupchat.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | GroupChat 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 0 20 | 0 21 | 291 22 | 181 23 | 24 | 25 | 26 | 27 | 28 | 29 | 0 30 | 200 31 | 291 32 | 41 33 | 34 | 35 | 36 | 37 | 38 | 39 | 220 40 | 270 41 | 75 42 | 23 43 | 44 | 45 | 46 | 发送 47 | 48 | 49 | 50 | 51 | 52 | 310 53 | 0 54 | 81 55 | 241 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /chat_client_code/main.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | Widget w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /chat_client_code/privatechat.cpp: -------------------------------------------------------------------------------- 1 | #include "privatechat.h" 2 | #include "ui_privatechat.h" 3 | 4 | PrivateChat::PrivateChat(QTcpSocket *s, QString u, QString f, Chatlist *c, QList *l, QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::PrivateChat) 7 | { 8 | ui->setupUi(this); 9 | socket = s; 10 | userName = u; 11 | friendName = f; 12 | mainWidget = c; 13 | chatWidgetList = l; 14 | 15 | connect(mainWidget, &Chatlist::signal_to_sub_widget, this, &PrivateChat::show_text_slot); 16 | } 17 | 18 | PrivateChat::~PrivateChat() 19 | { 20 | delete ui; 21 | } 22 | 23 | void PrivateChat::on_sendButton_clicked() 24 | { 25 | QString text = ui->lineEdit->text(); 26 | QJsonObject obj; 27 | obj.insert("cmd", "private_chat"); 28 | obj.insert("user_from", userName); 29 | obj.insert("user_to", friendName); 30 | obj.insert("text", text); 31 | QByteArray ba = QJsonDocument(obj).toJson(); 32 | socket->write(ba); 33 | 34 | ui->lineEdit->clear(); 35 | ui->textEdit->append(text); 36 | ui->textEdit->append("\n"); 37 | } 38 | 39 | void PrivateChat::show_text_slot(QJsonObject obj) 40 | { 41 | if (obj.value("cmd").toString() == "private_chat") 42 | { 43 | if (obj.value("user_from").toString() == friendName) 44 | { 45 | if (this->isMinimized()) 46 | { 47 | this->showNormal(); 48 | } 49 | this->activateWindow(); 50 | ui->textEdit->append(obj.value("text").toString()); 51 | ui->textEdit->append("\n"); 52 | } 53 | } 54 | } 55 | 56 | void PrivateChat::closeEvent(QCloseEvent *event) 57 | { 58 | for (int i = 0; i < chatWidgetList->size(); i++) 59 | { 60 | if (chatWidgetList->at(i).name == friendName) 61 | { 62 | chatWidgetList->removeAt(i); 63 | break; 64 | } 65 | } 66 | event->accept(); 67 | } 68 | 69 | void PrivateChat::on_fileButton_clicked() 70 | { 71 | QString fileName = QFileDialog::getOpenFileName(this, "发送文件", QCoreApplication::applicationFilePath()); 72 | if (fileName.isEmpty()) 73 | { 74 | QMessageBox::warning(this, "发送文件提示", "请选择一个文件"); 75 | } 76 | else 77 | { 78 | QFile file(fileName); 79 | file.open(QIODevice::ReadOnly); 80 | QJsonObject obj; 81 | obj.insert("cmd", "send_file"); 82 | obj.insert("from_user", userName); 83 | obj.insert("to_user", friendName); 84 | obj.insert("length", file.size()); 85 | obj.insert("filename", fileName); 86 | QByteArray ba = QJsonDocument(obj).toJson(); 87 | socket->write(ba); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /chat_client_code/privatechat.h: -------------------------------------------------------------------------------- 1 | #ifndef PRIVATECHAT_H 2 | #define PRIVATECHAT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class Chatlist; 14 | struct ChatWidgetInfo; 15 | #include "chatlist.h" 16 | 17 | namespace Ui { 18 | class PrivateChat; 19 | } 20 | 21 | class PrivateChat : public QWidget 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | explicit PrivateChat(QTcpSocket *s, QString u, QString f, Chatlist *c, QList *l, QWidget *parent = 0); 27 | ~PrivateChat(); 28 | void closeEvent(QCloseEvent *event); 29 | 30 | private slots: 31 | void on_sendButton_clicked(); 32 | void show_text_slot(QJsonObject); 33 | 34 | void on_fileButton_clicked(); 35 | 36 | private: 37 | Ui::PrivateChat *ui; 38 | QTcpSocket *socket; 39 | QString userName; 40 | QString friendName; 41 | Chatlist *mainWidget; 42 | QList *chatWidgetList; 43 | }; 44 | 45 | #endif // PRIVATECHAT_H 46 | -------------------------------------------------------------------------------- /chat_client_code/privatechat.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PrivateChat 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 0 20 | 0 21 | 401 22 | 171 23 | 24 | 25 | 26 | 27 | 28 | 29 | 0 30 | 200 31 | 401 32 | 41 33 | 34 | 35 | 36 | 37 | 38 | 39 | 290 40 | 260 41 | 75 42 | 23 43 | 44 | 45 | 46 | 发送 47 | 48 | 49 | 50 | 51 | 52 | 30 53 | 260 54 | 75 55 | 23 56 | 57 | 58 | 59 | 传输文件 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /chat_client_code/recvthread.cpp: -------------------------------------------------------------------------------- 1 | #include "recvthread.h" 2 | 3 | RecvThread::RecvThread(QJsonObject obj) 4 | { 5 | total = 0; 6 | port = obj.value("port").toInt(); 7 | fileLength = obj.value("length").toInt(); 8 | 9 | QString pathName = obj.value("filename").toString(); 10 | QStringList strList = pathName.split('/'); 11 | fileName = strList.at(strList.size() - 1); 12 | } 13 | 14 | void RecvThread::run() 15 | { 16 | file = new QFile(fileName); 17 | file->open(QIODevice::WriteOnly); 18 | 19 | recvSocket = new QTcpSocket; 20 | 21 | connect(recvSocket, &QTcpSocket::readyRead, this, &RecvThread::recv_file, Qt::DirectConnection); 22 | 23 | recvSocket->connectToHost(QHostAddress("47.101.128.140"), port); 24 | if (!recvSocket->waitForConnected(10000)) 25 | { 26 | this->quit(); 27 | } 28 | else 29 | { 30 | 31 | } 32 | 33 | exec(); 34 | } 35 | 36 | void RecvThread::recv_file() 37 | { 38 | QByteArray ba = recvSocket->readAll(); 39 | total += ba.size(); 40 | file->write(ba); 41 | 42 | if (total >= fileLength) 43 | { 44 | file->close(); 45 | recvSocket->close(); 46 | delete file; 47 | delete recvSocket; 48 | this->quit(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /chat_client_code/recvthread.h: -------------------------------------------------------------------------------- 1 | #ifndef RECVTHREAD_H 2 | #define RECVTHREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class RecvThread : public QThread 12 | { 13 | Q_OBJECT 14 | public: 15 | RecvThread(QJsonObject obj); 16 | void run(); 17 | 18 | private slots: 19 | void recv_file(); 20 | 21 | private: 22 | QString fileName; 23 | int fileLength; 24 | int total; 25 | int port; 26 | QTcpSocket *recvSocket; 27 | QFile *file; 28 | }; 29 | 30 | #endif // RECVTHREAD_H 31 | -------------------------------------------------------------------------------- /chat_client_code/sendthread.cpp: -------------------------------------------------------------------------------- 1 | #include "sendthread.h" 2 | 3 | SendThread::SendThread(QJsonObject obj) 4 | { 5 | port = obj.value("port").toInt(); 6 | fileName = obj.value("filename").toString(); 7 | fileLength = obj.value("length").toInt(); 8 | } 9 | 10 | void SendThread::run() 11 | { 12 | QTcpSocket sendSocket; 13 | sendSocket.connectToHost(QHostAddress("47.101.128.140"), port); 14 | if (!sendSocket.waitForConnected(10000)) 15 | { 16 | this->quit(); 17 | } 18 | else 19 | { 20 | QFile file(fileName); 21 | file.open(QIODevice::ReadOnly); 22 | 23 | while (1) 24 | { 25 | QByteArray ba = file.read(1024 * 1024); 26 | if (ba.size() == 0) 27 | { 28 | break; 29 | } 30 | sendSocket.write(ba); 31 | sendSocket.flush(); 32 | usleep(2000000); 33 | } 34 | file.close(); 35 | sendSocket.close(); 36 | this->quit(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chat_client_code/sendthread.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDTHREAD_H 2 | #define SENDTHREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class SendThread : public QThread 12 | { 13 | public: 14 | SendThread(QJsonObject obj); 15 | void run(); 16 | 17 | private: 18 | QString fileName; 19 | int fileLength; 20 | int port; 21 | }; 22 | 23 | #endif // SENDTHREAD_H 24 | -------------------------------------------------------------------------------- /chat_client_code/widget.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.h" 2 | #include "ui_widget.h" 3 | 4 | Widget::Widget(QWidget *parent) : 5 | QWidget(parent), 6 | ui(new Ui::Widget) 7 | { 8 | ui->setupUi(this); 9 | socket = new QTcpSocket; 10 | socket->connectToHost(QHostAddress("47.101.128.140"), 8000); 11 | 12 | connect(socket, &QTcpSocket::connected, this, &Widget::connect_success); 13 | connect(socket, &QTcpSocket::readyRead, this, &Widget::server_reply); 14 | } 15 | 16 | Widget::~Widget() 17 | { 18 | delete ui; 19 | } 20 | 21 | void Widget::connect_success() 22 | { 23 | QMessageBox::information(this, "连接提示", "连接服务器成功"); 24 | } 25 | 26 | void Widget::on_registerButton_clicked() 27 | { 28 | QString username = ui->userLineEdit->text(); 29 | QString password = ui->passwdLineEdit->text(); 30 | 31 | QJsonObject obj; 32 | obj.insert("cmd", "register"); 33 | obj.insert("user", username); 34 | obj.insert("password", password); 35 | 36 | QByteArray ba = QJsonDocument(obj).toJson(); 37 | socket->write(ba); 38 | } 39 | 40 | void Widget::server_reply() 41 | { 42 | QByteArray ba = socket->readAll(); 43 | QJsonObject obj = QJsonDocument::fromJson(ba).object(); 44 | QString cmd = obj.value("cmd").toString(); 45 | if (cmd == "register_reply") 46 | { 47 | client_register_handler(obj.value("result").toString()); 48 | } 49 | else if (cmd == "login_reply") 50 | { 51 | client_login_handler(obj.value("result").toString(), 52 | obj.value("friend").toString(), obj.value("group").toString()); 53 | } 54 | } 55 | 56 | void Widget::client_register_handler(QString res) 57 | { 58 | if (res == "success") 59 | { 60 | QMessageBox::information(this, "注册提示", "注册成功"); 61 | } 62 | else if (res == "failure") 63 | { 64 | QMessageBox::warning(this, "注册提示", "注册失败"); 65 | } 66 | } 67 | 68 | void Widget::on_loginButton_clicked() 69 | { 70 | QString username = ui->userLineEdit->text(); 71 | QString password = ui->passwdLineEdit->text(); 72 | 73 | QJsonObject obj; 74 | obj.insert("cmd", "login"); 75 | obj.insert("user", username); 76 | obj.insert("password", password); 77 | 78 | userName = username; 79 | 80 | QByteArray ba = QJsonDocument(obj).toJson(); 81 | socket->write(ba); 82 | } 83 | 84 | void Widget::client_login_handler(QString res, QString fri, QString group) 85 | { 86 | if (res == "user_not_exist") 87 | { 88 | QMessageBox::warning(this, "登录提示", "用户不存在"); 89 | } 90 | else if (res == "password_error") 91 | { 92 | QMessageBox::warning(this, "登录提示", "密码错误"); 93 | } 94 | else if (res == "success") 95 | { 96 | this->hide(); 97 | socket->disconnect(SIGNAL(readyRead())); 98 | Chatlist *c = new Chatlist(socket, fri, group, userName); 99 | c->setWindowTitle(userName); 100 | c->show(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /chat_client_code/widget.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGET_H 2 | #define WIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "chatlist.h" 11 | 12 | namespace Ui { 13 | class Widget; 14 | } 15 | 16 | class Widget : public QWidget 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit Widget(QWidget *parent = 0); 22 | ~Widget(); 23 | 24 | private slots: 25 | void connect_success(); 26 | void server_reply(); 27 | 28 | void on_registerButton_clicked(); 29 | 30 | void on_loginButton_clicked(); 31 | 32 | private: 33 | void client_register_handler(QString); 34 | void client_login_handler(QString, QString, QString); 35 | 36 | Ui::Widget *ui; 37 | QTcpSocket *socket; 38 | QString userName; 39 | }; 40 | 41 | #endif // WIDGET_H 42 | -------------------------------------------------------------------------------- /chat_client_code/widget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Widget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Widget 15 | 16 | 17 | 18 | 19 | 80 20 | 100 21 | 51 22 | 21 23 | 24 | 25 | 26 | <html><head/><body><p align="center"><span style=" font-size:12pt;">用户</span></p></body></html> 27 | 28 | 29 | 30 | 31 | 32 | 80 33 | 140 34 | 51 35 | 21 36 | 37 | 38 | 39 | <html><head/><body><p align="center"><span style=" font-size:12pt;">密码</span></p></body></html> 40 | 41 | 42 | 43 | 44 | 45 | 140 46 | 90 47 | 141 48 | 31 49 | 50 | 51 | 52 | 53 | 54 | 55 | 140 56 | 140 57 | 141 58 | 31 59 | 60 | 61 | 62 | 63 | 64 | 65 | 90 66 | 200 67 | 75 68 | 23 69 | 70 | 71 | 72 | 注册 73 | 74 | 75 | 76 | 77 | 78 | 230 79 | 200 80 | 75 81 | 23 82 | 83 | 84 | 85 | 登录 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /chat_database.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/chat_database.cpp -------------------------------------------------------------------------------- /chat_database.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/chat_database.h -------------------------------------------------------------------------------- /chat_server_project.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30907.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chat_server", "chat_server.vcxproj", "{376D12C2-B058-4FDA-93B9-2C2F81C440AB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM.Build.0 = Debug|ARM 22 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x64.ActiveCfg = Debug|x64 27 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x64.Build.0 = Debug|x64 28 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x64.Deploy.0 = Debug|x64 29 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x86.ActiveCfg = Debug|x86 30 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x86.Build.0 = Debug|x86 31 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Debug|x86.Deploy.0 = Debug|x86 32 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM.ActiveCfg = Release|ARM 33 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM.Build.0 = Release|ARM 34 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM.Deploy.0 = Release|ARM 35 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM64.Build.0 = Release|ARM64 37 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x64.ActiveCfg = Release|x64 39 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x64.Build.0 = Release|x64 40 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x64.Deploy.0 = Release|x64 41 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x86.ActiveCfg = Release|x86 42 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x86.Build.0 = Release|x86 43 | {376D12C2-B058-4FDA-93B9-2C2F81C440AB}.Release|x86.Deploy.0 = Release|x86 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {EA6647A8-C091-45F0-8AFF-D0632C712335} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /chat_server_project.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Release 10 | ARM 11 | 12 | 13 | Debug 14 | ARM64 15 | 16 | 17 | Release 18 | ARM64 19 | 20 | 21 | Debug 22 | x86 23 | 24 | 25 | Release 26 | x86 27 | 28 | 29 | Debug 30 | x64 31 | 32 | 33 | Release 34 | x64 35 | 36 | 37 | 38 | {376d12c2-b058-4fda-93b9-2c2f81c440ab} 39 | Linux 40 | chat_server 41 | 15.0 42 | Linux 43 | 1.0 44 | Generic 45 | {2238F9CD-F817-4ECC-BD14-2524D2669B35} 46 | 47 | 48 | 49 | true 50 | 51 | 52 | false 53 | 54 | 55 | true 56 | 57 | 58 | false 59 | 60 | 61 | true 62 | 63 | 64 | false 65 | 66 | 67 | false 68 | 69 | 70 | true 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /chatlist.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/chatlist.cpp -------------------------------------------------------------------------------- /chatlist.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/chatlist.h -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/main.cpp -------------------------------------------------------------------------------- /myassets/image-20220912180434913.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/myassets/image-20220912180434913.png -------------------------------------------------------------------------------- /myassets/image-20220912180641606.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/myassets/image-20220912180641606.png -------------------------------------------------------------------------------- /server.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/server.cpp -------------------------------------------------------------------------------- /server.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/server.h -------------------------------------------------------------------------------- /在阿里云服务器上搭建开发环境.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/在阿里云服务器上搭建开发环境.txt -------------------------------------------------------------------------------- /服务器与客户端接口文档.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/服务器与客户端接口文档.txt -------------------------------------------------------------------------------- /测试客户端.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuzsen/chat-software/fe0194dc3523f7914d36fe4f7d4b022ac5c774c6/测试客户端.c --------------------------------------------------------------------------------