服务器端
//多线程 聊天室 服务器
//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);
}