(C++ server study notes): Transmitting multiple sending and receiving of messages to one sending and receiving

Convert multiple sending and receiving of messages to one sending and receiving

  • The packet header and data are combined, and they are sent once and received once.
  • Data definition, shared by server and client
enum CMD             //命令枚举
{
    CMD_LOGIN,
    CMD_LOGIN_RESULT,
    CMD_LOGOUT,
    CMD_LOGOUT_RESULT,
    CMD_ERROR
};

//DataHeader
struct DataHeader      //数据包头
{
    short dataLength;
    short cmd;
};

//DataPackage
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;
        lgResult = 0;
    }
    int lgResult;
};

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;
        lgOutResult = 0;
    }
    int lgOutResult;
};

[Server-side data sending and receiving processing]

 while (true)
{
    DataHeader dbHeader = {};
    //读取包头数据
    int nLen = recv(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0);
    if (nLen < 0)
    {
        printf("客户端已退出,任务结束\n");
        break;
    }
 
    switch (dbHeader.cmd)
    {
    case CMD_LOGIN:
    {
        Login login = {};
        recv(sockAccpt, (char*)&login + sizeof(DataHeader) , sizeof(Login)- sizeof(DataHeader), 0);
        printf("收到命令:CMD_LOGIN ,数据长度: %d, UserName = %s, \
                PassWord = %s \n", login.dataLength, login.UserName, login.PassWord);
        //忽略对用户密码进行判断
        LoginResult lgRet = {};
        send(sockAccpt, (char*)&lgRet, sizeof(LoginResult), 0);
    }
    break;
    case CMD_LOGOUT:
    {
        LogOut logout = {};
        recv(sockAccpt, (char*)&logout + sizeof(DataHeader), sizeof(LogOut) - sizeof(DataHeader), 0);
        printf("收到命令:CMD_LOGOUT ,数据长度: %d, UserName = %s, \
                \n", logout.dataLength, logout.UserName );
        //忽略对用户密码进行判断
        LogOutResult lgOutRet = {};
        send(sockAccpt, (char*)&lgOutRet, sizeof(LogOutResult), 0);
    }
    break;
    default:
        dbHeader.cmd = CMD_ERROR;
        dbHeader.dataLength = 0;
        send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0);
        break;
    }
}

[Client data sending and receiving processing]

while (true)
{
	//3.输入请求命令
	char cmdBuff[128] = {};
	scanf("%s", cmdBuff);

	//4.处理请求
	if (0 == strcmp(cmdBuff, "exit"))
	{
		printf("接收到命令exit,退出 \n");
		break;
	}
	else if (0 == strcmp(cmdBuff, "login"))
	{
		Login login {};
		login.cmd = CMD_LOGIN;
		login.dataLength = sizeof(login);
		strcpy(login.UserName, "喜羊羊");
		strcpy(login.PassWord,"123456");
		//5.向服务器发送请求
		send(sockCli, (const char *)&login, sizeof(login), 0);			

		//接收服务器返回的数据
		LoginResult loginRet = {};
		recv(sockCli, ( char*)&loginRet, sizeof(loginRet), 0);
		printf("LoginResult: %d\n", loginRet.lgResult);
	}
	else if (0 == strcmp(cmdBuff, "logout"))
	{
		LogOut logout{};
		logout.cmd = CMD_LOGOUT;
		logout.dataLength = sizeof(logout);
		strcpy(logout.UserName, "灰太狼");
		//5.向服务器发送请求
		send(sockCli, (const char*)&logout, sizeof(logout), 0);

		//接收服务器返回的数据
		LogOutResult logOutRet = {};
		recv(sockCli, (char*)&logOutRet, sizeof(logOutRet), 0);
		printf("LogOutResult: %d\n", logOutRet.lgOutResult);
	}
	else
	{
		printf("不支持该命令,请从新输入 \n");
	}
}

Source code

[Server source code]

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <WinSock2.h>
#include <cstdio>

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

enum CMD             //命令枚举
{
    CMD_LOGIN,
    CMD_LOGIN_RESULT,
    CMD_LOGOUT,
    CMD_LOGOUT_RESULT,
    CMD_ERROR
};

