基于TCP/IP协议的网络通信实例——公共聊天室

功能说明:服务端设定客户端连接个数,达到上限不可用。客户端可以单独同服务端进行通信交流。
在这里插入图片描述

一、服务端代码:MyServer.cpp

// MyServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

#include<stdlib.h>
#include<winsock2.h>//引用头文件
#pragma comment(lib,"ws2_32.lib")//引用库文件

//最大连接数
#define g_MaxConnect  20
int g_Connect = 0;

struct sock_params
{
    
    
	SOCKET hsock;
	int nSockIndex;
};

//线程实现函数
DWORD WINAPI threadpro(LPVOID pParam)
{
    
    
	sock_params* sockPam = (sock_params*)pParam;
	SOCKET hsock = sockPam->hsock;
	int nSockIndex = sockPam->nSockIndex;

	char aIndex[4];
	_itoa_s(nSockIndex, aIndex, 10);

	char buffer[1024];
	char sendBuffer[1024];
	if(hsock != INVALID_SOCKET)
	{
    
    
		cout<<"客户端 "<< nSockIndex << " 加入服务器!" <<endl;
	}
		
	while(1)
	{
    
    
		//循环接收发送的内容
		int num = recv(hsock,buffer,1024,0);//阻塞函数,等待接收内容

		if(num > 0)
		{
    
    
			cout << "客户端 "<< nSockIndex << ": "<< buffer<<endl;

			memset(sendBuffer,0,1024);

			char strValue[30] = "服务器 ";
			strcat_s(strValue, aIndex);
			strcpy_s(sendBuffer,strValue);

			char strSpace[30] = " 收到数据如下: ";
			strcat_s(sendBuffer,strSpace);
			strcat_s(sendBuffer,buffer);

			int ires = send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息

			//cout<<"Send to Client: "<<sendBuffer<<endl;
		}
		else
		{
    
    
			cout<<"客户端 "<< nSockIndex <<" 关闭!" <<endl;
			//cout<<"Server Process " << nSockIndex << " Closed"<<endl;
			return 0;
		}
	}

	return 0;
}

//主函数
void main()
{
    
    
	WSADATA wsd;//定义WSADATA对象
	WSAStartup(MAKEWORD(2,2),&wsd);
	SOCKET m_SockServer;
	sockaddr_in serveraddr;

	sockaddr_in serveraddrfrom;
	SOCKET m_Server[g_MaxConnect];

	serveraddr.sin_family = AF_INET;//设置服务器地址
	serveraddr.sin_port = htons(4600);//设置端口号
	serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	m_SockServer = socket(AF_INET,SOCK_STREAM,0);
	int nStatus = bind(m_SockServer,(sockaddr*)&serveraddr,sizeof(serveraddr));
	if (nStatus == 0)
	{
    
    
		cout << "服务端启动成功!" <<endl;
	}
	else
	{
    
    
		cout << "服务端启动失败!" <<endl;
		return;
	}

	int iLisRet = 0;
	int len = sizeof(sockaddr);
	while(1)
	{
    
    
		iLisRet = listen(m_SockServer,0);//进行监听
		m_Server[g_Connect] = accept(m_SockServer,(sockaddr*)&serveraddrfrom,&len);

		//同意连接
		if(m_Server[g_Connect] != INVALID_SOCKET)
		{
    
    
			if(g_Connect > g_MaxConnect-1)
			{
    
    
				char WarnBuf[50]="客户端连接个数:超限!";
				int ires = send(m_Server[g_Connect],WarnBuf,sizeof(WarnBuf),0);
			}
			else
			{
    
    
				char cIndex[4];
				_itoa_s(g_Connect, cIndex, 10);
				char buf[50]="你的服务端ID: ";
				strcat_s(buf, cIndex);

				int ires = send(m_Server[g_Connect],buf,sizeof(buf),0);//发送字符过去
				//cout << buf <<endl;

				HANDLE m_Handel;	//线程句柄
				DWORD nThreadId=0;	//线程ID

				sock_params sockPam;
				sockPam.hsock = m_Server[g_Connect];
				sockPam.nSockIndex = g_Connect;

				m_Handel = (HANDLE)::CreateThread(NULL,0,threadpro,&sockPam,0,&nThreadId);
				CloseHandle(m_Handel);
			}

			++g_Connect;
		}
	}

	WSACleanup();
}

二、客户端:MyClient.cpp

