项目涉及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函数打印消息。
消息发送:在主循环中,监听键盘输入,并将输入的消息发送给服务器。