服务器端

//多线程 聊天室 服务器
//1.对于每一个上线的客户端 起一个单独的thread维护
//2.将收到的消息转发给全部客户端
//3.当某个客户端断开下线,处理断开链接
#include <iostream>
#include <stdio.h>
#include <process.h>
#include <windows.h>

#define MAX_CLIENT 256
#define MAX_BUFFER_SIZE 1024

#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable : 4996)

//函数声明
int Init_SOCKNET();
void ErrorHanding(const char* msg);
unsigned WINAPI HandleClient(void* arg);

SOCKET client_sockets[MAX_CLIENT];//连接的客户端的 socket
int client_count{ 0 };//客户端连接的数量

HANDLE hMutex;//互斥锁

int main() {
	//初始化套接字网络库
	int result = Init_SOCKNET();
	if (result != 0) {
		printf("Failed to initialize network library.\n");
		return result;
	}
	//创建一个互斥对象
	hMutex = CreateMutex(NULL,FALSE,NULL);
	//线程句柄
	HANDLE hThread;

	printf("This is Server\n");
	//创建服务器套接字
	SOCKET socket_server = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == socket_server) {
		ErrorHanding("socket error");
	}
	SOCKADDR_IN addr_server;
	addr_server.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//0.0.0.0
	addr_server.sin_family = AF_INET;
	addr_server.sin_port = htons(2233);

	char msg[MAX_BUFFER_SIZE] = { 0 };
	if (SOCKET_ERROR == bind(socket_server, (SOCKADDR*)&addr_server, sizeof(addr_server)))
		ErrorHanding("bind error");
	if (SOCKET_ERROR == listen(socket_server, 5)) 
		ErrorHanding("listen error");

	puts("listen done");

	SOCKADDR_IN addr_client;
	int len = sizeof(addr_client);
	SOCKET socket_client;
	int strLen{ 0 };
	//接收客户端的连接
	while (1)
	{
		SOCKET sockConn = accept(socket_server, (SOCKADDR*)&addr_client, &len);
		//每来一个连接,启动一个线程去维护连接
		WaitForSingleObject(hMutex, INFINITE);//加锁
		client_sockets[client_count++] = sockConn;
		ReleaseMutex(hMutex); //解锁
		hThread = (HANDLE)_beginthreadex(NULL,0,HandleClient,(void*)&sockConn,0,NULL);
		printf("client IP=%s num:%d\n", inet_ntoa(addr_client.sin_addr),client_count);

	}
	closesocket(socket_server);
	return 0;
}

void sendMSG(char* szMsg,int iLen) {
	int i = 0;
	WaitForSingleObject(hMutex, INFINITE);//加锁
	for (const auto a : client_sockets) {
		send(a, szMsg, iLen, 0);
	}
	ReleaseMutex(hMutex); //解锁
}

//thread functions
unsigned WINAPI HandleClient(void* arg) {
	SOCKET hClientSock = *((SOCKET*)arg);
	int iLen{ 0 };
	char szMsg[MAX_BUFFER_SIZE] = { 0 };
	while (1) {
		iLen = recv(hClientSock,szMsg,sizeof(szMsg),0);
		if (iLen > 0) {
			//将收到的消息转发给客户端
			sendMSG(szMsg,iLen);
		}
		else if(iLen == -1){
			break;
		}
	}
	printf("连接数目:%d\n",client_count);

	//处理下线
	WaitForSingleObject(hMutex, INFINITE);//加锁
	for (int i = 0; i < client_count; i++) {
		if (hClientSock == client_sockets[i]) {
			while ((i+1) < client_count) {
				client_sockets[i++] = client_sockets[i + 1];
			}
			break;
		}
	}
	client_count--;
	ReleaseMutex(hMutex); //解锁

	printf("断开后连接数目:%d\n",client_count);
	closesocket(hClientSock);
	return 0;
}

// 初始化网络库函数
int Init_SOCKNET() {
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	// 请求使用的 Winsock 版本为 2.2
	wVersionRequested = MAKEWORD(2, 2);

	// 初始化 Winsock 库
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		// 如果初始化失败,输出错误信息并返回错误码
		printf("WSAStartup failed with error: %d\n", err);
		return err;
	}

	// 检查 Winsock 库是否支持 2.2 版本
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
		// 如果版本不匹配,输出错误信息并清理 Winsock 库
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return -1;
	}

	// 初始化成功,返回 0
	printf("Winsock 2.2 initialized successfully.\n");
	return 0;
}


void ErrorHanding(const char* msg) {
	fputs(msg, stderr);
	fputc('\n', stderr);
	system("pause");
	exit(1);
}