// MyClient.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>

using namespace std;

#include<stdlib.h>
#include<stdio.h>
#include"winsock2.h"
#include<time.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
    
    
	WSADATA wsd;//定义WSADATA对象
	WSAStartup(MAKEWORD(2,2),&wsd);
	SOCKET m_SockClient;
	sockaddr_in clientaddr;

	clientaddr.sin_family = AF_INET;	//设置服务器地址
	clientaddr.sin_port = htons(4600);  //设置服务器端口号
	clientaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	m_SockClient = socket(AF_INET,SOCK_STREAM,0);
	
	if (m_SockClient == INVALID_SOCKET)
	{
    
    
		printf("Sock 初始化失败: %d\n", WSAGetLastError());
		WSACleanup();
		return ;
	}
	// 获取发送缓冲区和接送缓冲区大小
	{
    
    
		int optlen = 0;
		int optval = 0;

		optlen = sizeof(optval);
		getsockopt(m_SockClient, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
		printf("send buf len is %d\n",optval);           //64位 默认发送缓冲区64k
		getsockopt(m_SockClient, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
		printf("Recv buf len is %d\n",optval);          //64位 默认接收缓冲区64k
	}
	// 设定发送缓冲区大小
	// optval = 1024 * 2;  
    // setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen); 
    
	int nSuccess = connect(m_SockClient,(sockaddr*)&clientaddr,sizeof(clientaddr));//连接超时
	if (nSuccess == 0)
	{
    
    
		cout<<"连接服务器成功!"<<endl;
	}
	else
	{
    
    
		cout<<"连接服务器失败!"<<endl;
		return;
	}

	char buffer[1024];
	char inBuf[1024];
	int num = 0;
	num = recv(m_SockClient,buffer,1024,0);//阻塞
	if(num > 0)
	{
    
    
		cout << /*"Receive from server:"<<*/ buffer<<endl;

		char* pResult = strstr(buffer, "超限");
		if (pResult != NULL)
		{
    
    
			cout<<"服务器连接个数超限,不可用!"<<endl;
			system("pause"); 
			return;
		}

		while(1)
		{
    
    
			cout<<"请输入要发送的消息:"<<endl;
			cin >> inBuf;

			if(strcmp(inBuf,"exit") == 0)
			{
    
    
				send(m_SockClient,inBuf,sizeof(inBuf),0);//发送退出指令
				return;
			}

			int send_len = send(m_SockClient,inBuf,sizeof(inBuf),0);
			if (send_len < 0) 
			{
    
    
				cout << "发送失败!请检查服务器是否开启!" << endl;
				system("pause"); 
				return;
			}

			int recv_len = recv(m_SockClient,buffer,1024,0);//接收客户端发送过来的数据
			if(recv_len>=0)
			{
    
    
				cout<< buffer <<endl;
			}
		}
	}
}

资源文件:https://download.csdn.net/download/m0_37251750/36164589

三、 额外代码:加入线程锁或者识别消息类型

(可以跳过)

// 线程锁
class CAutoLock
{
    
    
public:
	CAutoLock();
	~CAutoLock();

	void Lock();
	void UnLock();

private:
	CRITICAL_SECTION m_Section;
};
#endif

CAutoLock::CAutoLock()
{
    
    
	InitializeCriticalSection(&m_Section);
}

CAutoLock::~CAutoLock()
{
    
    
	DeleteCriticalSection(&m_Section);
}

void CAutoLock::Lock()
{
    
    
	EnterCriticalSection(&m_Section);
}

void CAutoLock::UnLock()
{
    
    
	LeaveCriticalSection(&m_Section);
}


#pragma once
#define INVALID_MSG			0	// 无效的消息
#define MSG_FOLDER_INFO		1	// 文件目录信息消息
#define MSG_CREATE_FOLDER	2	// 创建文件目录消息
#define MSG_DELETE_FOLDER	3	// 删除文件目录消息
#define MSG_RENAME_FOLDER	4	// 重命名文件目录消息

class Message
{
    
    
public:
	struct MsgHead     //头消息
	{
    
    
		int msgId;    //消息标识
		MsgHead(int msg = INVALID_MSG) :msgId(msg) {
    
    };
	};
	struct MsgFolderInfo :public MsgHead
	{
    
    
		char chProjCode[_MAX_FNAME];
		MsgFolderInfo() :MsgHead(MSG_FOLDER_INFO) {
    
    }
	};
	struct MsgCreateFolder :public MsgHead
	{
    
    
		char chFolder[_MAX_DIR];
		MsgCreateFolder() :MsgHead(MSG_CREATE_FOLDER) {
    
    }
	};
	struct MsgDeleteFolder :public MsgHead
	{
    
    
		char chFolder[_MAX_DIR];
		MsgDeleteFolder() :MsgHead(MSG_DELETE_FOLDER) {
    
    }
	};
	struct MsgRenameFolder :public MsgHead
	{
    
    
		char chOldFolder[_MAX_DIR];
		char chNewFolder[_MAX_DIR];
		MsgRenameFolder() :MsgHead(MSG_RENAME_FOLDER) {
    
    }
	};
};

// 使用
CAutoLock g_lock;

bool ProcessConnection(SOCKET clientSocket)
{
    
    
	int nLen = 0;
	int nSize = 0;
	nLen = recv(clientSocket, (char*)&nSize, sizeof(int), 0);
	if (nLen <= 0)
	{
    
    
		hcDebugLog(_T("ZLY:接收Msg长度失败 \n"));
		cout << "接收Msg长度失败" << WSAGetLastError() << endl;
		return false;
	}

	char buff[MAX_PACK_SIZE];
	memset(buff, 0x00, sizeof(char)*MAX_PACK_SIZE);
	nLen = recv(clientSocket, buff, nSize, 0);
	if (nLen <= 0)
	{
    
    
		if (SOCKET_ERROR == nLen)
		{
    
    
			hcDebugLog(_T("ZLY:接收Msg数据失败 \n"));
			cout << "接收Msg数据失败" << WSAGetLastError() << endl;
		}
		else
		{
    
    
			hcDebugLog(_T("ZLY:客户端关闭连接 \n"));
			cout << "客户端关闭连接" << endl;
		}
		return false;
	}

	Message::MsgHead* msgHead;
	msgHead = (Message::MsgHead*)&buff;
	switch(msgHead->msgId)
	{
    
    
	case MSG_FOLDER_INFO:
		{
    
    }
		break;
	case MSG_CREATE_FOLDER:
		{
    
    }
		break;
	default:
		{
    
    
			cout<<"无效消息"<<endl;
			return false;
		}
	}

	return true;
}

DWORD WINAPI CreateClientThread(LPVOID pData)
{
    
    
	SocketData* pSocketData = (SocketData*)pData;
	if (pSocketData == NULL)
	{
    
    
		HY_SAFE_DELETE(pSocketData);
		return -1;
	}

	// 循环监听客户端发送消息
	while (pSocketData->serverSocket.ProcessConnection(pSocketData->clientSocket))
	{
    
    
	}

	// 处理客户端非正常关闭的情况
	g_lock.Lock();
	for (auto iter = g_vecProjCode.begin(); iter != g_vecProjCode.end(); iter++)
	{
    
    
		if (pSocketData->clientSocket == iter->socket)
		{
    
    
			g_vecProjCode.erase(iter);
			break;
		}
	}

	if (g_vecProjCode.size() == 0)
	{
    
    
		// 关闭套接字
		pSocketData->serverSocket.CloseSocket();
		exit(0);
	}
	g_lock.UnLock();

	HY_SAFE_DELETE(pSocketData);
	return 0;
}
bool MessageUtils::SendMsg(SOCKET socket, Message::MsgHead* pMsg)
{
    
    
	int nSize = 0;
	switch (pMsg->msgId)
	{
    
    
	case MSG_FOLDER_INFO:
		nSize = sizeof(Message::MsgFolderInfo);
		break;
	case MSG_CREATE_FOLDER:
		nSize = sizeof(Message::MsgCreateFolder);
		break;
	case MSG_DELETE_FOLDER:
		nSize = sizeof(Message::MsgDeleteFolder);
		break;
	case MSG_RENAME_FOLDER:
		nSize = sizeof(Message::MsgRenameFolder);
		break;
	
	default:
		return false;
	}

	// 发送消息长度
	if (sizeof(nSize) != send(socket, (char*)&nSize, sizeof(nSize), 0))
		return false;

	// 发送消息数据
	if (nSize != send(socket, (char*)pMsg, nSize, 0))
		return false;

	return true;
}

猜你喜欢

转载自blog.csdn.net/m0_37251750/article/details/121085030