C++多线程实现群聊服务端和客户端
- 学习笔记
- 2025-11-05
- 18热度
- 0评论
服务端实现代码:
#include <iostream>
#include <windows.h>
#include <process.h>
#pragma comment(lib, "ws2_32.lib")
#define MAX_CLNT 256
#define MAX_BUF_SIZE 1024
using namespace std;
SOCKET clnSocks[MAX_CLNT]; // 所有的连接的客户端的socket
int clntCnt = 0; // 客户端连接的个数
HANDLE hMutex;
// 将收到的转发给客户端
void SendMsg(char *szMsg, int iLen) {
WaitForSingleObject(hMutex, INFINITE);
for (int i = 0; i < clntCnt; i++)
{
send(clnSocks[i], szMsg, iLen, 0);
}
ReleaseMutex(hMutex);
}
// 处理收发消息
unsigned WINAPI HandleCln(void* arg) {
// 此时连接的socket的标记
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;
}
}
cout << "此时连接的数目为:" << clntCnt << endl;
// 处理下线的过程
// 3 确定是哪一个连接下线 遍历
WaitForSingleObject(hMutex, INFINITE);
for (i = 0; i < clntCnt; i++) {
if (hClntSock == clnSocks[i]) {
// 已经找到了是哪个连接下线的 移除这个连接
while (i ++ < clntCnt)
{
clnSocks[i] = clnSocks[i + 1];
}
// 移除完毕
break;
}
}
// 移除之后总数减少1
clntCnt--;
cout << "断开此时的连接后总数是:"<< clntCnt << endl;
ReleaseMutex(hMutex);
closesocket(hClntSock);
return 0;
}
int main() {
cout << "This is Server!" << endl;
// 加载套节字库
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(6000);
// 3 分配电话号码
// 绑定套接字到本地IP地址
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
cout << "socket error code = " << GetLastError() << endl;
return -1;
}
// 4、监听 listen
if (SOCKET_ERROR == listen(sockSrv, 5))
{
cout << "listen error code = " << GetLastError() << endl;
return -1;
}
cout << "start listen" << endl;
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
while (1)
{
// 接收来自客户端的连接
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
WaitForSingleObject(hMutex, INFINITE);
// 每来一个连接,就会起一个线程去维护
clnSocks[clntCnt++] = sockConn;
ReleaseMutex(hMutex);
hThread = (HANDLE)_beginthreadex(NULL, 0, HandleCln, (void*)&sockConn, 0, NULL);
cout << "Client IP = "<< inet_ntoa(addrCli.sin_addr)<< " Client Num = " << clntCnt << endl;
}
closesocket(sockSrv);
WSACleanup();
return 0;
}
客户端实现代码:
// 客户端做的事情
// 1 请求上线
// 2 发消息 等待用户输入消息 发给服务端
// 3 客户端等待服务端的消息 收完数据后 输出到控制台
// 4 等待用户自己的关闭
#include <iostream>
#include <windows.h>
#include <process.h>
#pragma comment(lib, "ws2_32.lib")
#define NAME_SIZE 256
#define MAX_BUF_SIZE 1024
using namespace std;
char szName[NAME_SIZE] = "[DEFAULT]";
char szMsg[MAX_BUF_SIZE];
unsigned WINAPI SendMsg(void *arg) {
SOCKET hClntSock = *((SOCKET*)arg);
char szNameMsg[NAME_SIZE + MAX_BUF_SIZE]; // 又要有昵称 又要有消息
while (1) {
// 阻塞在这一句 等待用户输入
fgets(szMsg, MAX_BUF_SIZE, stdin);
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) {
SOCKET hClntSock = *((SOCKET*)arg);
char szNameMsg[NAME_SIZE + MAX_BUF_SIZE]; // 又要有昵称 又要有消息
int iLen = 0;
while (1) {
// 阻塞 等待来自服务器的消息
iLen = recv(hClntSock, szNameMsg, NAME_SIZE + MAX_BUF_SIZE - 1, 0);
// 服务器断开
if (iLen == -1) {
return -1;
}
// szNameMsg的0到iLen -1 都是收到的数据 iLen个
szNameMsg[iLen] = 0;
fputs(szNameMsg, stdout);
}
}
int main(int argc, char *argv[]) {
cout << "This is Client!" << endl;
if (argc != 2) {
cout << "必须输入两个参数" << endl;
cout << "例如:MyThreadClient.exe WXS" << endl;
system("pause");
return -1;
}
// 赋值昵称
sprintf(szName, "[%s]", argv[1]);
// 加载套节字库
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;
}
// 1 建立socket
hSock = socket(PF_INET, SOCK_STREAM, 0);
// 2 配置端口和地址
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
servAdr.sin_family = AF_INET;
servAdr.sin_port = htons(6000);
// 3 连接服务器
if (connect(hSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) {
cout << "connect error code = " << GetLastError() << endl;
return -1;
}
// 4 发送给服务端消息的线程
hSendThread = (HANDLE)_beginthreadex(NULL, 0, SendMsg, (void*)&hSock, 0, NULL);
hRecvThread = (HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void*)&hSock, 0, NULL);
// 等待 hSendThread hRecvThread 执行完毕
WaitForSingleObject(hSendThread, INFINITE);
WaitForSingleObject(hRecvThread, INFINITE);
// 关闭套节字
closesocket(hSock);
WSACleanup();
return 0;
}
