面向select网络编程转化为面向对象

数据包

#ifndef _MessageHeader_hpp_
#define _MessageHeader_hpp_

enum CMD{

    CMD_LOGIN,
    CMD_LOGIN_RESULT,
    CMD_LOGOUT,
    CMD_LOGOUT_RESULT,
    CMD_NEW_USER_JOIN,
    CMD_ERROR
};

//文件头
struct DataHeader{

    short cmd;
    short dataLength;

};
//登录
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 = 1;
    }
    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 = 1;
    }
    int result;
    char data[1024];

};
//新用户加入
struct NewUserJoin :public DataHeader{

    NewUserJoin(){
        dataLength = sizeof(NewUserJoin);
        cmd = CMD_NEW_USER_JOIN;
        scok = 0;
    }
    int scok;

};
#endif

 

client端


#ifndef _easyClient_hpp_
#define _easyClient_hpp_

#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#include<windows.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")//动态链接库
#include<iostream>
#include<stdio.h>
#include<thread>
#include "MH.h"


class easyClient
{
    SOCKET sock;
public:
    easyClient()
    {
        sock = INVALID_SOCKET;
    }
    //虚析构函数
    virtual ~easyClient()
    {
        Close();
    }
    //初始化socket
    void InitSocket(){
        //启动winsock2.x环境
        WORD ver = MAKEWORD(2, 2);
        WSADATA dat;
        WSAStartup(ver, &dat);
        if (sock != INVALID_SOCKET){
            printf("<SOCKET=%d>关闭旧连接", sock);
            Close();
        }
        //1、建立socket
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


    }

    int Connect(char* ip, unsigned short port){
        if (sock == INVALID_SOCKET){
            InitSocket();
        }

        // 2、连接服务器
        sockaddr_in _sin = {};
        _sin.sin_family = AF_INET;
        _sin.sin_port = htons(port);//host to net unsigned short
        _sin.sin_addr.S_un.S_addr = inet_addr(ip);
        int ret = connect(sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
        if (SOCKET_ERROR == ret)
            std::printf("绑定网络端口失败。。。");
        return ret;
    }

    //关闭socket
    void Close(){

        //关闭winsock 2.x环境
        if (sock != INVALID_SOCKET){
            closesocket(sock);
            WSACleanup();
            sock = INVALID_SOCKET;
        }

    }

    //处理网络消息
    bool  OnRun(){

        if (isRun()){
            fd_set fdReader;
            FD_ZERO(&fdReader);
            FD_SET(sock, &fdReader);
            timeval t = { 1, 0 };

            int ret = select(sock, &fdReader, 0, 0, NULL);
            if (ret < 0){
                std::printf("<SOCKET=%d>select 任务结束1\n", sock);
                return false;
            }

            if (FD_ISSET(sock, &fdReader)){

                FD_CLR(sock, &fdReader);

                if (-1 == RecvData(sock)){
                    std::printf("<SOCKET=%d>select 任务结束2\n", sock);
                    return false;
                }
            }
            return true;
        }
        return false;

    }

    //接收消息,处理粘包
    int RecvData(SOCKET _cSocket){

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

        int nLen = recv(_cSocket, szRecv, sizeof(DataHeader), 0);

        DataHeader* header = (DataHeader*)szRecv;
        if (nLen <= 0){
            std::printf("与服务器断开连接,任务结束。\n", _cSocket);
            return -1;
        }
        recv(_cSocket, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);

        OnNetMag(_cSocket, header);

        return 0;

    }

    //响应网络消息
    void OnNetMag(SOCKET _cSocket, DataHeader* header){
        //6、处理请求

        switch (header->cmd){
        case CMD_LOGIN_RESULT: {

                                   LoginResult* login = (LoginResult*)header;
                                   std::printf("收到服务器=%d消息:CMD_LOGIN_RESULT,数据长度:%d  \n", _cSocket, header->dataLength);

 

        } break;
        case CMD_LOGOUT_RESULT:{

                                   LogoutResult* logout = (LogoutResult*)header;
                                   std::printf("收到服务器=%d消息:CMD_LOGOUT_RESULT,数据长度:%d  \n", _cSocket, header->dataLength);

 

        }break;
        case CMD_NEW_USER_JOIN:{

                                   NewUserJoin* userjoin = (NewUserJoin*)header;
                                   std::printf("收到服务器=%d消息:CMD__NEW_USER_JOIN,新用户加入\n", _cSocket);


        } break;

        }
        std::printf("发送成功\n");

    }
    //发送网络消息
    int SendData(DataHeader* header){
        if (isRun() && header){
            return send(sock, (const char*)header, header->dataLength, 0);
        }
        return INVALID_SOCKET;
    }


    //是否工作中
    bool isRun(){
        return sock != INVALID_SOCKET;
    }
private:

 

};
#endif

client端的main.cpp

主函数:

 

#include<iostream>
#include<stdio.h>
#include<thread>
#include "client.h"
#include"MH.h"
using namespace std;

 

void cmdThread(easyClient* client)
{
    while (true){

        char cmdBuf[256] = {};
        scanf("%s", cmdBuf);
        if (0 == strcmp(cmdBuf, "exit")){
            client->Close();
            std::printf("退出thread线程\n");
            return;
        }
        else if (0 == strcmp(cmdBuf, "login")){
            Login login;
            strcpy(login.username, "wwy");
            strcpy(login.password, "123");
            client->SendData(&login);
        }
        else if (0 == strcmp(cmdBuf, "logout")){
            Logout logout;
            strcpy(logout.userName, "wwy");
            client->SendData(&logout);
        }
        else{
            printf("不支持的命令\n");
        }
    }

 

}

int main(){

    easyClient client;
    client.Connect("127.0.0.1", 4567);

    //启动线程

    thread t1(cmdThread, &client);
    t1.detach();


    while (client.isRun()){
        client.OnRun();


    }
    client.Close();

 

    std::printf("已退出\n");
    std::system("pause");

    return 0;
}

 

 

server端

 

#ifndef _servers_hpp_
#define _servers_hpp_

#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#include<windows.h>
#include<WinSock2.h>
#include<iostream>
#include<vector>
#include"MH.h"
#pragma comment(lib,"ws2_32.lib")//动态链接库
//可以在属性的链接库中添加
using namespace std;


class servers{

public:
    servers(){
        sock = INVALID_SOCKET;
    }

