基于控制台实现的简易聊天室,详细实现见代码~
main.cpp(主程序)
#include <iostream>
#include<string>
#include "vol.h"
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
if(!init()){
cout<<"初始化失败"<<'\n';
return -1;
}
Client_connect();
return 0;
}
vol.h(封装函数列表)
#include<iostream>
using namespace std;
bool init(); //网络初始化
bool Client_connect(); //客户端连接
void GBKToUTF8(string& strGBK); //GBK转UTF-8
string UTF8ToGBK(const char* strUTF8){ //UTF-8转GBK
void login(); //客户端登录
void uiInit(); //初始化聊天室界面
void gotoxy(int x,int y); //移动光标
//DWORD WINAPI threadFuncRecv(LPCVOID pram); //接收信息线程
func.cpp(函数实现)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <stdio.h>
#include <cstring>
#include <string>
#include <conio.h>
#include "vol.h"
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define SERVER_IP "118.126.117.125"
#define GROUP_CHAT_PORT 2022
using namespace std;
SOCKET serverSocket; //网络套接字
sockaddr_in sockAddr ; //网络地址
string nickName; //昵称
string line1; //一行下划线
string line2; //一行空白字符串
HANDLE hMutex; //互斥锁
void GBKToUTF8(string& strGBK){ //GBK转UTF-8
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];
WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
strGBK = szUtf8;
delete[] szUtf8;
delete[] wszUtf8;
}
string UTF8ToGBK(const char* strUTF8){ //UTF-8转GBK
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;
}
bool init(){
// 1.网络服务的初始化
WSADATA data;
int ret=WSAStartup(MAKEWORD(1, 1), &data);
if(ret!=0)
{
cout<<"连接服务器失败,请检查网络连接!"<<'\n';
return 0;
}
// 2.创建套接字
serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
// 3.物理地址
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
sockAddr.sin_port = htons(GROUP_CHAT_PORT);
//创建互斥锁
hMutex = CreateMutex(0, 0, "console");
return 1;
}
void login(){
system("mode con lines=5 cols=30\n");
cout<<" 欢迎进入翎羽寒星的聊天室"<<"\n\n";
cout<<" 昵称:";
cin>>nickName;
cout<<endl;
GBKToUTF8(nickName);
send(serverSocket, nickName.c_str(), nickName.length()+1, 0);
}
void gotoxy(int x,int y){
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //获取光标
COORD pos;
pos.X=x,pos.Y=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+="-";
cout<<line1<<endl;
for(int i=0;i<110;i++)line2+=" ";
}
int cnt=0;
void printMsg(const char* msg){ // 打印接收到的信息
//上锁(申请互斥锁)
//INFINITE,表示如果没有申请到资源,就一直等待,直到等到为止!
WaitForSingleObject(hMutex, INFINITE);
static POINT pos = {0, 0}; //static是静态变量,只有第一次会被赋值,函数结束后不会消失
gotoxy(pos.x,pos.y);
srand(time(0));
int col=rand()%5+31;
printf("\033[0;%d;40m%s\033[0m\n", col, msg); //显示颜色字符串
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){ //实现滚动效果
cout<<line2<<endl;
cout<<endl<<endl;
gotoxy(0, 33);
cout<<line1<<endl;
pos.y-=1;
}
gotoxy(1,34);
//释放锁
ReleaseMutex(hMutex);
}
void editPrint(int col,char ch){ //编辑区字符打印
WaitForSingleObject(hMutex, INFINITE);
gotoxy(col,34);
cout<<ch;
ReleaseMutex(hMutex);
}
void editPrint(int col,string str){ //编辑区字符串打印
WaitForSingleObject(hMutex, INFINITE);
gotoxy(col,34);
cout<<str;
ReleaseMutex(hMutex);
}
DWORD WINAPI threadFuncRecv(LPVOID pram){ //创建接收信息线程
char buff[4096];
while(1){
int ret = recv(serverSocket, buff, sizeof(buff), 0);
if(ret<=0){
cout<<"服务器关闭或故障!"<<endl;
break;
}
// 打印接收到的信息
// to do.
printMsg(UTF8ToGBK(buff).c_str());
}
return 0;
}
bool is_HZ(char str[], int index){
//一个汉字两个字节 第一个字节<0 第二个字节 <0 or >0
//一个英文字符, 只有一个字节, >0
int i=0;
while(i<index){
if(str[i]>0){
i++;
}else{
i+=2;
}
}
return i>index;
}
bool Client_connect(){
int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr));
if(ret!=0){
cout<<"连接服务器失败,请检查网络连接!"<<'\n';
return -1;
}
login();
uiInit(); //初始化聊天室界面
HANDLE hTread = CreateThread(0, 0, threadFuncRecv, 0, 0, 0);
CloseHandle(hTread);
//编辑信息
while(1){
char buff[1024];
memset(buff, 0, sizeof(buff));
editPrint(0 , '>');
int len=0;
while(1){
if(_kbhit()){ //判断有无按键输入
char ch = getch(); //换行也能读入
if(ch == '\r')break; //按下了回车键
else if(ch == 8){
if(is_HZ(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{
cout<<ch;
buff[len++] = ch;
} while(_kbhit()&&(ch=getch())); //连续读入,解决中文输入问题
ReleaseMutex(hMutex);
}
}
if(len == 0)continue;
//清除编辑区的信息
editPrint(0, line2);
//把用户自己的话,输出到聊天室
char buff2[1024];
memset(buff2, 0, sizeof(buff2));
//char数组转字符串
string ss = "[LocalHost@"+UTF8ToGBK(nickName.c_str())+"]"+string(&buff[0],&buff[len]);
//字符串转char数组
strncpy(buff2, ss.c_str(), ss.length());
printMsg(buff2);
//发送编辑好的信息
send(serverSocket, buff, strlen(buff)+1, 0);
}
// 编辑信息
// to do.
return 1;
}
完美实现~,使用devc++中文显示有点儿小bug,欢迎大佬帮忙改进~