当前位置:网站首页>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; }
边栏推荐
- ConstraintLayout的流式布局Flow
- 能源势动:电力行业的碳中和该如何实现?
- StaticLayout的使用详解
- B站大量虚拟主播被集体强制退款:收入蒸发,还倒欠B站;乔布斯被追授美国总统自由勋章;Grafana 9 发布|极客头条...
- TypeError: Cannot read properties of undefined (reading ‘cancelToken‘)
- 驱动制造业产业升级新思路的领域知识网络,什么来头?
- Those who are good at using soldiers, hide in the invisible, and explain the best promotional value works in depth in 90 minutes
- . Net delay queue
- How to get the STW (pause) time of GC (garbage collector)?
- [NTIRE 2022]Residual Local Feature Network for Efficient Super-Resolution
猜你喜欢

QT event filter simple case

Hard core, have you ever seen robots play "escape from the secret room"? (code attached)

【系统设计】指标监控和告警系统

The king of pirated Dall · e? 50000 images per day, crowded hugging face server, and openai ordered to change its name

WorkManager的学习二

Have you learned to make money in Dingding, enterprise micro and Feishu?

> Could not create task ‘:app:MyTest.main()‘. > SourceSet with name ‘main‘ not found.问题修复

Usage differences between isempty and isblank

isEmpty 和 isBlank 的用法区别

> Could not create task ‘:app:MyTest. main()‘. > SourceSet with name ‘main‘ not found. Problem repair
随机推荐
Apple 5g chip research and development failure? It's too early to get rid of Qualcomm
Tianlong Babu TLBB series - single skill group injury
Universal double button or single button pop-up
Tianlong Babu TLBB series - about items dropped from packages
Pagoda panel MySQL cannot be started
《微信小程序-基础篇》小程序中的事件与冒泡
自动化规范检查软件如何发展而来?
What is the origin of the domain knowledge network that drives the new idea of manufacturing industry upgrading?
uniapp + uniCloud+unipay 实现微信小程序支付功能
报错:Module not found: Error: Can‘t resolve ‘XXX‘ in ‘XXXX‘
QT realizes signal transmission and reception between two windows
WorkManager的学习二
QT event filter simple case
Node red series (29): use slider and chart nodes to realize double broken line time series diagram
Design of stepping motor controller based on single chip microcomputer (forward rotation and reverse rotation indicator gear)
Uni app running to wechat development tool cannot Preview
Glide Mastery
What is the most suitable book for programmers to engage in open source?
伪类元素--before和after
高级 OpenCV:BGR 像素强度图