    virtual ~servers(){

        Close();
    }

    //初始化Socket
    SOCKET InitSocket(){

        //启动winsock2.x环境
        WORD ver = MAKEWORD(2, 2);
        WSADATA dat;
        WSAStartup(ver, &dat);
        if (sock != INVALID_SOCKET){
            printf("<SOCKET=%d>关闭旧连接", sock);
            Close();
        }
        //1、建立socket
         sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        return sock;
    }

    //绑定端口号
    int Bind(const char* ip, unsigned short port){

        if (sock == INVALID_SOCKET){
            InitSocket();
        }
        // 2、绑定网络端口
        sockaddr_in _sin = {};
        _sin.sin_family = AF_INET;
        _sin.sin_port = htons(port);//host to net unsigned short
        _sin.sin_addr.S_un.S_addr = inet_addr(ip);

        int ret = bind(sock, (sockaddr*)&_sin, sizeof(sockaddr_in));

        if (SOCKET_ERROR == ret)
            printf("绑定网络端口失败。。。");

        return ret;

 

    }


    //监听端口号
    int Listen(int n){

        //3、listen 监听端口
        int ret = listen(sock, n);
        if (SOCKET_ERROR == ret)
            printf("<SOCKET=%d>监听网络端口失败。。。", sock);

        return ret;
    }

 

    //接收客户端连接
    SOCKET Accept(){
        //4、accept 等待客户端连接
        sockaddr_in clientAddr = {};
        int nAddrlen = sizeof(sockaddr_in);
        SOCKET _cSocket = INVALID_SOCKET;

        _cSocket = accept(sock, (sockaddr*)&clientAddr, &nAddrlen);

        if (INVALID_SOCKET == _cSocket){
            printf("<SOCKET=%d>错误,接收到无效客户端。。。\n", (int)_cSocket);
        }
        else
        {
            NewUserJoin userjoin;
            SendDataToAll(&userjoin);
            g_clients.push_back(_cSocket);

            printf("新客户端加入:socket=%d,IP=%s\n", (int)_cSocket, inet_ntoa(clientAddr.sin_addr));

        }
        return _cSocket;
    }