//DataHeader
struct DataHeader      //数据包头
{
    short dataLength;
    short cmd;
};

//DataPackage
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;
        lgResult = 0;
    }
    int lgResult;
};

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;
        lgOutResult = 0;
    }
    int lgOutResult;
};


int main()
{
    WORD ver = MAKEWORD(2, 2);
    WSAData dat;
    WSAStartup(ver, &dat);

    //1.创建socket套接字
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    //2,bind 绑定用于接收客户端连接的端口
    sockaddr_in sinAddr = {};
    sinAddr.sin_family = AF_INET;
    sinAddr.sin_port = htons(5678); //host to net unsigned short
    sinAddr.sin_addr.S_un.S_addr = INADDR_ANY;  //inet_addr("127.0.0.1")
    if (SOCKET_ERROR == bind(sock, (sockaddr*)&sinAddr, sizeof(sockaddr_in)))
    {
        printf("Bind Error\n");
    }
    else
    {
        printf("Bind Success\n");
    }

    //3. listen 监听网络端口
    if (SOCKET_ERROR == listen(sock, 5))
    {
        printf("Listen Error\n");
    }
    else
    {
        printf("Listen Success\n");
    }

    //4.accept 接收客户端连接
    sockaddr_in clientAddr = {};
    int clAddrLen = sizeof(sockaddr_in);

    SOCKET sockAccpt = INVALID_SOCKET;
    sockAccpt = accept(sock, (sockaddr*)&clientAddr, &clAddrLen);

    if (INVALID_SOCKET == sockAccpt)
    {
        printf("Accept Error\n");
    }
    else
    {
        printf("Accept Success\n");
    }
    printf("新客户端加入:Socket = %d,IP = %s \n", (int)sockAccpt, inet_ntoa(clientAddr.sin_addr));


    while (true)
    {
        DataHeader dbHeader = {};
        //读取包头数据
        int nLen = recv(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0);
        if (nLen < 0)
        {
            printf("客户端已退出,任务结束\n");
            break;
        }
     
        switch (dbHeader.cmd)
        {
        case CMD_LOGIN:
        {
            Login login = {};
            recv(sockAccpt, (char*)&login + sizeof(DataHeader) , sizeof(Login)- sizeof(DataHeader), 0);
            printf("收到命令:CMD_LOGIN ,数据长度: %d, UserName = %s, \
                    PassWord = %s \n", login.dataLength, login.UserName, login.PassWord);
            //忽略对用户密码进行判断
            LoginResult lgRet = {};
            send(sockAccpt, (char*)&lgRet, sizeof(LoginResult), 0);
        }
        break;
        case CMD_LOGOUT:
        {
            LogOut logout = {};
            recv(sockAccpt, (char*)&logout + sizeof(DataHeader), sizeof(LogOut) - sizeof(DataHeader), 0);
            printf("收到命令:CMD_LOGOUT ,数据长度: %d, UserName = %s, \
                    \n", logout.dataLength, logout.UserName );
            //忽略对用户密码进行判断
            LogOutResult lgOutRet = {};
            send(sockAccpt, (char*)&lgOutRet, sizeof(LogOutResult), 0);
        }
        break;
        default:
            dbHeader.cmd = CMD_ERROR;
            dbHeader.dataLength = 0;
            send(sockAccpt, (char*)&dbHeader, sizeof(DataHeader), 0);
            break;
        }
    }
        //closesocket 关闭套接字
        closesocket(sock);
        closesocket(sockAccpt);

        WSACleanup();

        printf("结束任务\n");
        getchar();
        return 0;
 }

[Client source code]

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define  _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <WinSock2.h>
#include <cstdio>
#pragma comment(lib,"ws2_32.lib")

enum CMD             //命令枚举
{
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,
	CMD_LOGOUT_RESULT,
	CMD_ERROR
};

//DataHeader
struct DataHeader      //数据包头
{
	short dataLength;
	short cmd;
};

//DataPackage
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;
		lgResult = 0;
	}
	int lgResult;
};

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;
		lgOutResult = 0;
	}
	int lgOutResult;
};


