当前位置:   article > 正文

c++编写聊天室(客户端)Windows_聊天软件 c++ windows

聊天软件 c++ windows

 项目涉及Windows网络编程开发

 服务器使用Linux系统

 互斥锁的使用

 多线程开发

 windows 控制台的设置开发

        

项目效果预览

c++聊天室


编写思路

  •  导入所需的头文件:
    1. #include <iostream>
    2. #include <sys/socket.h>
    3. #include <arpa/inet.h>
  • 创建套接字:

    1. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    2. if (sockfd == -1) {
    3. std::cout << "Failed to create socket" << std::endl;
    4. return -1;
    5. }
  • 设置服务器地址和端口

    1. sockaddr_in serverAddress{};
    2. serverAddress.sin_family = AF_INET;
    3. serverAddress.sin_port = htons(port); // 设置端口号 用htons转为网络字节序
    4. if (inet_pton(AF_INET, ipAddress, &serverAddress.sin_addr) <= 0) {
    5. std::cout << "Invalid address/ Address not supported" << std::endl;
    6. return -1;
    7. }
  • 连接到服务器:

    1. if (connect(sockfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {
    2. std::cout << "Connection Failed" << std::endl;
    3. return -1;
    4. }
  • 发送数据到服务器:

     

    1. char message[] = "Hello Server!";
    2. if (send(sockfd, message, strlen(message), 0) < 0) {
    3. std::cout << "Send failed" << std::endl;
    4. return -1;
    5. }
  • 接收服务器返回的数据:

    1. char buffer[1024] {0};
    2. if (recv(sockfd, buffer, sizeof(buffer), 0) < 0) {
    3. std::cout << "Receive failed" << std::endl;
    4. return -1;
    5. }
    6. std::cout << "Server response: " << buffer << std::endl;
  • 关闭套接字:

    close(sockfd);
    
     接下来就是再此基础上优化(由于客户端和服务端的编码格式不同,所以相互之间要进行转码)

    头文件

  1. #include <stdio.h>
  2. #include <string>
  3. #include <conio.h>
  4. #include <WinSock2.h>//网络相关服务头文件
  5. #pragma comment (lib,"WS2_32.lib")//网络相关库文件
  6. using namespace std;
  7. HANDLE hMutex;//互斥锁(用于线程之间的互斥)
  8. #define SERVER_IP "118.126.117.125"//服务器IP 字符串
  9. #define QUN_LIAO_PROT 2022
  10. char line1[111];//up
  11. char line2[111];//空白字符分割线
  12. char nickName[32];//昵称
  13. SOCKET serverSocket;//网络套接字
  14. sockaddr_in sockAddr;//网络地址
  15. void GBKToUTF8(string& strGBK);
  16. void gotoxy(int x, int y);
  17. string UTF8ToGBK(const char* strUTF8);

main.cpp

  1. #include "main.h"
  2. //转码
  3. void GBKToUTF8(string& strGBK)
  4. {
  5. int len = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
  6. wchar_t* wszUtf8 = new wchar_t[len];
  7. memset(wszUtf8, 0, len);
  8. MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wszUtf8, len);
  9. len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
  10. char* szUtf8 = new char[len + 1];
  11. memset(szUtf8, 0, len + 1);
  12. WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
  13. strGBK = szUtf8;
  14. delete[] szUtf8;
  15. delete[] wszUtf8;
  16. }
  17. void gotoxy(int x,int y)
  18. {
  19. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄
  20. COORD pos = { x,y };
  21. SetConsoleCursorPosition(hOut,pos);
  22. }
  23. void uiInit()
  24. {
  25. system("mode con lines=36 cols=110");
  26. system("cls");
  27. gotoxy(0,33);
  28. for (int i = 0; i < 110; i++)line1[i] = '-';
  29. line1[110] = 0;
  30. for (int i = 0; i < 110; i++)line2[i] = ' ';
  31. line2[110] = 0;
  32. printf("%s\n\n", line1);
  33. }
  34. /*
  35. *
  36. * mciSendString("open ./images/小情歌.mp3 alias bgm ", NULL, 0, NULL);
  37. mciSendString("play bgm", NULL, 0, NULL);
  38. */
  39. void login()
  40. {
  41. system("mode con lines=5 cols=30 \n ");
  42. printf(" 欢迎进入内部聊天室 \n \n");
  43. printf(" 昵称:");
  44. scanf_s("%s", nickName, sizeof(nickName));
  45. while (getchar() != '\n');//清空输入缓冲区
  46. string name = nickName;
  47. GBKToUTF8(name );
  48. send(serverSocket,name.c_str()/*c++字符串 转成c语言*/, strlen(name.c_str()) + 1, 0);//send niskname to server 如果中文,可能回出问题
  49. //做send检查
  50. }
  51. string UTF8ToGBK(const char* strUTF8)
  52. {
  53. int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
  54. wchar_t* wszGBK = new wchar_t[len + 1];
  55. memset(wszGBK, 0, len * 2 + 2);
  56. MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
  57. len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
  58. char* szGBK = new char[len + 1];
  59. memset(szGBK, 0, len + 1);
  60. WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
  61. std::string strTemp(szGBK);
  62. if (wszGBK) delete[] wszGBK;
  63. if (szGBK) delete[] szGBK;
  64. return strTemp;
  65. }
  66. void printMsg(const char* msg)
  67. {
  68. //上锁(申请互斥锁)
  69. WaitForSingleObject(hMutex, INFINITE/*等待时间 永远等*/);
  70. //没有申请到 就一直等待 直到等到为止
  71. static POINT pos = { 0,0 };
  72. gotoxy(pos.x, pos.y);
  73. //printf("%s \n", msg);
  74. static int color = 31;
  75. printf("\033[0;%d;40m%s\033[0m\n",color++,msg);
  76. if (color > 36)
  77. {
  78. color = 31;
  79. }
  80. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  81. CONSOLE_SCREEN_BUFFER_INFO info;
  82. GetConsoleScreenBufferInfo(hOut,&info);
  83. pos.x = info.dwCursorPosition.X;
  84. pos.y = info.dwCursorPosition.Y;
  85. if (pos.y >= 33)
  86. {
  87. printf("%s\n", line2);
  88. printf("\n\n");
  89. gotoxy(0, 33);
  90. printf("%s\n ",line1);
  91. pos.y -= 1;
  92. }
  93. gotoxy(1,34);
  94. //释放锁
  95. ReleaseMutex(hMutex);
  96. }
  97. void editPrint(int col,int ch)
  98. {
  99. WaitForSingleObject(hMutex, INFINITE);
  100. gotoxy(col, 34);
  101. printf("%c",ch);
  102. ReleaseMutex(hMutex);
  103. }
  104. void editPrint(int col,const char * str)
  105. {
  106. WaitForSingleObject(hMutex, INFINITE);
  107. gotoxy(col, 34);
  108. printf("%s", str);
  109. ReleaseMutex(hMutex);
  110. }
  111. bool init()
  112. {
  113. WSADATA data;//初始化结果
  114. //1.网络服务初始化
  115. int ret = WSAStartup(MAKEWORD(1, 1), &data);
  116. if (ret != 0)
  117. {
  118. return false;
  119. }
  120. //2.网络套接字socket
  121. serverSocket = socket(PF_INET/*网络 数据流 数据报 类型*/, SOCK_STREAM/*数据流*/,IPPROTO_TCP/*TCP协议*/);
  122. //3.配置物理地址
  123. sockAddr.sin_family = PF_INET;//网络地址
  124. sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
  125. //端口号 最大为65535 转字节序
  126. sockAddr.sin_port = htons(QUN_LIAO_PROT);
  127. CreateMutex(0, 0, L"console");
  128. return true;
  129. }
  130. //创建线程
  131. DWORD WINAPI threadFuncRecy(LPVOID pram) {
  132. char buff[4096];//接收信息
  133. while (1) {
  134. int ret = recv(serverSocket, buff, sizeof(buff),0);
  135. if (ret <= 0) {
  136. printf("服务器关闭或故障\n");
  137. break;
  138. }
  139. //打印接收到的信息
  140. printMsg(UTF8ToGBK(buff).c_str());
  141. }
  142. return NULL;
  143. }
  144. bool isHZ(char str[], int index) {
  145. //一个汉字占两个字节 第一个字节< 0; 第二个字节 有可能小于零 也可能大于0
  146. //一个英文字符,只有一个字节, 》0
  147. int i = 0;
  148. while (i < index) {
  149. if (str[i] > 0) {
  150. i++;
  151. }
  152. else {
  153. i += 2;
  154. }
  155. }
  156. if (i == index)
  157. {
  158. return false;
  159. }
  160. else {
  161. return true;
  162. }
  163. }
  164. int main(void)
  165. {
  166. if (init() == false) {
  167. printf("Initialization failed\n");
  168. getchar();
  169. return -1;
  170. }
  171. else {
  172. printf("Initialization succeeded \n");
  173. }
  174. // 连接服务器(发起网络连接)
  175. int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
  176. if (ret != 0) {
  177. printf("Initialization failed Failed to connect to the server. Please check the network \n");
  178. return 1;
  179. }
  180. else
  181. {
  182. printf("connect succeeded \n");
  183. }
  184. //登录聊天室
  185. login();
  186. uiInit();//初始化界面
  187. //创建线程回返回一个线程句柄
  188. HANDLE hThread = CreateThread(0, 0, threadFuncRecy,0,0,0);
  189. CloseHandle(hThread);//关闭句柄 没关闭线程
  190. char buff[1024];//保存用户输入的字符串
  191. //内容设置为零
  192. memset(buff, 0,sizeof(buff));
  193. //编辑信息
  194. while (1)
  195. {
  196. editPrint(0,'>');
  197. int len = 0;
  198. while (1){
  199. if (_kbhit())
  200. {
  201. char c = getch();
  202. if (c == '\r')
  203. {
  204. //按下回车
  205. break;
  206. }
  207. else if(c == 8)//退格键
  208. {
  209. if (len == 0)
  210. {
  211. continue;
  212. }
  213. //删除
  214. if (isHZ(buff, len - 1) ) {
  215. //printf("\b\b \b\b");
  216. editPrint(len + 1, "\b\b \b\b");
  217. buff[len - 1] = 0;
  218. buff[len - 2] = 0;
  219. len -= 2;
  220. }
  221. else {
  222. editPrint(len + 1, "\b \b");
  223. buff[len - 1] = 0;
  224. len -= 1;
  225. }
  226. continue;
  227. }
  228. WaitForSingleObject(hMutex, INFINITE);
  229. do {
  230. printf("%c",c);
  231. buff[len++] = c;
  232. } while (_kbhit()&& (c=getch()));
  233. ReleaseMutex(hMutex);
  234. //editPrint(len + 1,c);
  235. //buff[len++] = c ;
  236. }
  237. }
  238. if (len == 0)
  239. {
  240. continue;
  241. }
  242. //清楚编辑区的信息
  243. char buff2[1024];
  244. sprintf_s(buff2,sizeof(buff2),"%s\n",line2);
  245. editPrint(0, buff2);
  246. //把用户自己说的话,输出到聊天室
  247. sprintf_s(buff2, sizeof(buff2), "【localHost@%s】%s", nickName, buff);
  248. printMsg(buff2);
  249. //发送编辑好的字符串
  250. send(serverSocket,buff, strlen(buff)+1,0);
  251. }
  252. getchar();
  253. return 0;
  254. }

 

 

    GBKToUTF8:将GBK编码的字符串转换为UTF-8编码的字符串。
    gotoxy:在控制台上设置光标的位置。
    UTF8ToGBK:将UTF-8编码的字符串转换为GBK编码的字符串。
    uiInit:初始化控制台界面,设置控制台的大小和清空屏幕。
    login:用户登录函数,用户输入昵称并发送到服务器。
    printMsg:打印接收到的消息到控制台界面。
    editPrint:在控制台界面上打印编辑区的信息。
    init:初始化网络服务,包括启动WinSock和创建网络套接字。
    threadFuncRecy:接收消息的线程函数,负责接收服务器发送的消息并打印到控制台界面。
    isHZ:判断一个字符是否是汉字。
    main:程序的入口函数,包括初始化网络连接、登录、界面初始化、创建接收线程和处理用户输入等功能。

下面是一些优化建议:

  使用更现代化的网络库:WinSock2是一个较旧的网络库,可以考虑使用更现代化的网络库,如Boost.Asio或C++11/14中引入的网络功能,以提供更简洁和高效的网络通信代码。

    错误处理和异常处理:代码中需要更好的错误处理和异常处理机制。例如,在连接服务器、发送和接收数据时,应检查返回值并处理可能出现的错误情况,而不仅仅是简单地打印错误消息。可以使用异常处理来更好地处理错误和异常情况。

    优化界面显示:目前的界面显示使用了控制台打印,可以考虑使用图形界面或更高级的用户界面库来提升用户体验。

    代码结构和模块化:代码可以更好地组织和模块化,将不同的功能模块分离开来,以提高代码的可读性和维护性。可以将网络通信部分、界面显示部分和其他功能部分分别封装成不同的函数或类。

    代码注释和文档:为了方便他人阅读和理解代码,可以添加更多的注释来解释代码的逻辑和功能。此外,编写适当的文档和说明文件,以便其他开发人员理解和使用这段代码。

    使用现代C++特性:代码中使用了一些较旧的C++特性,可以考虑使用现代C++特性来简化代码并提高性能。例如,可以使用智能指针、lambda表达式和更好的字符串处理函数来改进代码。

错误异常处理

  1. #include <stdexcept> // 引入标准异常类头文件
  2. // ...
  3. bool init()
  4. {
  5. WSADATA data;
  6. int ret = WSAStartup(MAKEWORD(2, 2), &data);
  7. if (ret != 0)
  8. {
  9. throw std::runtime_error("Failed to initialize WinSock");
  10. }
  11. serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  12. if (serverSocket == INVALID_SOCKET)
  13. {
  14. WSACleanup();
  15. throw std::runtime_error("Failed to create socket");
  16. }
  17. // ...
  18. return true;
  19. }
  20. void login()
  21. {
  22. system("mode con lines=5 cols=30 \n ");
  23. printf(" 欢迎进入内部聊天室 \n \n");
  24. printf(" 昵称:");
  25. scanf_s("%s", nickName, sizeof(nickName));
  26. while (getchar() != '\n');
  27. string name = nickName;
  28. GBKToUTF8(name);
  29. int ret = send(serverSocket, name.c_str(), strlen(name.c_str()) + 1, 0);
  30. if (ret == SOCKET_ERROR)
  31. {
  32. closesocket(serverSocket);
  33. WSACleanup();
  34. throw std::runtime_error("Failed to send nickname to server");
  35. }
  36. }
  37. // ...
  38. DWORD WINAPI threadFuncRecy(LPVOID pram)
  39. {
  40. char buff[4096];
  41. while (1)
  42. {
  43. int ret = recv(serverSocket, buff, sizeof(buff), 0);
  44. if (ret <= 0)
  45. {
  46. closesocket(serverSocket);
  47. WSACleanup();
  48. throw std::runtime_error("Server closed or encountered an error");
  49. }
  50. printMsg(UTF8ToGBK(buff).c_str());
  51. }
  52. return NULL;
  53. }
  54. // ...
  55. int main(void)
  56. {
  57. try
  58. {
  59. if (init() == false)
  60. {
  61. throw std::runtime_error("Initialization failed");
  62. }
  63. int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
  64. if (ret != 0)
  65. {
  66. closesocket(serverSocket);
  67. WSACleanup();
  68. throw std::runtime_error("Failed to connect to the server");
  69. }
  70. // ...
  71. }
  72. catch (const std::exception& ex)
  73. {
  74. printf("An error occurred: %s\n", ex.what());
  75. return 1;
  76. }
  77. return 0;
  78. }

 代码结构模块化

  1. #include <stdio.h>
  2. #include <string>
  3. #include <conio.h>
  4. #include <WinSock2.h>
  5. #include <stdexcept>
  6. #pragma comment(lib, "WS2_32.lib")
  7. using namespace std;
  8. // 定义常量
  9. #define SERVER_IP "118.126.117.125"
  10. #define QUN_LIAO_PROT 2022
  11. // 函数声明
  12. void GBKToUTF8(string& strGBK);
  13. void gotoxy(int x, int y);
  14. string UTF8ToGBK(const char* strUTF8);
  15. void uiInit();
  16. void login();
  17. void printMsg(const char* msg);
  18. void editPrint(int col, int ch);
  19. void editPrint(int col, const char* str);
  20. bool init();
  21. DWORD WINAPI threadFuncRecy(LPVOID pram);
  22. bool isHZ(char str[], int index);
  23. // 全局变量
  24. HANDLE hMutex; // 互斥锁
  25. char line1[111]; // 分隔线
  26. char line2[111]; // 空白字符分割线
  27. char nickName[32]; // 昵称
  28. SOCKET serverSocket; // 网络套接字
  29. sockaddr_in sockAddr; // 网络地址
  30. void GBKToUTF8(string& strGBK)
  31. {
  32. // 转码实现
  33. }
  34. void gotoxy(int x, int y)
  35. {
  36. // 设置光标位置实现
  37. }
  38. string UTF8ToGBK(const char* strUTF8)
  39. {
  40. // 转码实现
  41. }
  42. void uiInit()
  43. {
  44. // 界面初始化实现
  45. }
  46. void login()
  47. {
  48. // 用户登录实现
  49. }
  50. void printMsg(const char* msg)
  51. {
  52. // 打印消息实现
  53. }
  54. void editPrint(int col, int ch)
  55. {
  56. // 编辑区打印字符实现
  57. }
  58. void editPrint(int col, const char* str)
  59. {
  60. // 编辑区打印字符串实现
  61. }
  62. bool init()
  63. {
  64. // 初始化网络服务实现
  65. }
  66. DWORD WINAPI threadFuncRecy(LPVOID pram)
  67. {
  68. // 接收消息的线程函数实现
  69. }
  70. bool isHZ(char str[], int index)
  71. {
  72. // 判断字符是否是汉字实现
  73. }
  74. int main(void)
  75. {
  76. try
  77. {
  78. if (init() == false)
  79. {
  80. throw std::runtime_error("Initialization failed");
  81. }
  82. int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
  83. if (ret != 0)
  84. {
  85. throw std::runtime_error("Failed to connect to the server");
  86. }
  87. login();
  88. uiInit();
  89. HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
  90. CloseHandle(hThread);
  91. char buff[1024];
  92. memset(buff, 0, sizeof(buff));
  93. while (1)
  94. {
  95. // 编辑信息实现
  96. }
  97. }
  98. catch (const std::exception& ex)
  99. {
  100. printf("An error occurred: %s\n", ex.what());
  101. return 1;
  102. }
  103. return 0;
  104. }

代码 注释和文档

  1. #include <stdio.h>
  2. #include <string>
  3. #include <conio.h>
  4. #include <WinSock2.h>
  5. #include <stdexcept>
  6. #pragma comment(lib, "WS2_32.lib")
  7. using namespace std;
  8. // 定义常量
  9. #define SERVER_IP "118.126.117.125" // 服务器IP
  10. #define QUN_LIAO_PROT 2022 // 聊天室端口号
  11. // 函数声明
  12. // 将GBK编码的字符串转换为UTF-8编码的字符串
  13. void GBKToUTF8(string& strGBK);
  14. // 在控制台上设置光标的位置
  15. void gotoxy(int x, int y);
  16. // 将UTF-8编码的字符串转换为GBK编码的字符串
  17. string UTF8ToGBK(const char* strUTF8);
  18. // 初始化控制台界面
  19. void uiInit();
  20. // 用户登录
  21. void login();
  22. // 打印接收到的消息到控制台界面
  23. void printMsg(const char* msg);
  24. // 在控制台界面上打印编辑区的字符
  25. void editPrint(int col, int ch);
  26. // 在控制台界面上打印编辑区的字符串
  27. void editPrint(int col, const char* str);
  28. // 初始化网络服务
  29. bool init();
  30. // 接收消息的线程函数
  31. DWORD WINAPI threadFuncRecy(LPVOID pram);
  32. // 判断一个字符是否是汉字
  33. bool isHZ(char str[], int index);
  34. int main(void)
  35. {
  36. try
  37. {
  38. if (init() == false)
  39. {
  40. throw std::runtime_error("Initialization failed");
  41. }
  42. int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
  43. if (ret != 0)
  44. {
  45. throw std::runtime_error("Failed to connect to the server");
  46. }
  47. login();
  48. uiInit();
  49. HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
  50. CloseHandle(hThread);
  51. char buff[1024];
  52. memset(buff, 0, sizeof(buff));
  53. while (1)
  54. {
  55. // 编辑信息实现
  56. }
  57. }
  58. catch (const std::exception& ex)
  59. {
  60. printf("An error occurred: %s\n", ex.what());
  61. return 1;
  62. }
  63. return 0;
  64. }

        

  1. #include <stdio.h>
  2. #include <string>
  3. #include <conio.h>
  4. #include <WinSock2.h>
  5. #include <stdexcept>
  6. #pragma comment(lib, "WS2_32.lib")
  7. using namespace std;
  8. // 定义常量
  9. #define SERVER_IP "118.126.117.125" // 服务器IP
  10. #define QUN_LIAO_PROT 2022 // 聊天室端口号
  11. // 函数声明
  12. void GBKToUTF8(string& strGBK);
  13. void gotoxy(int x, int y);
  14. string UTF8ToGBK(const char* strUTF8);
  15. void uiInit();
  16. void login();
  17. void printMsg(const char* msg);
  18. void editPrint(int col, int ch);
  19. void editPrint(int col, const char* str);
  20. bool init();
  21. DWORD WINAPI threadFuncRecy(LPVOID pram);
  22. bool isHZ(char str[], int index);
  23. // 全局变量
  24. HANDLE hMutex; // 互斥锁
  25. char line1[111]; // 分隔线
  26. char line2[111]; // 空白字符分割线
  27. char nickName[32]; // 昵称
  28. SOCKET serverSocket; // 网络套接字
  29. sockaddr_in sockAddr; // 网络地址
  30. void GBKToUTF8(string& strGBK)
  31. {
  32. int len = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
  33. wchar_t* wszUtf8 = new wchar_t[len];
  34. memset(wszUtf8, 0, len);
  35. MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wszUtf8, len);
  36. len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
  37. char* szUtf8 = new char[len + 1];
  38. memset(szUtf8, 0, len + 1);
  39. WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
  40. strGBK = szUtf8;
  41. delete[] szUtf8;
  42. delete[] wszUtf8;
  43. }
  44. void gotoxy(int x, int y)
  45. {
  46. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  47. COORD pos = { x, y };
  48. SetConsoleCursorPosition(hOut, pos);
  49. }
  50. string UTF8ToGBK(const char* strUTF8)
  51. {
  52. int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
  53. wchar_t* wszGBK = new wchar_t[len + 1];
  54. memset(wszGBK, 0, len * 2 + 2);
  55. MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
  56. len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
  57. char* szGBK = new char[len + 1];
  58. memset(szGBK, 0, len + 1);
  59. WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
  60. string strTemp(szGBK);
  61. if (wszGBK) delete[] wszGBK;
  62. if (szGBK) delete[] szGBK;
  63. return strTemp;
  64. }
  65. void uiInit()
  66. {
  67. system("mode con lines=36 cols=110");
  68. system("cls");
  69. gotoxy(0, 33);
  70. for (int i = 0; i < 110; i++) line1[i] = '-';
  71. line1[110] = 0;
  72. for (int i = 0; i < 110; i++) line2[i] = ' ';
  73. line2[110] = 0;
  74. printf("%s\n\n", line1);
  75. }
  76. void login()
  77. {
  78. system("mode con lines=5 cols=30 \n ");
  79. printf(" 欢迎进入内部聊天室 \n \n");
  80. printf(" 昵称:");
  81. scanf_s("%s", nickName, sizeof(nickName));
  82. while (getchar() != '\n');
  83. string name = nickName;
  84. GBKToUTF8(name);
  85. send(serverSocket, name.c_str(), strlen(name.c_str()) + 1, 0);
  86. }
  87. void printMsg(const char* msg)
  88. {
  89. WaitForSingleObject(hMutex, INFINITE);
  90. static POINT pos = { 0,0 };
  91. gotoxy(pos.x, pos.y);
  92. static int color = 31;
  93. printf("\033[0;%d;40m%s\033[0m\n", color++, msg);
  94. if (color > 36)
  95. {
  96. color = 31;
  97. }
  98. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  99. CONSOLE_SCREEN_BUFFER_INFO info;
  100. GetConsoleScreenBufferInfo(hOut, &info);
  101. pos.x = info.dwCursorPosition.X;
  102. pos.y = info.dwCursorPosition.Y;
  103. if (pos.y >= 33)
  104. {
  105. printf("%s\n", line2);
  106. printf("\n\n");
  107. gotoxy(0, 33);
  108. printf("%s\n ", line1);
  109. pos.y -= 1;
  110. }
  111. gotoxy(1, 34);
  112. ReleaseMutex(hMutex);
  113. }
  114. void editPrint(int col, int ch)
  115. {
  116. WaitForSingleObject(hMutex, INFINITE);
  117. gotoxy(col, 34);
  118. printf("%c", ch);
  119. ReleaseMutex(hMutex);
  120. }
  121. void editPrint(int col, const char* str)
  122. {
  123. WaitForSingleObject(hMutex, INFINITE);
  124. gotoxy(col, 34);
  125. printf("%s", str);
  126. ReleaseMutex(hMutex);
  127. }
  128. bool init()
  129. {
  130. WSADATA data;
  131. int ret = WSAStartup(MAKEWORD(2, 2), &data);
  132. if (ret != 0)
  133. {
  134. return false;
  135. }
  136. serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  137. if (serverSocket == INVALID_SOCKET)
  138. {
  139. WSACleanup();
  140. return false;
  141. }
  142. sockAddr.sin_family = PF_INET;
  143. sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
  144. sockAddr.sin_port = htons(QUN_LIAO_PROT);
  145. CreateMutex(0, 0, L"console");
  146. return true;
  147. }
  148. DWORD WINAPI threadFuncRecy(LPVOID pram)
  149. {
  150. char buff[4096];
  151. while (1)
  152. {
  153. int ret = recv(serverSocket, buff, sizeof(buff), 0);
  154. if (ret <= 0)
  155. {
  156. printf("Server closed or encountered an error\n");
  157. break;
  158. }
  159. printMsg(UTF8ToGBK(buff).c_str());
  160. }
  161. return NULL;
  162. }
  163. bool isHZ(char str[], int index)
  164. {
  165. int i = 0;
  166. while (i < index)
  167. {
  168. if (str[i] > 0)
  169. {
  170. i++;
  171. }
  172. else
  173. {
  174. i += 2;
  175. }
  176. }
  177. if (i == index)
  178. {
  179. return false;
  180. }
  181. else
  182. {
  183. return true;
  184. }
  185. }
  186. int main(void)
  187. {
  188. try
  189. {
  190. if (init() == false)
  191. {
  192. throw std::runtime_error("Initialization failed");
  193. }
  194. int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
  195. if (ret != 0)
  196. {
  197. throw std::runtime_error("Failed to connect to the server");
  198. }
  199. login();
  200. uiInit();
  201. HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
  202. CloseHandle(hThread);
  203. char buff[1024];
  204. memset(buff, 0, sizeof(buff));
  205. while (1)
  206. {
  207. editPrint(0, '>');
  208. int len = 0;
  209. while (1)
  210. {
  211. if (_kbhit())
  212. {
  213. char c = getch();
  214. if (c == '\r')
  215. {
  216. break;
  217. }
  218. else if (c == 8)
  219. {
  220. if (len == 0)
  221. {
  222. continue;
  223. }
  224. if (isHZ(buff, len - 1))
  225. {
  226. editPrint(len + 1, "\b\b \b\b");
  227. buff[len - 1] = 0;
  228. buff[len - 2] = 0;
  229. len -= 2;
  230. }
  231. else
  232. {
  233. editPrint(len + 1, "\b \b");
  234. buff[len - 1] = 0;
  235. len -= 1;
  236. }
  237. continue;
  238. }
  239. WaitForSingleObject(hMutex, INFINITE);
  240. do
  241. {
  242. printf("%c", c);
  243. buff[len++] = c;
  244. } while (_kbhit() && (c = getch()));
  245. ReleaseMutex(hMutex);
  246. }
  247. }
  248. if (len == 0)
  249. {
  250. continue;
  251. }
  252. char buff2[1024];
  253. sprintf_s(buff2, sizeof(buff2), "%s\n", line2);
  254. editPrint(0, buff2);
  255. sprintf_s(buff2, sizeof(buff2), "【localHost@%s】%s", nickName, buff);
  256. printMsg(buff2);
  257. send(serverSocket, buff, strlen(buff) + 1, 0);
  258. }
  259. }
  260. catch (const std::exception& ex)
  261. {
  262. printf("An error occurred: %s\n", ex.what());
  263. return 1;
  264. }
  265. return 0;
  266. }
  1. #include <stdio.h>
  2. #include <string>
  3. #include <conio.h>
  4. #include <WinSock2.h>
  5. #include <stdexcept>
  6. #pragma comment(lib, "WS2_32.lib")
  7. using namespace std;
  8. // 定义常量
  9. #define SERVER_IP "118.126.117.125" // 服务器IP
  10. #define QUN_LIAO_PROT 2022 // 聊天室端口号
  11. // 函数声明
  12. void GBKToUTF8(string& strGBK);
  13. void gotoxy(int x, int y);
  14. string UTF8ToGBK(const char* strUTF8);
  15. void uiInit();
  16. void login();
  17. void printMsg(const char* msg);
  18. void editPrint(int col, int ch);
  19. void editPrint(int col, const char* str);
  20. bool init();
  21. DWORD WINAPI threadFuncRecy(LPVOID pram);
  22. bool isHZ(char str[], int index);
  23. // 全局变量
  24. HANDLE hMutex; // 互斥锁
  25. char line1[111]; // 分隔线
  26. char line2[111]; // 空白字符分割线
  27. char nickName[32]; // 昵称
  28. SOCKET serverSocket; // 网络套接字
  29. sockaddr_in sockAddr; // 网络地址
  30. void GBKToUTF8(string& strGBK)
  31. {
  32. int len = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
  33. wchar_t* wszUtf8 = new wchar_t[len];
  34. memset(wszUtf8, 0, len);
  35. MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wszUtf8, len);
  36. len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
  37. char* szUtf8 = new char[len + 1];
  38. memset(szUtf8, 0, len + 1);
  39. WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
  40. strGBK = szUtf8;
  41. delete[] szUtf8;
  42. delete[] wszUtf8;
  43. }
  44. void gotoxy(int x, int y)
  45. {
  46. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  47. COORD pos = { x, y };
  48. SetConsoleCursorPosition(hOut, pos);
  49. }
  50. string UTF8ToGBK(const char* strUTF8)
  51. {
  52. int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
  53. wchar_t* wszGBK = new wchar_t[len + 1];
  54. memset(wszGBK, 0, len * 2 + 2);
  55. MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
  56. len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
  57. char* szGBK = new char[len + 1];
  58. memset(szGBK, 0, len + 1);
  59. WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
  60. string strTemp(szGBK);
  61. if (wszGBK) delete[] wszGBK;
  62. if (szGBK) delete[] szGBK;
  63. return strTemp;
  64. }
  65. void uiInit()
  66. {
  67. system("mode con lines=36 cols=110");
  68. system("cls");
  69. gotoxy(0, 33);
  70. for (int i = 0; i < 110; i++) line1[i] = '-';
  71. line1[110] = 0;
  72. for (int i = 0; i < 110; i++) line2[i] = ' ';
  73. line2[110] = 0;
  74. printf("%s\n\n", line1);
  75. }
  76. void login()
  77. {
  78. system("mode con lines=5 cols=30 \n ");
  79. printf(" 欢迎进入内部聊天室 \n \n");
  80. printf(" 昵称:");
  81. scanf_s("%s", nickName, sizeof(nickName));
  82. while (getchar() != '\n');
  83. string name = nickName;
  84. GBKToUTF8(name);
  85. send(serverSocket, name.c_str(), strlen(name.c_str()) + 1, 0);
  86. }
  87. void printMsg(const char* msg)
  88. {
  89. WaitForSingleObject(hMutex, INFINITE);
  90. static POINT pos = { 0,0 };
  91. gotoxy(pos.x, pos.y);
  92. static int color = 31;
  93. printf("\033[0;%d;40m%s\033[0m\n", color++, msg);
  94. if (color > 36)
  95. {
  96. color = 31;
  97. }
  98. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  99. CONSOLE_SCREEN_BUFFER_INFO info;
  100. GetConsoleScreenBufferInfo(hOut, &info);
  101. pos.x = info.dwCursorPosition.X;
  102. pos.y = info.dwCursorPosition.Y;
  103. if (pos.y >= 33)
  104. {
  105. printf("%s\n", line2);
  106. printf("\n\n");
  107. gotoxy(0, 33);
  108. printf("%s\n ", line1);
  109. pos.y -= 1;
  110. }
  111. gotoxy(1, 34);
  112. ReleaseMutex(hMutex);
  113. }
  114. void editPrint(int col, int ch)
  115. {
  116. WaitForSingleObject(hMutex, INFINITE);
  117. gotoxy(col, 34);
  118. printf("%c", ch);
  119. ReleaseMutex(hMutex);
  120. }
  121. void editPrint(int col, const char* str)
  122. {
  123. WaitForSingleObject(hMutex, INFINITE);
  124. gotoxy(col, 34);
  125. printf("%s", str);
  126. ReleaseMutex(hMutex);
  127. }
  128. bool init()
  129. {
  130. WSADATA data;
  131. int ret = WSAStartup(MAKEWORD(2, 2), &data);
  132. if (ret != 0)
  133. {
  134. return false;
  135. }
  136. serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  137. if (serverSocket == INVALID_SOCKET)
  138. {
  139. WSACleanup();
  140. return false;
  141. }
  142. sockAddr.sin_family = PF_INET;
  143. sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
  144. sockAddr.sin_port = htons(QUN_LIAO_PROT);
  145. CreateMutex(0, 0, L"console");
  146. return true;
  147. }
  148. DWORD WINAPI threadFuncRecy(LPVOID pram)
  149. {
  150. char buff[4096];
  151. while (1)
  152. {
  153. int ret = recv(serverSocket, buff, sizeof(buff), 0);
  154. if (ret <= 0)
  155. {
  156. printf("Server closed or encountered an error\n");
  157. break;
  158. }
  159. printMsg(UTF8ToGBK(buff).c_str());
  160. }
  161. return NULL;
  162. }
  163. bool isHZ(char str[], int index)
  164. {
  165. int i = 0;
  166. while (i < index)
  167. {
  168. if (str[i] > 0)
  169. {
  170. i++;
  171. }
  172. else
  173. {
  174. i += 2;
  175. }
  176. }
  177. if (i == index)
  178. {
  179. return false;
  180. }
  181. else
  182. {
  183. return true;
  184. }
  185. }
  186. int main(void)
  187. {
  188. try
  189. {
  190. if (init() == false)
  191. {
  192. throw std::runtime_error("Initialization failed");
  193. }
  194. int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
  195. if (ret != 0)
  196. {
  197. throw std::runtime_error("Failed to connect to the server");
  198. }
  199. login();
  200. uiInit();
  201. HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
  202. CloseHandle(hThread);
  203. char buff[1024];
  204. memset(buff, 0, sizeof(buff));
  205. while (1)
  206. {
  207. editPrint(0, '>');
  208. int len = 0;
  209. while (1)
  210. {
  211. if (_kbhit())
  212. {
  213. char c = getch();
  214. if (c == '\r')
  215. {
  216. break;
  217. }
  218. else if (c == 8)
  219. {
  220. if (len == 0)
  221. {
  222. continue;
  223. }
  224. if (isHZ(buff, len - 1))
  225. {
  226. editPrint(len + 1, "\b\b \b\b");
  227. buff[len - 1] = 0;
  228. buff[len - 2] = 0;
  229. len -= 2;
  230. }
  231. else
  232. {
  233. editPrint(len + 1, "\b \b");
  234. buff[len - 1] = 0;
  235. len -= 1;
  236. }
  237. continue;
  238. }
  239. WaitForSingleObject(hMutex, INFINITE);
  240. do
  241. {
  242. printf("%c", c);
  243. buff[len++] = c;
  244. } while (_kbhit() && (c = getch()));
  245. ReleaseMutex(hMutex);
  246. }
  247. }
  248. if (len == 0)
  249. {
  250. continue;
  251. }
  252. char buff2[1024];
  253. sprintf_s(buff2, sizeof(buff2), "%s\n", line2);
  254. editPrint(0, buff2);
  255. sprintf_s(buff2, sizeof(buff2), "【localHost@%s】%s", nickName, buff);
  256. printMsg(buff2);
  257. send(serverSocket, buff, strlen(buff) + 1, 0);
  258. }
  259. }
  260. catch (const std::exception& ex)
  261. {
  262. printf("An error occurred: %s\n", ex.what());
  263. return 1;
  264. }
  265. return 0;
  266. }

程序的主要功能包括:

  1. 初始化:使用WSAStartup函数初始化WinSock库,并创建客户端的套接字。

  2. 连接服务器:使用connect函数与指定的服务器建立连接。

  3. 登录:通过输入昵称进行登录。

  4. 界面初始化:设置控制台的窗口大小和清屏操作。

  5. 消息打印:使用printMsg函数打印接收到的消息。

  6. 编辑区域打印:使用editPrint函数在控制台指定位置打印输入的消息。

  7. 接收线程:创建一个线程用于接收服务器发送的消息,并在接收到消息时调用printMsg函数打印消息。

  8. 消息发送:在主循环中,监听键盘输入,并将输入的消息发送给服务器。

 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/663336
推荐阅读
相关标签
  

闽ICP备14008679号