2021-07-14服务端linux/MacOS移植

//#pragma comment(lib,"ws2_32.lib")			//声明静态链接库


#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<windows.h>
#include<WinSock2.h>
#else
#include<unistd.h>
#include<arpa/inet.h>
#include<string.h>

#define SOCKET int
#define INVALID_SOCKET (int)(~0)
#define SOCKET_ERROR (-1)
#endif

#include<stdio.h>
#include<thread>
#include<vector>

//指令
enum CMD
{
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGINOUT,
	CMD_LOGINOUT_RESULT,
	CMD_NEW_USER_JOIN,
	CMD_ERROR
};
//消息头
struct DataHeader
{
	short dataLength;
	short 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_RESULT;
		result = 0;
	}
	int result;

};
//登出
struct Loginout : public DataHeader
{
	Loginout()
	{
		dataLength = sizeof(Loginout);
		cmd = CMD_LOGINOUT;
	}
	char userName[32];
};
//登出结果
struct LoginoutResult : public DataHeader
{
	LoginoutResult()
	{
		dataLength = sizeof(LoginoutResult);
		cmd = CMD_LOGINOUT_RESULT;
		result = 0;
	}
	int result;
};

struct NewUserJoin : public DataHeader
{
	NewUserJoin()
	{
		dataLength = sizeof(LoginoutResult);
		cmd = CMD_NEW_USER_JOIN;
		sock = 0;
	}
	int sock;
};


//客户端SOCKET数组,用来存储客户端的套接字
std::vector<SOCKET> _clients;
//处理请求函数
int processor(SOCKET _cSock)
{
	char szRecv[1024] = { };
	int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);
	// 5 接收客户端数据
	//将数据放在缓冲区中,缓冲区大小为1024
	//首先判断消息头
	DataHeader* header = (DataHeader*)szRecv;
	if (nLen <= 0)
	{
		printf("客户端<Socket:%d>已退出,任务结束。\n", _cSock);
		return -1;
	}
	// 6 处理请求
	switch (header->cmd)
	{
	case CMD_LOGIN:
	{

		//这里一共有两次接收数据,第二次接受的数据已经没有了包头DataHeader部分,因此需要加上一个偏移量sizeof(DataHeader),同时,数据长度也要减去一个sizeof(DataHeader)。
		//然后取得相应的数据
		recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
		Login* login = (Login*)szRecv;
		printf("收到客户端<Socket:%d>命令:CMD_LOGIN,数据长度 : %d,userName = %s ,PassWord = %s \n", _cSock, login->dataLength, login->userName, login->PassWord);
		//忽略判断用户密码是否正确的过程
		LoginResult ret = { };
		// 7 向客户端发送数据
		send(_cSock, (char*)&ret, sizeof(LoginResult), 0);
	}
	break;
	case CMD_LOGINOUT:
	{
		recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
		Loginout* loginout = (Loginout*)szRecv;;
		printf("收到客户端<Socket:%d>命令:CMD_LOGINOUT,数据长度 : %d,userName = %s \n", _cSock, loginout->dataLength, loginout->userName);
		//忽略判断用户密码是否正确的过程
		LoginoutResult ret = {  };

		send(_cSock, (char*)&ret, sizeof(LoginoutResult), 0);
	}
	break;
	default:
	{
		DataHeader header = { 0,CMD_ERROR };
		send(_cSock, (char*)&header, sizeof(DataHeader), 0);
	}
	break;
	}
	return 0;
}

int main()
{
#ifdef _WIN32
	WORD ver = MAKEWORD(2, 2);				//创建WINDOWS版本号
	WSADATA dat;
	WSAStartup(ver, &dat);					//启动网络环境,此函数调用了一个WINDOWS的静态链接库,因此需要加入静态链接库文件
#endif
	//-----------------

	//-- 用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);
#ifdef _WIN32
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;		//inet_addr("127.0.0.1");
#else
	_sin.sin_addr.s_addr = INADDR_ANY;
#endif
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		printf("错误,绑定网络端口失败...\n");
	}
	else {
		printf("绑定端口成功...\n");
	}

	// 3 listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		printf("错误,监听网络端口失败...\n");
	}
	else {
		printf("监听网络端口成功...\n");
	}


	while (true)
	{	
		//可用select实现非阻塞式程序
		fd_set fdRead;
		fd_set fdWrite;
		fd_set fdExc;
		//清空
		FD_ZERO(&fdRead);
		FD_ZERO(&fdWrite);
		FD_ZERO(&fdExc);
		//将描述符存入数组
		FD_SET(_sock, &fdRead);
		FD_SET(_sock, &fdWrite);
		FD_SET(_sock, &fdExc);

		SOCKET maxSock = _sock;
		//将新加入的客户端加入fdRead数组
		for (int n = (int)_clients.size() - 1; n >= 0; n--)
		{
			FD_SET(_clients[n], &fdRead);
			if (_clients[n] > maxSock) {
				maxSock = _clients[n];
			}
		}

		timeval t = { 1,0 };

		//select最后一个参数为NULL时,select函数阻塞直到有数据可以操作
		int ret = select(maxSock + 1, &fdRead, &fdWrite, &fdExc, &t);
		{
			if (ret < 0) {
				printf("select任务结束。\n");
				break;
			}
		}

		//测试该集合中一个给定位是否发生变化
		if (FD_ISSET(_sock, &fdRead))
		{
			//将数组中对应的描述符的计数值清0,并未将该描述符清除
			FD_CLR(_sock, &fdRead);

			sockaddr_in _clientAddr = { };
			int nAddrLen = sizeof(_clientAddr);
			SOCKET _cSock = INVALID_SOCKET;

			// accept 等待接收客户端连接
#ifdef _WIN32
			_cSock = accept(_sock, (sockaddr*)&_clientAddr, &nAddrLen);
#else
			_cSock = accept(_sock, (sockaddr*)&_clientAddr, (socklen_t*)&nAddrLen);
#endif
			if (INVALID_SOCKET == _cSock)
			{
				printf("错误,接收到无效客户端SOCKET...\n");
			}
			else {
				for (int n = (int)_clients.size() - 1; n >= 0; n--)
				{
					NewUserJoin userJoin;
					send(_clients[n], (const char*)&userJoin, sizeof(NewUserJoin), 0);
				}
				_clients.push_back(_cSock);
				printf("新客户加入:IP = %s ,socket = %d \n", inet_ntoa(_clientAddr.sin_addr), (int)_cSock);
			}
		}

		for (int n = (int)_clients.size() - 1; n >= 0; n--)
		{
			if (FD_ISSET(_clients[n], &fdRead))
			{
				if (processor(_clients[n]) == -1)
				{
					auto iter = _clients.begin()+n;
					if (iter != _clients.end())
					{
						_clients.erase(iter);
					}
				}
			}


		}

		//printf("空闲时间处理其他业务...\n");
	}
#ifdef _WIN32
	for (int n = _clients.size() - 1; n >= 0; n--)
	{
		closesocket(_clients[n]);
	}
	// 8 关闭套接字closesocket
	closesocket(_sock);

	//-----------------
	//清除Windows socket环境
	WSACleanup();							//关闭Socket网络环境
#else
	for (int n = _clients.size() - 1; n >= 0; n--)
	{
		close(_clients[n]);
	}
	close(_sock);
#endif

	getchar();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值