赞
踩
项目涉及Windows网络编程开发
服务器使用Linux系统
互斥锁的使用
多线程开发
windows 控制台的设置开发
项目效果预览
c++聊天室
- #include <iostream>
- #include <sys/socket.h>
- #include <arpa/inet.h>
创建套接字:
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd == -1) {
- std::cout << "Failed to create socket" << std::endl;
- return -1;
- }
设置服务器地址和端口
- sockaddr_in serverAddress{};
- serverAddress.sin_family = AF_INET;
- serverAddress.sin_port = htons(port); // 设置端口号 用htons转为网络字节序
- if (inet_pton(AF_INET, ipAddress, &serverAddress.sin_addr) <= 0) {
- std::cout << "Invalid address/ Address not supported" << std::endl;
- return -1;
- }
连接到服务器:
- if (connect(sockfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {
- std::cout << "Connection Failed" << std::endl;
- return -1;
- }
发送数据到服务器:
- char message[] = "Hello Server!";
- if (send(sockfd, message, strlen(message), 0) < 0) {
- std::cout << "Send failed" << std::endl;
- return -1;
- }
接收服务器返回的数据:
- char buffer[1024] {0};
- if (recv(sockfd, buffer, sizeof(buffer), 0) < 0) {
- std::cout << "Receive failed" << std::endl;
- return -1;
- }
- std::cout << "Server response: " << buffer << std::endl;
关闭套接字:
close(sockfd);
接下来就是再此基础上优化(由于客户端和服务端的编码格式不同,所以相互之间要进行转码)
头文件
-
- #include <stdio.h>
- #include <string>
-
- #include <conio.h>
- #include <WinSock2.h>//网络相关服务头文件
- #pragma comment (lib,"WS2_32.lib")//网络相关库文件
-
- using namespace std;
- HANDLE hMutex;//互斥锁(用于线程之间的互斥)
- #define SERVER_IP "118.126.117.125"//服务器IP 字符串
- #define QUN_LIAO_PROT 2022
- char line1[111];//up
- char line2[111];//空白字符分割线
- char nickName[32];//昵称
- SOCKET serverSocket;//网络套接字
- sockaddr_in sockAddr;//网络地址
- void GBKToUTF8(string& strGBK);
- void gotoxy(int x, int y);
- string UTF8ToGBK(const char* strUTF8);
main.cpp
- #include "main.h"
-
-
- //转码
- void GBKToUTF8(string& strGBK)
- {
- int len = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
- wchar_t* wszUtf8 = new wchar_t[len];
- memset(wszUtf8, 0, len);
- MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wszUtf8, len);
- len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
- char* szUtf8 = new char[len + 1];
- memset(szUtf8, 0, len + 1);
- WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
- strGBK = szUtf8;
- delete[] szUtf8;
- delete[] wszUtf8;
- }
-
- void gotoxy(int x,int y)
- {
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄
- COORD pos = { x,y };
- SetConsoleCursorPosition(hOut,pos);
-
- }
-
- void uiInit()
- {
- system("mode con lines=36 cols=110");
- system("cls");
- gotoxy(0,33);
-
- for (int i = 0; i < 110; i++)line1[i] = '-';
- line1[110] = 0;
- for (int i = 0; i < 110; i++)line2[i] = ' ';
- line2[110] = 0;
- printf("%s\n\n", line1);
-
-
- }
- /*
- *
- * mciSendString("open ./images/小情歌.mp3 alias bgm ", NULL, 0, NULL);
- mciSendString("play bgm", NULL, 0, NULL);
- */
-
- void login()
- {
- system("mode con lines=5 cols=30 \n ");
- printf(" 欢迎进入内部聊天室 \n \n");
- printf(" 昵称:");
- scanf_s("%s", nickName, sizeof(nickName));
-
- while (getchar() != '\n');//清空输入缓冲区
- string name = nickName;
- GBKToUTF8(name );
- send(serverSocket,name.c_str()/*c++字符串 转成c语言*/, strlen(name.c_str()) + 1, 0);//send niskname to server 如果中文,可能回出问题
- //做send检查
- }
-
- string UTF8ToGBK(const char* strUTF8)
- {
- int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
- wchar_t* wszGBK = new wchar_t[len + 1];
- memset(wszGBK, 0, len * 2 + 2);
- MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
- len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
- char* szGBK = new char[len + 1];
- memset(szGBK, 0, len + 1);
- WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
- std::string strTemp(szGBK);
- if (wszGBK) delete[] wszGBK;
- if (szGBK) delete[] szGBK;
- return strTemp;
- }
-
- void printMsg(const char* msg)
- {
- //上锁(申请互斥锁)
- WaitForSingleObject(hMutex, INFINITE/*等待时间 永远等*/);
- //没有申请到 就一直等待 直到等到为止
- static POINT pos = { 0,0 };
- gotoxy(pos.x, pos.y);
- //printf("%s \n", msg);
- static int color = 31;
-
- printf("\033[0;%d;40m%s\033[0m\n",color++,msg);
- if (color > 36)
- {
- color = 31;
- }
-
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO info;
- GetConsoleScreenBufferInfo(hOut,&info);
- pos.x = info.dwCursorPosition.X;
- pos.y = info.dwCursorPosition.Y;
- if (pos.y >= 33)
- {
- printf("%s\n", line2);
- printf("\n\n");
- gotoxy(0, 33);
- printf("%s\n ",line1);
- pos.y -= 1;
- }
- gotoxy(1,34);
- //释放锁
- ReleaseMutex(hMutex);
- }
-
- void editPrint(int col,int ch)
- {
- WaitForSingleObject(hMutex, INFINITE);
-
- gotoxy(col, 34);
- printf("%c",ch);
- ReleaseMutex(hMutex);
- }
-
- void editPrint(int col,const char * str)
- {
- WaitForSingleObject(hMutex, INFINITE);
-
- gotoxy(col, 34);
- printf("%s", str);
- ReleaseMutex(hMutex);
- }
-
- bool init()
- {
- WSADATA data;//初始化结果
- //1.网络服务初始化
- int ret = WSAStartup(MAKEWORD(1, 1), &data);
- if (ret != 0)
- {
- return false;
- }
- //2.网络套接字socket
-
- serverSocket = socket(PF_INET/*网络 数据流 数据报 类型*/, SOCK_STREAM/*数据流*/,IPPROTO_TCP/*TCP协议*/);
-
- //3.配置物理地址
- sockAddr.sin_family = PF_INET;//网络地址
- sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
- //端口号 最大为65535 转字节序
- sockAddr.sin_port = htons(QUN_LIAO_PROT);
-
- CreateMutex(0, 0, L"console");
-
- return true;
- }
-
- //创建线程
- DWORD WINAPI threadFuncRecy(LPVOID pram) {
-
- char buff[4096];//接收信息
- while (1) {
- int ret = recv(serverSocket, buff, sizeof(buff),0);
- if (ret <= 0) {
- printf("服务器关闭或故障\n");
- break;
- }
-
- //打印接收到的信息
-
- printMsg(UTF8ToGBK(buff).c_str());
-
- }
- return NULL;
- }
-
- bool isHZ(char str[], int index) {
- //一个汉字占两个字节 第一个字节< 0; 第二个字节 有可能小于零 也可能大于0
- //一个英文字符,只有一个字节, 》0
-
- int i = 0;
- while (i < index) {
- if (str[i] > 0) {
- i++;
- }
- else {
- i += 2;
- }
-
- }
- if (i == index)
- {
- return false;
- }
- else {
- return true;
- }
- }
-
- int main(void)
- {
-
- if (init() == false) {
- printf("Initialization failed\n");
- getchar();
- return -1;
- }
- else {
- printf("Initialization succeeded \n");
-
- }
-
-
- // 连接服务器(发起网络连接)
- int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
- if (ret != 0) {
- printf("Initialization failed Failed to connect to the server. Please check the network \n");
-
- return 1;
- }
- else
- {
- printf("connect succeeded \n");
-
- }
-
- //登录聊天室
- login();
-
- uiInit();//初始化界面
-
- //创建线程回返回一个线程句柄
- HANDLE hThread = CreateThread(0, 0, threadFuncRecy,0,0,0);
- CloseHandle(hThread);//关闭句柄 没关闭线程
- char buff[1024];//保存用户输入的字符串
- //内容设置为零
- memset(buff, 0,sizeof(buff));
- //编辑信息
- while (1)
- {
- editPrint(0,'>');
- int len = 0;
- while (1){
- if (_kbhit())
- {
- char c = getch();
- if (c == '\r')
- {
- //按下回车
- break;
- }
- else if(c == 8)//退格键
- {
- if (len == 0)
- {
- continue;
- }
- //删除
- if (isHZ(buff, len - 1) ) {
- //printf("\b\b \b\b");
- editPrint(len + 1, "\b\b \b\b");
- buff[len - 1] = 0;
- buff[len - 2] = 0;
- len -= 2;
-
- }
- else {
- editPrint(len + 1, "\b \b");
- buff[len - 1] = 0;
- len -= 1;
- }
-
- continue;
- }
-
- WaitForSingleObject(hMutex, INFINITE);
- do {
- printf("%c",c);
- buff[len++] = c;
- } while (_kbhit()&& (c=getch()));
-
- ReleaseMutex(hMutex);
-
- //editPrint(len + 1,c);
- //buff[len++] = c ;
- }
- }
- if (len == 0)
- {
- continue;
- }
-
- //清楚编辑区的信息
- char buff2[1024];
- sprintf_s(buff2,sizeof(buff2),"%s\n",line2);
- editPrint(0, buff2);
-
- //把用户自己说的话,输出到聊天室
- sprintf_s(buff2, sizeof(buff2), "【localHost@%s】%s", nickName, buff);
-
- printMsg(buff2);
- //发送编辑好的字符串
-
- send(serverSocket,buff, strlen(buff)+1,0);
- }
-
- getchar();
-
- return 0;
- }
-
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表达式和更好的字符串处理函数来改进代码。
- #include <stdexcept> // 引入标准异常类头文件
-
- // ...
-
- bool init()
- {
- WSADATA data;
- int ret = WSAStartup(MAKEWORD(2, 2), &data);
- if (ret != 0)
- {
- throw std::runtime_error("Failed to initialize WinSock");
- }
-
- serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (serverSocket == INVALID_SOCKET)
- {
- WSACleanup();
- throw std::runtime_error("Failed to create socket");
- }
-
- // ...
-
- return true;
- }
-
- void login()
- {
- system("mode con lines=5 cols=30 \n ");
- printf(" 欢迎进入内部聊天室 \n \n");
- printf(" 昵称:");
- scanf_s("%s", nickName, sizeof(nickName));
-
- while (getchar() != '\n');
-
- string name = nickName;
- GBKToUTF8(name);
- int ret = send(serverSocket, name.c_str(), strlen(name.c_str()) + 1, 0);
- if (ret == SOCKET_ERROR)
- {
- closesocket(serverSocket);
- WSACleanup();
- throw std::runtime_error("Failed to send nickname to server");
- }
- }
-
- // ...
-
- DWORD WINAPI threadFuncRecy(LPVOID pram)
- {
- char buff[4096];
- while (1)
- {
- int ret = recv(serverSocket, buff, sizeof(buff), 0);
- if (ret <= 0)
- {
- closesocket(serverSocket);
- WSACleanup();
- throw std::runtime_error("Server closed or encountered an error");
- }
-
- printMsg(UTF8ToGBK(buff).c_str());
- }
- return NULL;
- }
-
- // ...
-
- int main(void)
- {
- try
- {
- if (init() == false)
- {
- throw std::runtime_error("Initialization failed");
- }
-
- int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
- if (ret != 0)
- {
- closesocket(serverSocket);
- WSACleanup();
- throw std::runtime_error("Failed to connect to the server");
- }
-
- // ...
- }
- catch (const std::exception& ex)
- {
- printf("An error occurred: %s\n", ex.what());
- return 1;
- }
-
- return 0;
- }
- #include <stdio.h>
- #include <string>
- #include <conio.h>
- #include <WinSock2.h>
- #include <stdexcept>
-
- #pragma comment(lib, "WS2_32.lib")
-
- using namespace std;
-
- // 定义常量
- #define SERVER_IP "118.126.117.125"
- #define QUN_LIAO_PROT 2022
-
- // 函数声明
- void GBKToUTF8(string& strGBK);
- void gotoxy(int x, int y);
- string UTF8ToGBK(const char* strUTF8);
- void uiInit();
- void login();
- void printMsg(const char* msg);
- void editPrint(int col, int ch);
- void editPrint(int col, const char* str);
- bool init();
- DWORD WINAPI threadFuncRecy(LPVOID pram);
- bool isHZ(char str[], int index);
-
- // 全局变量
- HANDLE hMutex; // 互斥锁
- char line1[111]; // 分隔线
- char line2[111]; // 空白字符分割线
- char nickName[32]; // 昵称
- SOCKET serverSocket; // 网络套接字
- sockaddr_in sockAddr; // 网络地址
-
- void GBKToUTF8(string& strGBK)
- {
- // 转码实现
- }
-
- void gotoxy(int x, int y)
- {
- // 设置光标位置实现
- }
-
- string UTF8ToGBK(const char* strUTF8)
- {
- // 转码实现
- }
-
- void uiInit()
- {
- // 界面初始化实现
- }
-
- void login()
- {
- // 用户登录实现
- }
-
- void printMsg(const char* msg)
- {
- // 打印消息实现
- }
-
- void editPrint(int col, int ch)
- {
- // 编辑区打印字符实现
- }
-
- void editPrint(int col, const char* str)
- {
- // 编辑区打印字符串实现
- }
-
- bool init()
- {
- // 初始化网络服务实现
- }
-
- DWORD WINAPI threadFuncRecy(LPVOID pram)
- {
- // 接收消息的线程函数实现
- }
-
- bool isHZ(char str[], int index)
- {
- // 判断字符是否是汉字实现
- }
-
- int main(void)
- {
- try
- {
- if (init() == false)
- {
- throw std::runtime_error("Initialization failed");
- }
-
- int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
- if (ret != 0)
- {
- throw std::runtime_error("Failed to connect to the server");
- }
-
- login();
-
- uiInit();
-
- HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
- CloseHandle(hThread);
-
- char buff[1024];
- memset(buff, 0, sizeof(buff));
-
- while (1)
- {
- // 编辑信息实现
- }
- }
- catch (const std::exception& ex)
- {
- printf("An error occurred: %s\n", ex.what());
- return 1;
- }
-
- return 0;
- }
- #include <stdio.h>
- #include <string>
- #include <conio.h>
- #include <WinSock2.h>
- #include <stdexcept>
-
- #pragma comment(lib, "WS2_32.lib")
-
- using namespace std;
-
- // 定义常量
- #define SERVER_IP "118.126.117.125" // 服务器IP
- #define QUN_LIAO_PROT 2022 // 聊天室端口号
-
- // 函数声明
-
- // 将GBK编码的字符串转换为UTF-8编码的字符串
- void GBKToUTF8(string& strGBK);
-
- // 在控制台上设置光标的位置
- void gotoxy(int x, int y);
-
- // 将UTF-8编码的字符串转换为GBK编码的字符串
- string UTF8ToGBK(const char* strUTF8);
-
- // 初始化控制台界面
- void uiInit();
-
- // 用户登录
- void login();
-
- // 打印接收到的消息到控制台界面
- void printMsg(const char* msg);
-
- // 在控制台界面上打印编辑区的字符
- void editPrint(int col, int ch);
-
- // 在控制台界面上打印编辑区的字符串
- void editPrint(int col, const char* str);
-
- // 初始化网络服务
- bool init();
-
- // 接收消息的线程函数
- DWORD WINAPI threadFuncRecy(LPVOID pram);
-
- // 判断一个字符是否是汉字
- bool isHZ(char str[], int index);
-
- int main(void)
- {
- try
- {
- if (init() == false)
- {
- throw std::runtime_error("Initialization failed");
- }
-
- int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
- if (ret != 0)
- {
- throw std::runtime_error("Failed to connect to the server");
- }
-
- login();
-
- uiInit();
-
- HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
- CloseHandle(hThread);
-
- char buff[1024];
- memset(buff, 0, sizeof(buff));
-
- while (1)
- {
- // 编辑信息实现
- }
- }
- catch (const std::exception& ex)
- {
- printf("An error occurred: %s\n", ex.what());
- return 1;
- }
-
- return 0;
- }
- #include <stdio.h>
- #include <string>
- #include <conio.h>
- #include <WinSock2.h>
- #include <stdexcept>
-
- #pragma comment(lib, "WS2_32.lib")
-
- using namespace std;
-
- // 定义常量
- #define SERVER_IP "118.126.117.125" // 服务器IP
- #define QUN_LIAO_PROT 2022 // 聊天室端口号
-
- // 函数声明
- void GBKToUTF8(string& strGBK);
- void gotoxy(int x, int y);
- string UTF8ToGBK(const char* strUTF8);
- void uiInit();
- void login();
- void printMsg(const char* msg);
- void editPrint(int col, int ch);
- void editPrint(int col, const char* str);
- bool init();
- DWORD WINAPI threadFuncRecy(LPVOID pram);
- bool isHZ(char str[], int index);
-
- // 全局变量
- HANDLE hMutex; // 互斥锁
- char line1[111]; // 分隔线
- char line2[111]; // 空白字符分割线
- char nickName[32]; // 昵称
- SOCKET serverSocket; // 网络套接字
- sockaddr_in sockAddr; // 网络地址
-
- void GBKToUTF8(string& strGBK)
- {
- int len = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
- wchar_t* wszUtf8 = new wchar_t[len];
- memset(wszUtf8, 0, len);
- MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wszUtf8, len);
- len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
- char* szUtf8 = new char[len + 1];
- memset(szUtf8, 0, len + 1);
- WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
- strGBK = szUtf8;
- delete[] szUtf8;
- delete[] wszUtf8;
- }
-
- void gotoxy(int x, int y)
- {
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- COORD pos = { x, y };
- SetConsoleCursorPosition(hOut, pos);
- }
-
- string UTF8ToGBK(const char* strUTF8)
- {
- int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
- wchar_t* wszGBK = new wchar_t[len + 1];
- memset(wszGBK, 0, len * 2 + 2);
- MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
- len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
- char* szGBK = new char[len + 1];
- memset(szGBK, 0, len + 1);
- WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
- string strTemp(szGBK);
- if (wszGBK) delete[] wszGBK;
- if (szGBK) delete[] szGBK;
- return strTemp;
- }
-
- void uiInit()
- {
- system("mode con lines=36 cols=110");
- system("cls");
- gotoxy(0, 33);
-
- for (int i = 0; i < 110; i++) line1[i] = '-';
- line1[110] = 0;
- for (int i = 0; i < 110; i++) line2[i] = ' ';
- line2[110] = 0;
- printf("%s\n\n", line1);
- }
-
- void login()
- {
- system("mode con lines=5 cols=30 \n ");
- printf(" 欢迎进入内部聊天室 \n \n");
- printf(" 昵称:");
- scanf_s("%s", nickName, sizeof(nickName));
-
- while (getchar() != '\n');
-
- string name = nickName;
- GBKToUTF8(name);
- send(serverSocket, name.c_str(), strlen(name.c_str()) + 1, 0);
- }
-
- void printMsg(const char* msg)
- {
- WaitForSingleObject(hMutex, INFINITE);
- static POINT pos = { 0,0 };
- gotoxy(pos.x, pos.y);
- static int color = 31;
-
- printf("\033[0;%d;40m%s\033[0m\n", color++, msg);
- if (color > 36)
- {
- color = 31;
- }
-
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO info;
- GetConsoleScreenBufferInfo(hOut, &info);
- pos.x = info.dwCursorPosition.X;
- pos.y = info.dwCursorPosition.Y;
- if (pos.y >= 33)
- {
- printf("%s\n", line2);
- printf("\n\n");
- gotoxy(0, 33);
- printf("%s\n ", line1);
- pos.y -= 1;
- }
- gotoxy(1, 34);
- ReleaseMutex(hMutex);
- }
-
- void editPrint(int col, int ch)
- {
- WaitForSingleObject(hMutex, INFINITE);
- gotoxy(col, 34);
- printf("%c", ch);
- ReleaseMutex(hMutex);
- }
-
- void editPrint(int col, const char* str)
- {
- WaitForSingleObject(hMutex, INFINITE);
- gotoxy(col, 34);
- printf("%s", str);
- ReleaseMutex(hMutex);
- }
-
- bool init()
- {
- WSADATA data;
- int ret = WSAStartup(MAKEWORD(2, 2), &data);
- if (ret != 0)
- {
- return false;
- }
-
- serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (serverSocket == INVALID_SOCKET)
- {
- WSACleanup();
- return false;
- }
-
- sockAddr.sin_family = PF_INET;
- sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
- sockAddr.sin_port = htons(QUN_LIAO_PROT);
-
- CreateMutex(0, 0, L"console");
-
- return true;
- }
-
- DWORD WINAPI threadFuncRecy(LPVOID pram)
- {
- char buff[4096];
- while (1)
- {
- int ret = recv(serverSocket, buff, sizeof(buff), 0);
- if (ret <= 0)
- {
- printf("Server closed or encountered an error\n");
- break;
- }
-
- printMsg(UTF8ToGBK(buff).c_str());
- }
- return NULL;
- }
-
- bool isHZ(char str[], int index)
- {
- int i = 0;
- while (i < index)
- {
- if (str[i] > 0)
- {
- i++;
- }
- else
- {
- i += 2;
- }
- }
- if (i == index)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
-
- int main(void)
- {
- try
- {
- if (init() == false)
- {
- throw std::runtime_error("Initialization failed");
- }
-
- int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
- if (ret != 0)
- {
- throw std::runtime_error("Failed to connect to the server");
- }
-
- login();
-
- uiInit();
-
- HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
- CloseHandle(hThread);
-
- char buff[1024];
- memset(buff, 0, sizeof(buff));
-
- while (1)
- {
- editPrint(0, '>');
- int len = 0;
- while (1)
- {
- if (_kbhit())
- {
- char c = getch();
- if (c == '\r')
- {
- break;
- }
- else if (c == 8)
- {
- if (len == 0)
- {
- continue;
- }
- if (isHZ(buff, len - 1))
- {
- editPrint(len + 1, "\b\b \b\b");
- buff[len - 1] = 0;
- buff[len - 2] = 0;
- len -= 2;
- }
- else
- {
- editPrint(len + 1, "\b \b");
- buff[len - 1] = 0;
- len -= 1;
- }
- continue;
- }
- WaitForSingleObject(hMutex, INFINITE);
- do
- {
- printf("%c", c);
- buff[len++] = c;
- } while (_kbhit() && (c = getch()));
- ReleaseMutex(hMutex);
- }
- }
- if (len == 0)
- {
- continue;
- }
- char buff2[1024];
- sprintf_s(buff2, sizeof(buff2), "%s\n", line2);
- editPrint(0, buff2);
- sprintf_s(buff2, sizeof(buff2), "【localHost@%s】%s", nickName, buff);
- printMsg(buff2);
- send(serverSocket, buff, strlen(buff) + 1, 0);
- }
- }
- catch (const std::exception& ex)
- {
- printf("An error occurred: %s\n", ex.what());
- return 1;
- }
-
- return 0;
- }
- #include <stdio.h>
- #include <string>
- #include <conio.h>
- #include <WinSock2.h>
- #include <stdexcept>
-
- #pragma comment(lib, "WS2_32.lib")
-
- using namespace std;
-
- // 定义常量
- #define SERVER_IP "118.126.117.125" // 服务器IP
- #define QUN_LIAO_PROT 2022 // 聊天室端口号
-
- // 函数声明
- void GBKToUTF8(string& strGBK);
- void gotoxy(int x, int y);
- string UTF8ToGBK(const char* strUTF8);
- void uiInit();
- void login();
- void printMsg(const char* msg);
- void editPrint(int col, int ch);
- void editPrint(int col, const char* str);
- bool init();
- DWORD WINAPI threadFuncRecy(LPVOID pram);
- bool isHZ(char str[], int index);
-
- // 全局变量
- HANDLE hMutex; // 互斥锁
- char line1[111]; // 分隔线
- char line2[111]; // 空白字符分割线
- char nickName[32]; // 昵称
- SOCKET serverSocket; // 网络套接字
- sockaddr_in sockAddr; // 网络地址
-
- void GBKToUTF8(string& strGBK)
- {
- int len = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
- wchar_t* wszUtf8 = new wchar_t[len];
- memset(wszUtf8, 0, len);
- MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wszUtf8, len);
- len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
- char* szUtf8 = new char[len + 1];
- memset(szUtf8, 0, len + 1);
- WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
- strGBK = szUtf8;
- delete[] szUtf8;
- delete[] wszUtf8;
- }
-
- void gotoxy(int x, int y)
- {
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- COORD pos = { x, y };
- SetConsoleCursorPosition(hOut, pos);
- }
-
- string UTF8ToGBK(const char* strUTF8)
- {
- int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
- wchar_t* wszGBK = new wchar_t[len + 1];
- memset(wszGBK, 0, len * 2 + 2);
- MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
- len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
- char* szGBK = new char[len + 1];
- memset(szGBK, 0, len + 1);
- WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
- string strTemp(szGBK);
- if (wszGBK) delete[] wszGBK;
- if (szGBK) delete[] szGBK;
- return strTemp;
- }
-
- void uiInit()
- {
- system("mode con lines=36 cols=110");
- system("cls");
- gotoxy(0, 33);
-
- for (int i = 0; i < 110; i++) line1[i] = '-';
- line1[110] = 0;
- for (int i = 0; i < 110; i++) line2[i] = ' ';
- line2[110] = 0;
- printf("%s\n\n", line1);
- }
-
- void login()
- {
- system("mode con lines=5 cols=30 \n ");
- printf(" 欢迎进入内部聊天室 \n \n");
- printf(" 昵称:");
- scanf_s("%s", nickName, sizeof(nickName));
-
- while (getchar() != '\n');
-
- string name = nickName;
- GBKToUTF8(name);
- send(serverSocket, name.c_str(), strlen(name.c_str()) + 1, 0);
- }
-
- void printMsg(const char* msg)
- {
- WaitForSingleObject(hMutex, INFINITE);
- static POINT pos = { 0,0 };
- gotoxy(pos.x, pos.y);
- static int color = 31;
-
- printf("\033[0;%d;40m%s\033[0m\n", color++, msg);
- if (color > 36)
- {
- color = 31;
- }
-
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO info;
- GetConsoleScreenBufferInfo(hOut, &info);
- pos.x = info.dwCursorPosition.X;
- pos.y = info.dwCursorPosition.Y;
- if (pos.y >= 33)
- {
- printf("%s\n", line2);
- printf("\n\n");
- gotoxy(0, 33);
- printf("%s\n ", line1);
- pos.y -= 1;
- }
- gotoxy(1, 34);
- ReleaseMutex(hMutex);
- }
-
- void editPrint(int col, int ch)
- {
- WaitForSingleObject(hMutex, INFINITE);
- gotoxy(col, 34);
- printf("%c", ch);
- ReleaseMutex(hMutex);
- }
-
- void editPrint(int col, const char* str)
- {
- WaitForSingleObject(hMutex, INFINITE);
- gotoxy(col, 34);
- printf("%s", str);
- ReleaseMutex(hMutex);
- }
-
- bool init()
- {
- WSADATA data;
- int ret = WSAStartup(MAKEWORD(2, 2), &data);
- if (ret != 0)
- {
- return false;
- }
-
- serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (serverSocket == INVALID_SOCKET)
- {
- WSACleanup();
- return false;
- }
-
- sockAddr.sin_family = PF_INET;
- sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
- sockAddr.sin_port = htons(QUN_LIAO_PROT);
-
- CreateMutex(0, 0, L"console");
-
- return true;
- }
-
- DWORD WINAPI threadFuncRecy(LPVOID pram)
- {
- char buff[4096];
- while (1)
- {
- int ret = recv(serverSocket, buff, sizeof(buff), 0);
- if (ret <= 0)
- {
- printf("Server closed or encountered an error\n");
- break;
- }
-
- printMsg(UTF8ToGBK(buff).c_str());
- }
- return NULL;
- }
-
- bool isHZ(char str[], int index)
- {
- int i = 0;
- while (i < index)
- {
- if (str[i] > 0)
- {
- i++;
- }
- else
- {
- i += 2;
- }
- }
- if (i == index)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
-
- int main(void)
- {
- try
- {
- if (init() == false)
- {
- throw std::runtime_error("Initialization failed");
- }
-
- int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
- if (ret != 0)
- {
- throw std::runtime_error("Failed to connect to the server");
- }
-
- login();
-
- uiInit();
-
- HANDLE hThread = CreateThread(0, 0, threadFuncRecy, 0, 0, 0);
- CloseHandle(hThread);
-
- char buff[1024];
- memset(buff, 0, sizeof(buff));
-
- while (1)
- {
- editPrint(0, '>');
- int len = 0;
- while (1)
- {
- if (_kbhit())
- {
- char c = getch();
- if (c == '\r')
- {
- break;
- }
- else if (c == 8)
- {
- if (len == 0)
- {
- continue;
- }
- if (isHZ(buff, len - 1))
- {
- editPrint(len + 1, "\b\b \b\b");
- buff[len - 1] = 0;
- buff[len - 2] = 0;
- len -= 2;
- }
- else
- {
- editPrint(len + 1, "\b \b");
- buff[len - 1] = 0;
- len -= 1;
- }
- continue;
- }
- WaitForSingleObject(hMutex, INFINITE);
- do
- {
- printf("%c", c);
- buff[len++] = c;
- } while (_kbhit() && (c = getch()));
- ReleaseMutex(hMutex);
- }
- }
- if (len == 0)
- {
- continue;
- }
- char buff2[1024];
- sprintf_s(buff2, sizeof(buff2), "%s\n", line2);
- editPrint(0, buff2);
- sprintf_s(buff2, sizeof(buff2), "【localHost@%s】%s", nickName, buff);
- printMsg(buff2);
- send(serverSocket, buff, strlen(buff) + 1, 0);
- }
- }
- catch (const std::exception& ex)
- {
- printf("An error occurred: %s\n", ex.what());
- return 1;
- }
-
- return 0;
- }
程序的主要功能包括:
初始化:使用WSAStartup函数初始化WinSock库,并创建客户端的套接字。
连接服务器:使用connect函数与指定的服务器建立连接。
登录:通过输入昵称进行登录。
界面初始化:设置控制台的窗口大小和清屏操作。
消息打印:使用printMsg函数打印接收到的消息。
编辑区域打印:使用editPrint函数在控制台指定位置打印输入的消息。
接收线程:创建一个线程用于接收服务器发送的消息,并在接收到消息时调用printMsg函数打印消息。
消息发送:在主循环中,监听键盘输入,并将输入的消息发送给服务器。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。