C语言制作-QQ聊天室

创建项目
创建空项目,导入素材(可选)。
2. 网络初始化
#include <stdio.h> #include <winsock2.h> #pragma comment (lib, "WS2_32.lib") sockaddr_in sockAddr; // 网络地址 SOCKET serverSocket; // 网络套接字 #define SERVER_IP "118.126.117.125" #define JIAN_KONG_PORT 2022 bool init() { // windows 网络服务的初始化 WSADATA wsaData; int err = WSAStartup(MAKEWORD(1, 1), &wsaData); if (err != 0) { return false; }// 创建套接字 serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
// 设置服务器端的网络地址 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP); sockAddr.sin_port = htons(JIAN_KONG_PORT); return true; }int main(void) { if (!init()) { printf("初始化失败!\n"); return -1; }return 0; }
3. 连接到聊天室服务器
// 连接服务器(发起网络连接) int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)); if (ret != 0) { printf("连接服务器失败,请检查网络连接!"); return 1; }
4. 登录聊天室
char nickName[16]; //昵称 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 login() { system("mode con lines=5 cols=30"); printf("\n 欢迎使用奇牛 QQ 聊天室\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); }
5. 创建聊天室界面
char line1[111]; char line2[111]; void gotoxy(short x, short y) { COORD pos = { x,y }; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 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("play res/bg.mp3", 0, 0, 0); }int main(void) {
...... login(); uiInit(); getchar(); return 0; }
6. 创建接收消息的线程
线程的概念
代码实现
int main(void) { if (!init()) { printf("初始化失败!\n"); return -1; }// 连接服务器(发起网络连接) int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)); if (ret != 0) { printf("连接服务器失败,请检查网络连接!"); return 1; }login(); uiInit(); // 创建接收线程 HANDLE hThread = CreateThread(NULL, 0, ThreadFunRecv, 0, 0, 0); CloseHandle(hThread); //仅关闭线程句柄,对应的线程没有影响 getchar(); return 0; }
线程函数:
DWORD WINAPI ThreadFunRecv(LPVOID param) { char buff[4096];
while (1) { int ret = recv(serverSocket, buff, sizeof(buff), 0); if (ret <= 0) { printf("服务器关闭!\n"); break; }// 打印收到的消息 // to do. }return NULL; }
7. 群聊区的消息打印
多线程的互斥
多线程对共享资源的互斥使用。
共享资源:控制台的光标位置
互斥锁的创建
HANDLE hMutex; //互斥锁(用于线程之间的互斥) bool init() { // windows 网络服务的初始化 WSADATA wsaData; int err = WSAStartup(MAKEWORD(1, 1), &wsaData); if (err != 0) { return false; }// 创建套接字 serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // 设置服务器端的网络地址 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP); sockAddr.sin_port = htons(JIAN_KONG_PORT);
// 创建互斥锁 hMutex = CreateMutex(NULL, FALSE, "console"); //参数 3 表示这个互斥锁的名字 return true; }
使用互斥锁来控制聊天信息的输出
void printMsg(const char* msg) { // 申请互斥锁, INFINITE, 表示如果没有申请到资源就一直等待该资源 WaitForSingleObject(hMutex, INFINITE); // 使用 static 变量表示聊天区域的打印位置 static POINT pos = { 0,0 }; gotoxy(pos.x, pos.y); printf("%s\n", msg); // 获取光标的当前位置(第几行第几列) CONSOLE_SCREEN_BUFFER_INFO bInfo;; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hOut, &bInfo); pos.x = bInfo.dwCursorPosition.X; pos.y = bInfo.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); }
在接收线程中输出信息
DWORD WINAPI ThreadFunRecv(LPVOID param) { 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; }
编码转换
std::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; }
8. 在主线程中实现编辑功能
int main(void) { if (!init()) { printf("初始化失败!\n"); return -1; }// 连接服务器(发起网络连接)
int ret = connect(serverSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)); if (ret != 0) { printf("连接服务器失败,请检查网络连接!"); return 1; }login(); uiInit(); // 创建接收线程 HANDLE hThread = CreateThread(NULL, 0, ThreadFunRecv, 0, 0, 0); CloseHandle(hThread); //仅关闭线程句柄,对应的线程没有影响 while (1) { char buff[1024]; // 存储编辑信息 memset(buff, 0, sizeof(buff)); editPrint(0, '>'); int len = 0; while (1) { if (_kbhit()) { char c = getch(); if (c == '\r') { //使用 getch 读取时,按回车键,返回的是 '\r' break; }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; }
待完善
实现编辑区的退格删除功能
实现中文输入功能
聊天区多种彩色文本功能
服务器端功能

今天的分享就到这里了,大家要好好学C语言/C++哟~

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!
对于准备学习C/C++编程的小伙伴,如果你想更好的提升你的编程核心能力(内功)不妨从现在开始!
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)加君羊获取哦~
C语言C++编程学习交流圈子,企鹅君羊:

【点击进入】


C语言从入门到精通(C语言入门C语言教程C语言零基础C语言基础C语言学习C

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值