当前位置:网站首页>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; }
边栏推荐
猜你喜欢

Wechat applet - simple diet recommendation (3)

驱动制造业产业升级新思路的领域知识网络,什么来头?

MySQL character type learning notes

Advanced opencv:bgr pixel intensity map

RMS TO EAP通过MQTT简单实现

AtCoder Beginner Contest 258「ABCDEFG」

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

Kotlin compose and native nesting

一个程序员的职业生涯到底该怎么规划?

How to write high-quality code?
随机推荐
Fluent generates icon prompt logo widget
Apple 5g chip research and development failure? It's too early to get rid of Qualcomm
A large number of virtual anchors in station B were collectively forced to refund: revenue evaporated, but they still owe station B; Jobs was posthumously awarded the U.S. presidential medal of freedo
学习笔记5--高精地图解决方案
[C language] the use of dynamic memory development "malloc"
AtCoder Beginner Contest 258「ABCDEFG」
How to judge that the thread pool has completed all tasks?
RMS TO EAP通过MQTT简单实现
Matrix processing practice
. Net delay queue
Unity粒子特效系列-毒液喷射预制体做好了,unitypackage包直接用 - 上
苹果 5G 芯片研发失败?想要摆脱高通为时过早
The Alipay in place function can't be found, and the Alipay in place function is offline
StaticLayout的使用详解
QT realizes signal transmission and reception between two windows
WorkManager学习一
Wechat applet - simple diet recommendation (2)
Swift saves an array of class objects with userdefaults and nssecurecoding
Jupiter notebook shortcut key
B站大量虚拟主播被集体强制退款:收入蒸发,还倒欠B站;乔布斯被追授美国总统自由勋章;Grafana 9 发布|极客头条...