    //关闭socket
    void Close(){

        //关闭winsock 2.x环境
        if (sock != INVALID_SOCKET){
            for (size_t n = g_clients.size() - 1; n >= 0; n--){
                closesocket(g_clients[n]);
            }

            // 7、关闭服务端套接字
            closesocket(sock);
            WSACleanup();
            sock = INVALID_SOCKET;
        }

    }


    //处理网络消息
    bool OnRun(){
        if (isRun()){

            fd_set fdRead;
            fd_set fdWrite;
            fd_set fdExp;

            //清理集合
            FD_ZERO(&fdRead);
            FD_ZERO(&fdWrite);
            FD_ZERO(&fdExp);

            //加入集合
            FD_SET(sock, &fdRead);
            FD_SET(sock, &fdWrite);
            FD_SET(sock, &fdExp);

            SOCKET maxSock = sock;
            for (int n = (int)g_clients.size() - 1; n >= 0; n--){
                //判断集合是否在集合中
                FD_SET(g_clients[n], &fdRead);
                if (maxSock < g_clients[n])
                    maxSock = g_clients[n];
            }
            timeval t = { 1, 0 };
            int ret = select(sock + 1, &fdRead, &fdWrite, &fdExp, &t);
            if (ret < 0){
                printf("select 客户端已退出。。。\n");
                Close();
                return false;
            }

            if (FD_ISSET(sock, &fdRead)){

                FD_CLR(sock, &fdRead);
                Accept();
            }

 

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

            }
            return true;
        }

        else{
            return false;
        }
    }

    //发送指定socket网络消息
    int SendData(SOCKET _cSocket, DataHeader* header){
        if (isRun() && header){
            return send(_cSocket, (const char*)header, header->dataLength, 0);
        }
        return INVALID_SOCKET;
    }

    //发送指定socket网络消息
    void SendDataToAll(DataHeader* header){
        if (isRun() && header){
            for (int n = (int)g_clients.size() - 1; n >= 0; n--){
                SendData(g_clients[n], header);
            }
        }

    }

    //是否工作中
    bool isRun(){
        return sock != INVALID_SOCKET;
    }
    //接收消息,处理粘包
    int RecvData(SOCKET  _cSocket){
        char szRecv[1024] = {};
        //5、接收客户端请求数据

        int nLen = recv(_cSocket, szRecv, sizeof(DataHeader), 0);
        DataHeader* header = (DataHeader*)szRecv;
        if (nLen <= 0){
            std::printf("客户端<SOCKET=%d>已退出\n", _cSocket);
            return -1;
        }
        std::printf("收到客户端的信息为:%d ,数据长度为%d\n", header->cmd, header->dataLength);
        recv(_cSocket, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
        OnNetMag(_cSocket, header);
        return 1;

    }

    //响应网络消息
    virtual void OnNetMag(SOCKET _cSocket, DataHeader* header){

        switch (header->cmd){
        case CMD_LOGIN: {


                            Login* login = (Login*)header;
                            std::printf("收到<SOCKET=%d>命令:CMD_LOGIN,数据长度:%d ,username:%s password:%s \n", _cSocket, login->dataLength, login->username, login->password);

                            std::printf("姓名为:%s上线了。。。\n", login->username);

                            LoginResult ret;
                            SendData(_cSocket, (DataHeader*)&ret);

        }break;

        case CMD_LOGOUT:{


                            Logout* logout = (Logout*)header;
                            std::printf("收到<SOCKET=%d>命令:CMD_LOGOUT,数据长度:%d  username:%s \n", _cSocket, logout->dataLength, logout->userName);

                            std::printf("姓名为:%s 已下线。。。\n", logout->userName);

                            LogoutResult ret;
                            SendData(_cSocket, (DataHeader*)&ret);


        }
            break;

        default:
        {    DataHeader header ;
        SendData(_cSocket, &header);
        }
            break;

        }

 

        std::printf("发送成功\n");
    }

    
private:
    vector<SOCKET> g_clients;
    SOCKET sock;

};

 

#endif

 

server端main.cpp文件

主函数:


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


int main(){

    servers server;
    server.InitSocket();
    server.Bind("127.0.0.1", 4567);
    server.Listen(5);


    while (server.isRun()){

        server.OnRun();

    }
    server.Close();

 

    printf("已退出,任务结束");
    system("pause");
    return 0;
}

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值