int main()
{
	WORD ver = MAKEWORD(2, 2);
	WSAData dat;
	WSAStartup(ver, &dat);

	//1.建立一个socket
	SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == sockCli)
	{
		printf("Socket Error\n");
	}
	else
	{
		printf("Socket Success\n");
	}

	//2. connect连接服务器
	sockaddr_in servAddr = {};
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(5678);
	servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	int ret = connect(sockCli, (sockaddr*)&servAddr, sizeof(sockaddr_in));

	if (SOCKET_ERROR == ret)
	{
		printf("Connect Error\n");
	}
	else
	{
		printf("Connect Success\n");
	}

	while (true)
	{
		//3.输入请求命令
		char cmdBuff[128] = {};
		scanf("%s", cmdBuff);

		//4.处理请求
		if (0 == strcmp(cmdBuff, "exit"))
		{
			printf("接收到命令exit,退出 \n");
			break;
		}
		else if (0 == strcmp(cmdBuff, "login"))
		{
			Login login {};
			login.cmd = CMD_LOGIN;
			login.dataLength = sizeof(login);
			strcpy(login.UserName, "喜羊羊");
			strcpy(login.PassWord,"123456");
			//5.向服务器发送请求
			send(sockCli, (const char *)&login, sizeof(login), 0);			

			//接收服务器返回的数据
			LoginResult loginRet = {};
			recv(sockCli, ( char*)&loginRet, sizeof(loginRet), 0);
			printf("LoginResult: %d\n", loginRet.lgResult);
		}
		else if (0 == strcmp(cmdBuff, "logout"))
		{
			LogOut logout{};
			logout.cmd = CMD_LOGOUT;
			logout.dataLength = sizeof(logout);
			strcpy(logout.UserName, "灰太狼");
			//5.向服务器发送请求
			send(sockCli, (const char*)&logout, sizeof(logout), 0);

			//接收服务器返回的数据
			LogOutResult logOutRet = {};
			recv(sockCli, (char*)&logOutRet, sizeof(logOutRet), 0);
			printf("LogOutResult: %d\n", logOutRet.lgOutResult);
		}
		else
		{
			printf("不支持该命令,请从新输入 \n");
		}
	}

	//7.关闭套接字 closesocket
	closesocket(sockCli);

	WSACleanup();
	printf("结束任务\n");
	getchar();
	return 0;
}

【operation result】

Server-side data receiving and processing optimization

while (true)
{
    //缓冲区
    char szRecv[1024] = {};

    //读取包头数据
    int nLen = recv(sockAccpt, (char*)&szRecv, sizeof(DataHeader), 0);
    DataHeader* dbHeader = (DataHeader*)szRecv;
    if (nLen < 0)
    {
        printf("客户端已退出,任务结束\n");
        break;
    }
    //if(nLen >= sizeof(DataHeader))
    switch (dbHeader->cmd)
    {
    case CMD_LOGIN:
    {
        recv(sockAccpt, szRecv + sizeof(DataHeader) , dbHeader->dataLength- sizeof(DataHeader), 0);
        Login* login = (Login*)szRecv;
        printf("收到命令:CMD_LOGIN ,数据长度: %d, UserName = %s, \
                PassWord = %s \n", login->dataLength, login->UserName, login->PassWord);
        //忽略对用户密码进行判断
        LoginResult lgRet = {};
        send(sockAccpt, (char*)&lgRet, sizeof(LoginResult), 0);
    }
    break;
    case CMD_LOGOUT:
    {
        recv(sockAccpt, szRecv + sizeof(DataHeader), dbHeader->dataLength - sizeof(DataHeader), 0);
        LogOut* logout = (LogOut*)szRecv;
        printf("收到命令:CMD_LOGOUT ,数据长度: %d, UserName = %s, \
                \n", logout->dataLength, logout->UserName );
        //忽略对用户密码进行判断
        LogOutResult lgOutRet = {};
        send(sockAccpt, (char*)&lgOutRet, sizeof(LogOutResult), 0);
    }
    break;
    default:
        DataHeader  HeaderError = { 0, CMD_ERROR };
        send(sockAccpt, (char*)&HeaderError, sizeof(HeaderError), 0);
        break;
    }
}
  • test

Guess you like

Origin blog.csdn.net/baidu_41388533/article/details/111825029