客户端

/**
 * 客户端:
 * 1.向服务器请求连接
 * 2.发送消息&客户端等待服务端消息
 * 3.等待用户自己下线客户端
 */
#include <iostream>
#include <stdio.h>
#include <process.h>
#include <windows.h>

#define MAX_NAME_SIZE 256
#define MAX_BUFFER_SIZE 1024

#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable : 4996)

 //函数声明
int Init_SOCKNET();
void ErrorHanding(const char* msg);

char szName[MAX_NAME_SIZE]{"DEFAULT"};//默认昵称
char szMsg[MAX_BUFFER_SIZE]{""};//收发数据容器

unsigned WINAPI sendMsg(void* arg) {

	SOCKET hClientSock = *((SOCKET*)arg);
	char szNameMsg[MAX_BUFFER_SIZE+MAX_NAME_SIZE] = { 0 };//昵称+消息

	while (1) {
		memset(szMsg, 0, MAX_BUFFER_SIZE);
		fgets(szMsg, MAX_BUFFER_SIZE, stdin);//阻塞在这里,等待控制台消息
		if ((!strcmp(szMsg, "q\n")) || (!strcmp(szMsg, "Q\n"))) {//处理下线
			closesocket(hClientSock);
			exit(0);
		}
		//拿到消息发送给服务器
		sprintf(szNameMsg, "%s:%s", szName, szMsg);
		send(hClientSock, szNameMsg, strlen(szNameMsg), 0);
	}
}

unsigned WINAPI recvMsg(void* arg) {
	SOCKET hClientSock = *((SOCKET*)arg);
	char szNameMsg[MAX_BUFFER_SIZE + MAX_NAME_SIZE] = { 0 };//昵称+消息
	int iLen{ 0 };
	while (1) {
		memset(szMsg, 0, MAX_BUFFER_SIZE);
		//等待来自服务端的消息
		iLen = recv(hClientSock, szNameMsg, MAX_BUFFER_SIZE, 0);
		if (iLen == -1) {
			return 2;
		}
		szNameMsg[iLen] = 0;
		fputs(szNameMsg,stdout);
	}

	return 0;
}


int main(int argc,char* argv[]) {
	if (argc != 2) {
		puts("请输入昵称");
		puts("程序当前目录 使用shell运行程序!");
		puts("例如:MyThreadClient.exe 王xx");
		system("pause");
		return -1;
	}
	sprintf(szName, "%s", argv[1]);
	//初始化套接字网络库
	int result = Init_SOCKNET();
	if (result != 0) {
		printf("Failed to initialize network library.\n");
		return result;
	}
	puts("客户端网络库初始化完成!");
	//客户端的发送和接收分别用两个线程维护
	//等待用户从控制台的输入然后发送给服务端
	HANDLE hSendThread, hRecvThread;
	SOCKADDR_IN addr_server;
	addr_server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addr_server.sin_family = AF_INET;
	addr_server.sin_port = htons(2233);

	SOCKET hSock = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == hSock) {
		ErrorHanding("socket error");
	}

	if (SOCKET_ERROR == connect(hSock, (SOCKADDR*)&addr_server, sizeof(addr_server))) {
		ErrorHanding("connect error");
	}
	//发送给服务端消息 安排一个线程维护
	hSendThread = (HANDLE)_beginthreadex(NULL,0,&sendMsg,(void*)&hSock,0,NULL);
	hRecvThread = (HANDLE)_beginthreadex(NULL,0,&recvMsg,(void*)&hSock,0,NULL);

	//等待线程的内核对象发送信号;
	WaitForSingleObject(hSendThread,INFINITE);
	WaitForSingleObject(hRecvThread,INFINITE);

	closesocket(hSock);

	return 0;
}

// 初始化网络库函数
int Init_SOCKNET() {
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	// 请求使用的 Winsock 版本为 2.2
	wVersionRequested = MAKEWORD(2, 2);

	// 初始化 Winsock 库
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		// 如果初始化失败,输出错误信息并返回错误码
		printf("WSAStartup failed with error: %d\n", err);
		return err;
	}

	// 检查 Winsock 库是否支持 2.2 版本
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
		// 如果版本不匹配,输出错误信息并清理 Winsock 库
		printf("Could not find a usable version of Winsock.dll\n");
		WSACleanup();
		return -1;
	}

	// 初始化成功,返回 0
	printf("Winsock 2.2 initialized successfully.\n");
	return 0;
}


void ErrorHanding(const char* msg) {
	fputs(msg, stderr);
	fputc('\n', stderr);
	system("pause");
	exit(1);
}