当前位置:网站首页>C语言实现QQ聊天室小项目 [完整源码]
C语言实现QQ聊天室小项目 [完整源码]
2022-07-05 09:55:00 【陈子青 :See】
聊天小项目用于练习Windows下的 tcp socket编程和线程同步,其中send 和 recv 使用,对tcp数据传输时经常遇到的中文乱码、数据残缺等问题有示范和纠正作用。
项目效果图
客户端代码
#include <WinSock2.h> #include <iostream> #include <windows.h> #include <process.h> #include<WS2tcpip.h> #pragma comment(lib, "ws2_32.lib") #define NAME_SIZE 32 #define BUF_SIZE 256 char szName[NAME_SIZE] = "[DEFAULT]"; char szMsg[BUF_SIZE]; //发送消息给服务端 unsigned WINAPI SendMsg(void* arg) { //1 接收传递过来的参数 SOCKET hClntSock = *((SOCKET*)arg); char szNameMsg[NAME_SIZE + BUF_SIZE]; //又有名字,又有消息 //循环接收来自于控制台的消息 while (1) { fgets(szMsg, BUF_SIZE, stdin); //阻塞在这一句 //退出机制 当收到q或Q 退出 if (!strcmp(szMsg, "Q\n") || !strcmp(szMsg, "q\n")) { closesocket(hClntSock); exit(0); } sprintf(szNameMsg, "%s %s", szName, szMsg);//字符串拼接 send(hClntSock, szNameMsg, strlen(szNameMsg), 0);//发送 } return 0; } //接收服务端的消息 unsigned WINAPI RecvMsg(void* arg) { //1 接收传递过来的参数 SOCKET hClntSock = *((SOCKET*)arg); char szNameMsg[NAME_SIZE + BUF_SIZE]; //又有名字,又有消息 int iLen = 0; while (1) { //recv阻塞 iLen = recv(hClntSock, szNameMsg, NAME_SIZE + BUF_SIZE - 1, 0); //服务端断开 if (iLen == -1) { return -1; } // szNameMsg的0到iLen -1 都是收到的数据 iLen个 szNameMsg[iLen] = 0; //接收到的数据输出到控制台 fputs(szNameMsg, stdout); } return 0; } int main(int argc,char *argv[]) { WORD wVersionRequested; WSADATA wsaData; int err; SOCKET hSock; SOCKADDR_IN servAdr; HANDLE hSendThread, hRecvThread; wVersionRequested = MAKEWORD(1, 1); // 初始化套接字库 err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return err; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return -1; } sprintf(szName, "[%s]", argv[1]); //1 建立socket hSock = socket(PF_INET, SOCK_STREAM, 0); // 2 配置端口和地址 memset(&servAdr, 0, sizeof(servAdr)); inet_pton(AF_INET, "192.168.10.3", &servAdr.sin_addr); //servAdr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); servAdr.sin_family = AF_INET; servAdr.sin_port = htons(9999); // 3 连接服务器 if (connect(hSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) { printf("connect error error code = %d\n", GetLastError()); return -1; } hSendThread = (HANDLE)_beginthreadex(NULL, 0, SendMsg, (void*)&hSock, 0, NULL); // 5 接收消息给服务端 安排一个工人 起一个线程接收消息 hRecvThread = (HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void*)&hSock, 0, NULL); //等待内核对象的信号发生变化 WaitForSingleObject(hSendThread, INFINITE); WaitForSingleObject(hRecvThread, INFINITE); // 6 关闭套接字 closesocket(hSock); WSACleanup(); return 0; }
服务端代码
#include<stdio.h> #include<windows.h> #include<process.h> #pragma comment(lib, "ws2_32.lib") #define MAX_CLNT 256 #define MAX_BUF_SIZE 256 SOCKET clntSocks[MAX_CLNT]; //所有的连接的客户端socket HANDLE hMutex; int clntCnt = 0; //当前连接的数目 void SendMsg(char* szMsg, int iLen) { int i = 0; WaitForSingleObject(hMutex, INFINITE); for (i = 0; i < clntCnt; i++) { send(clntSocks[i], szMsg, iLen, 0); } ReleaseMutex(hMutex); } unsigned WINAPI HandleCln(void* arg) { SOCKET hClntSock = *((SOCKET*)arg); int iLen = 0, i; char szMsg[MAX_BUF_SIZE] = { 0 }; while (1) { iLen = recv(hClntSock, szMsg, sizeof(szMsg), 0); if (iLen != -1) { //收到的数据立马发给所有的客户端 SendMsg(szMsg, iLen); } else { break; } } printf("此时连接数目为 %d\n", clntCnt); //3 某个连接断开,需要处理断开的连接 遍历 WaitForSingleObject(hMutex, INFINITE); for (i = 0; i < clntCnt; i++) { if (hClntSock == clntSocks[i]) { //移位 while (i++ < clntCnt) { clntSocks[i] = clntSocks[i + 1]; } break; } } clntCnt--; //当前连接数的一个自减 printf("断开此时连接数目 %d", clntCnt); ReleaseMutex(hMutex); closesocket(hClntSock); return 0; } int main() { // 加载套接字库 WORD wVersionRequested; WSADATA wsaData; int err; HANDLE hThread; wVersionRequested = MAKEWORD(1, 1); // 初始化套接字库 err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return err; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return -1; } //创建一个互斥对象 hMutex = CreateMutex(NULL, FALSE, NULL); // 新建套接字 SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(9999); // 绑定套接字到本地IP地址,端口号9190 if (bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) == SOCKET_ERROR) { printf("bind ERRORnum = %d\n", GetLastError()); return -1; } // 开始监听 if (listen(sockSrv, 5) == SOCKET_ERROR) { printf("listen ERRORnum = %d\n", GetLastError()); return -1; } printf("start listen\n"); SOCKADDR_IN addrCli; int len = sizeof(SOCKADDR); while (1) { // 接收客户连接 sockConn此时来的客户端连接 SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len); WaitForSingleObject(hMutex, INFINITE); clntSocks[clntCnt++] = sockConn; ReleaseMutex(hMutex); hThread = (HANDLE)_beginthreadex(NULL, 0, HandleCln, (void*)&sockConn, 0, NULL); printf("Connect client IP: %s \n", inet_ntoa(addrCli.sin_addr)); printf("Connect client num: %d \n", clntCnt); } closesocket(sockSrv); WSACleanup(); return 0; }
边栏推荐
- 【系统设计】指标监控和告警系统
- RMS TO EAP通过MQTT简单实现
- Universal double button or single button pop-up
- [NTIRE 2022]Residual Local Feature Network for Efficient Super-Resolution
- Uni app running to wechat development tool cannot Preview
- [system design] index monitoring and alarm system
- Activity jump encapsulation
- Usage differences between isempty and isblank
- Zblogphp breadcrumb navigation code
- . Net delay queue
猜你喜欢
mongoDB副本集
Is it really reliable for AI to make complex decisions for enterprises? Participate in the live broadcast, Dr. Stanford to share his choice | qubit · viewpoint
View Slide
学习笔记5--高精地图解决方案
WorkManager学习一
如何獲取GC(垃圾回收器)的STW(暫停)時間?
Meitu lost 300 million yuan in currency speculation for half a year. Huawei was exposed to expand its enrollment in Russia. Alphago's peers have made another breakthrough in chess. Today, more big new
Six simple cases of QT
Events and bubbles in the applet of "wechat applet - Basics"
How to write high-quality code?
随机推荐
Today in history: the first e-book came out; The inventor of magnetic stripe card was born; The pioneer of handheld computer was born
mongoDB副本集
程序员如何活成自己喜欢的模样?
The most complete is an I2C summary
RMS to EAP is simply implemented through mqtt
面试:List 如何根据对象的属性去重?
How did automated specification inspection software develop?
Lepton 无损压缩原理及性能分析
Excerpt from "sword comes" (VII)
横向滚动的RecycleView一屏显示五个半,低于五个平均分布
面试:Bitmap像素内存分配在堆内存还是在native中
Matrix processing practice
小程序中自定义行内左滑按钮,类似于qq和wx消息界面那种
ConstraintLayout官方提供圆角ImageFilterView
ByteDance Interviewer: how to calculate the memory size occupied by a picture
QT realizes signal transmission and reception between two windows
Wechat applet - simple diet recommendation (2)
Generics, generic defects and application scenarios that 90% of people don't understand
学习笔记6--卫星定位技术(上)
IDEA新建sprintboot项目