#include<WinSock2.h>
#include<Windows.h>
#include
#include<stdio.h>
#include
#pragma comment(lib,“ws2_32.lib”)
enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, 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 Logout :public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_Logout;
}
char username[32];
};
struct LogoutResult :public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_Logout_Result;
result = 0;
}
int result;
};
std::vector g_client;
int process_solve(SOCKET _cSOCK)
{
//增加一个缓冲区
char szRecv[1024] = {};
//5.接收客户端新数据
int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), 0);
DataHeader
header = (DataHeader
)szRecv;
if (nLen <= 0)
{
printf("客户端已退出!任务结束!");
return -1;
}
switch (header->cmd){
case CMD_Login:
{
recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
Login *login = (Login*)szRecv;
printf("收到命令:CMD_Login,数据长度:%d\nUserName:%s\nPassWord:%s\n", login->dataLength, login->username, login->password);
//忽略判断用户密码是否正确的过程
LoginResult ret;
send(_cSOCK, (char *)&ret, sizeof(LoginResult), 0); //再发消息体
}
case CMD_Logout:
{
recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
Logout* logout = (Logout*)szRecv;
printf("收到命令:CMD_Logout,数据长度:%d\nUserName:%s\n", logout->dataLength, logout->username);
//忽略判断用户密码是否正确的过程
LogoutResult let;
send(_cSOCK, (char *)&let, sizeof(let), 0); //再发消息体
}
break;
default:
{
DataHeader header = { 0 };
send(_cSOCK, (char *)&header.cmd, sizeof(header), 0);
}
break;
}
}
int main()
{
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
//WinSocket启动
WSAStartup(ver, &dat);
//1、建立一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //AF_INET创建一个IPV4的套接字,SOCK_STREAM面向数据流的,IPPROTO_TCP TCP
if (INVALID_SOCKET == _sock)
{
printf("ERROR:建立失败!\n");
}
//2.绑定
sockaddr_in _sin = {}; //创建网络地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567); //Host to Network Short
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // IP地址
if (bind(_sock, (sockaddr *)&_sin, sizeof(_sin)) == SOCKET_ERROR)
{
printf("ERROR:绑定失败!\n");
}
else
{
printf("服务器端绑定成功......\n");
}
//3.监听网络端口
if (listen(_sock, 5) == SOCKET_ERROR)//第二个参数为最大等待多少人可以同时连接
{
printf("ERROR:监听失败!\n");
}
else
{
printf("服务器端监听成功......\n");
}
while (1)
{
//伯克利 socket
fd_set fd_Read;
fd_set fd_Write;
fd_set fd_Exp;
FD_ZERO(&fd_Read);//FD_ZERO 清空集合里的数据
FD_ZERO(&fd_Write);
FD_ZERO(&fd_Exp);
FD_SET(_sock, &fd_Read);//FD_SET 可以进行操作的宏
FD_SET(_sock, &fd_Write);
FD_SET(_sock, &fd_Exp);
for (int n = g_client.size() - 1; n >= 0; n--)
{
FD_SET(g_client[n], &fd_Read);
}
/*
select(
_In_ int nfds,
_Inout_opt_ fd_set FAR * readfds,
_Inout_opt_ fd_set FAR * writefds,
_Inout_opt_ fd_set FAR * exceptfds,
_In_opt_ const struct timeval FAR * timeout
);
*/
//nfds是一个整数值,是指fd_set集合所有的描述符(select里的第一个参数)的范围(而不是数量)
//既是所有文件描述符最大值+1
int ret = select(_sock + 1, &fd_Read, &fd_Write, &fd_Exp, NULL);
if (ret < 0)
{
printf("select任务结束!\n");
break;
}
if (FD_ISSET(_sock, &fd_Read))
{
FD_CLR(_sock, &fd_Read);
//4.等待接收客户端连接
sockaddr_in clientAddr = {};
int nAddrLen = sizeof(sockaddr_in);
SOCKET _cSOCK = INVALID_SOCKET;
_cSOCK = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);
if (_cSOCK == INVALID_SOCKET)
{
printf("ERROR:无效客户端SOCKET!\n");
}
g_client.push_back(_cSOCK);
printf("新客户端加入:Socket=%d,IP = %s\n", (int)_cSOCK, inet_ntoa(clientAddr.sin_addr));//inet_ntoa(clientAddr.sin_addr)将接收到的IP地址转化为字符串
}
for (int n = 0; n < fd_Read.fd_count; n++)
{
if (process_solve(fd_Read.fd_array[n]) == -1)
{
auto iter = find(g_client.begin(), g_client.end(), process_solve(fd_Read.fd_array[n]));
if (iter != g_client.end())
{
g_client.erase(iter);
}
}
}
}
for (int n = g_client.size(); n >= 0; n--)
{
//8.关闭自身的socket
closesocket(g_client[n]);
}
//8.关闭自身的socket
closesocket(_sock);
//WinSocket关闭
WSACleanup();
system("pause");
return 0;
}