select实现服务端程序

#define WIN32_LEAN_AND_MEAN
#define  _WINSOCK_DEPRECATED_NO_WARNINGS 

#include <windows.h>
#include <WinSock2.h>
#include <stdio.h>
#include <vector>

#pragma comment(lib, "ws2_32.lib")

enum CMD
{
	CMD_LOGIN,
	CMD_LOGIN_RET,
	CMD_LOGOUT,
	CMD_LOGOUT_RET,
	CMD_ERROR
};


struct DataHeader
{
	int dataLength;
	CMD    cmd;
};

struct Login :public DataHeader
{
	Login()
	{
		dataLength = sizeof(Login);
		cmd = CMD_LOGIN;
	}
	char userName[32];
	char passWord[32];
};

struct LoginResult :public DataHeader
{
	LoginResult()
	{
		dataLength = sizeof(LoginResult);
		cmd = CMD_LOGIN_RET;
		ret = 0;
	}
	int ret;
};

struct LogOut :public DataHeader
{
	LogOut()
	{
		dataLength = sizeof(LogOut);
		cmd = CMD_LOGOUT;
	}
	char userName[32];
};

struct LogOutResult :public DataHeader
{
	LogOutResult()
	{
		dataLength = sizeof(LogOutResult);
		cmd = CMD_LOGOUT_RET;
		ret = 0;
	}
	int ret;
};

int processor(SOCKET _cSock)
{

	//5 接收客户端数据
	char szRecv[1024] = {};

	int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);
	if (nLen <= 0)
	{
		printf("客户端%d已退出。\n", _cSock);
		return -1;
	}

	DataHeader* pHeader = (DataHeader*)szRecv;

	switch (pHeader->cmd)
	{
	case CMD_LOGIN:
	{
		recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0);
		Login *pLogin = (Login*)szRecv;
		printf("客户端%d,cmd logint,user:%s,password:%s\n", _cSock, pLogin->userName, pLogin->passWord);
		//----------------------------
		//----------------------------
		LoginResult longinRet = {};
		send(_cSock, (const char*)&longinRet, sizeof(LoginResult), 0);
	}
	break;
	case CMD_LOGOUT:
	{

		recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0);
		LogOut *pLogOut = (LogOut*)szRecv;
		printf("客户端%d,cmd logout,user:%s\n", _cSock, pLogOut->userName);
		//----------------------------
		LogOutResult logOutRet = {};
		send(_cSock, (const char*)&logOutRet, sizeof(LogOutResult), 0);
	}
	break;
	default:
	{
		//剩余的垃圾数据是否要读取出来?
		if (pHeader->dataLength >0)
		{
			recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0);
		}

		DataHeader header = {};
		header.cmd = CMD_ERROR;
		header.dataLength = 0;
		send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
	}
	break;
	}

	return 0;
}

std::vector<SOCKET> g_clients;

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);

	//--用Socket API建立简易TCP服务端
	//1.建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//2 bind绑定用于接受客户端连接的网络端口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);//host to net unsigned
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	//使用127.0.0.1可以防止外网访问
	//启用本机全部的ip地址可以使用,INADDR_ANY
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in)))
	{
		printf("错误,绑定网络端口失败...\n");
	}
	else
	{
		printf("绑定网络端口成功...\n");
	}
	//3 listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		printf("错误,监听网络端口失败...\n");
	}
	else
	{
		printf("监听网络端口成功...\n");
	}



	while (true)
	{
		fd_set fdRead;
		fd_set fdWrite;
		fd_set fdExp;

		FD_ZERO(&fdRead);    /// 读套节字集合清空
		FD_ZERO(&fdWrite);   /// 写套接字集合清空
		FD_ZERO(&fdExp);     /// 异常套接字集合清空

		FD_SET(_sock, &fdRead);   /// 将_sock加入到读套接字集合中
		FD_SET(_sock, &fdWrite);  /// 将_sock加入到写套接字集合中
		FD_SET(_sock, &fdExp);    /// 将_sock加入到异常套接字集合中
		//接入的客户端socket放入到可读集合中,通过select来筛选出具有可读状态的socket
		for (size_t n = 0; n < g_clients.size(); n++)
		{
			FD_SET(g_clients[n], &fdRead);  /// 将客户端socket加入到可读套接字集合中
		}

		//timeval t = { 0,0 };
		int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, NULL); /// 监测读套接字集合、写套接字集合、异常套接字集合,任何时间发生都会返回
		/// select将更新这个集合,把其中不可读、不可写、非异常的套节字去掉 
		if (ret < 0)
		{
			printf("select任务结束。\n");
			break;
		}

		if (FD_ISSET(_sock, &fdRead))  /// 检查_sock是否在可读套接字集合里面
		{
			FD_CLR(_sock, &fdRead);    /// 从可读套接字集合中移除_sock
			sockaddr_in clientAddr = {};
			int nAddrLen = sizeof(clientAddr);
			SOCKET _cSock = INVALID_SOCKET;

			_cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);
			if (INVALID_SOCKET == _cSock)
			{
				printf("错误,接受到无效客户端socket...\n");
			}
			else
			{
				g_clients.push_back(_cSock);
				printf("新客户端加入:socket=%d,IP=%s\n", _cSock, inet_ntoa(clientAddr.sin_addr));
			}

		}

		for (size_t n = 0; n<fdRead.fd_count; n++)
		{
			if (-1 == processor(fdRead.fd_array[n]))
			{
				auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]);
				if (iter != g_clients.end())
				{
					closesocket(*iter);
					g_clients.erase(iter);
				}
			}
		}


	}

	//6 关闭套接字closesocket
	for (size_t n = 0; n<g_clients.size(); n++)
	{
		closesocket(g_clients[n]);
	}
	closesocket(_sock);


	//清除Windows socket环境
	WSACleanup();
	printf("已退出。\n");
	getchar();
	return 0;
}

参考: C++ 百万并发网络通信引擎架构与实现视频

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值