Linux网络编程 6 - 使用原型模式代码重构

socket编程包括服务端和客户端,TCP或UDP,单线程、多线程、select模式等多种实现方式,现对代码进行重构。

详细实现代码:https://download.csdn.net/download/k117470154/10520499

一、类图

类图

类图说明:

1. CSktReq:socket地址基类,封装socktet、ip、端口

2. CSktReq:socket请求基类,每个socket请求创建一个实例,保存本端和对端地址

3. CSktApp:socket应用基类,实现不同类型的socket应用,比如tcpserver、tcpclient等

二、代码重构后,单线程TcpServer的实现代码

1. socket_base.h

#ifndef _SOCKET_BASE_H
#define _SOCKET_BASE_H

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h> 
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <assert.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <map>

using namespace std;


/* 重定义一些数据类型 */
typedef void                      VOID;
typedef char                      CHAR;
typedef unsigned char             BYTE;
typedef unsigned int              BOOL;
typedef unsigned short            WORD16;
typedef unsigned int              WORD32;
typedef int                       SWORD32;
typedef unsigned long             ULONG;
typedef stringstream              SSTREAM;
typedef ostringstream             OSTREAM;
typedef istringstream             ISTREAM;
typedef std::string               STRCPP;
typedef std::vector<string>       STRVEC;
typedef std::map<string, string>  STRMAP;
typedef std::map<string, string>::iterator STRMAPIT;
/*------------------------------------------------*/
typedef int                       SOCKFD;
typedef struct sockaddr_in        SOCKADDRIN;
typedef struct sockaddr           SOCKADDR;
typedef struct epoll_event        EPEVENT;
/*------------------------------------------------*/
typedef ofstream                  OFSTREAM;
typedef ifstream                  IFSTREAM;
typedef int                       FILEFD;
typedef struct stat               FILEST;
typedef struct dirent             DIRENT;
/*------------------------------------------------*/
typedef pthread_t                 THREAD;
typedef pthread_mutex_t           TMUTEX;
typedef pthread_cond_t            TCONDT;
/*------------------------------------------------*/
typedef pid_t                     PID;
typedef int                       PSTAT;
/*------------------------------------------------*/
typedef time_t                    TIMET;
typedef tm                        TIMETM;
typedef struct timeval            TIMEVAL;

/*------------------------------------------------*/
#define TRUE                      (1)
#define FALSE                     (0)
#define FDREAD                    0
#define FDWRITE                   1
#define STDIN                     0
#define STDOUT                    1
#define INVALIDFD                 ((SOCKFD)-1)
/*------------------------------------------------*/


// 定义socket地址类 封装socket ip port
class CSktAddr
{
public:
    CSktAddr();
    STRCPP addr();
    STRCPP portstr();
    VOID   noblock();
    VOID   setaddr(SOCKFD fd, STRCPP ip, WORD16 port, BOOL block);
    VOID   closefd();
public:
    SOCKFD fd;    // socket
    STRCPP ip;    // ip地址
    WORD16 port;  // 端口号
};


// 定义socket请求基类
class CSktReq
{
public:
    CSktReq():m_rbuff(), m_sbuff(), m_saddr(), m_paddr(), m_block(FALSE){}
    virtual ~CSktReq(){}

    SOCKFD selffd()          {return m_saddr.fd;}
    SOCKFD peerfd()          {return m_paddr.fd;}
    
    VOID   closeself();
    VOID   closepeer();

    VOID   setsaddr(SOCKFD fd);
    VOID   setpaddr(SOCKFD fd);

    virtual VOID* clone() = 0;
    virtual BOOL  initialize() = 0;
    virtual BOOL  activate() = 0;
    virtual BOOL  receive() = 0;
    virtual BOOL  dispatch() = 0;
    virtual BOOL  process() = 0;
protected:
    STRCPP   m_rbuff;  //接收数据的buffer
    STRCPP   m_sbuff;  //发送数据的buffer
    CSktAddr m_saddr;  //本端地址
    CSktAddr m_paddr;  //对端地址
    BOOL     m_block;
};

typedef list<CSktReq*>           REQUESTLIST;
typedef list<CSktReq*>::iterator REQUESTITER;
typedef map<SOCKFD, CSktReq*>    REQUESTMAP;


// 定义socket应用基类
class CSktApp
{
public:
    CSktApp(STRCPP ip, WORD16 port) : m_ip(ip), m_port(port), m_reqtype(NULL) {}
    virtual ~CSktApp();
    virtual VOID working() = 0;
    virtual VOID initreqtype() = 0;
    virtual VOID startup();
protected:
    STRCPP    m_ip;       //
    WORD16    m_port;     //
    CSktReq*  m_reqtype;  // 请求原型 (使用原型模式)
private:
};


STRCPP get_current_time();
STRCPP getgmttime(TIMET timestamp);
BOOL   getline(STRCPP& buffer, STRCPP& line);
BOOL   getsize(STRCPP& buffer, STRCPP& str, ULONG size);
VOID   delCRLF(STRCPP& buffer);
VOID   strsplit(const STRCPP& str, STRCPP sep, STRVEC& strvec);



#endif

2. socket_base.cpp

#include "socket_base.h"

//socket地址类构造函数
CSktAddr::CSktAddr() : fd(INVALIDFD), ip(), port(0)
{
}

//返回 ip地址:端口号:socket 格式的字符串
STRCPP CSktAddr::addr()
{
    OSTREAM addr;
    addr << ip << ":" << port << ":" << fd;
    return addr.str();
}

STRCPP CSktAddr::portstr()
{
    SSTREAM pstr;
    pstr << port;
    return pstr.str();
}


//设置socket为非阻塞
VOID CSktAddr::noblock()
{
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}


//设置socket地址各个字段
VOID CSktAddr::setaddr(SOCKFD sfd, STRCPP sip, WORD16 sport, BOOL block)
{
    fd    = sfd;
    ip    = sip;
    port  = sport;
    if(INVALIDFD != fd && !block)
    {
        fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
    }
}


//关闭socket
VOID CSktAddr::closefd()
{
    if(INVALIDFD != fd)
    {
        close(fd);
        fd = INVALIDFD;
    }
}


VOID CSktReq::closeself()
{
    m_saddr.closefd();
}

VOID CSktReq::closepeer()
{
    m_paddr.closefd();    
}


VOID CSktReq::setsaddr(SOCKFD fd)
{
    SOCKADDRIN addr = {0};
    WORD32     size = sizeof(addr);
    getsockname(fd, (SOCKADDR*)&addr, &size);
    m_saddr.fd   = fd;
    m_saddr.ip   = inet_ntoa(addr.sin_addr);
    m_saddr.port = ntohs(addr.sin_port);
}

VOID CSktReq::setpaddr(SOCKFD fd)
{
    SOCKADDRIN addr = {0};
    WORD32     size = sizeof(addr);
    getpeername(fd, (SOCKADDR*)&addr, &size);
    m_paddr.fd   = fd;
    m_paddr.ip   = inet_ntoa(addr.sin_addr);
    m_paddr.port = ntohs(addr.sin_port);
}


CSktApp::~CSktApp()
{
    if(NULL != m_reqtype)
    {
        m_reqtype->closeself();
        m_reqtype->closepeer();
        delete m_reqtype;
        m_reqtype = NULL;
    }
}


VOID CSktApp::startup()
{
    initreqtype();
    if(NULL != m_reqtype)
    {
        if(m_reqtype->initialize())
        {
            working();  // working必须有个循环在一直执行
        }
    }
    else
    {
        cout << "CSktApp startup failed." << endl;
    }
}


// 获取当前时间
STRCPP get_current_time()
{
    TIMET   tt = time(NULL);
    TIMETM* t  = localtime(&tt);
    CHAR    str[100] = {0};

    sprintf(str, "%d-%02d-%02d %02d:%02d:%02d",
            t->tm_year + 1900,
            t->tm_mon + 1,
            t->tm_mday,
            t->tm_hour,
            t->tm_min,
            t->tm_sec);
    return str;
}

// 获取GMT时间
STRCPP getgmttime(TIMET timestamp)
{
    static const CHAR* NAME_WEEKDAY[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    static const CHAR* NAME_MONTH[]   = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    CHAR    str[100] = {0};
    TIMETM* timegmt;                                          
    timegmt = gmtime(&timestamp);

    sprintf(str, "%s, %02d %3s %4d %02d:%02d:%02d GMT",
            NAME_WEEKDAY[timegmt->tm_wday],
            timegmt->tm_mday,
            NAME_MONTH[timegmt->tm_mon],
            timegmt->tm_year + 1900,
            timegmt->tm_hour,
            timegmt->tm_min,
            timegmt->tm_sec);
    return STRCPP(str);                                      
}

/* 从buffer中读取一行 \n或\r\n 并且去掉了\n 或\r\n */
BOOL getline(STRCPP& buffer, STRCPP& line)
{
    ULONG linepos = buffer.find('\n');
    if(string::npos == linepos)
    {
        return FALSE;
    }
    line.clear();
    if(linepos > 0 && buffer[linepos-1] == '\r')
    {
        line.assign(buffer, 0, linepos-1);
    }
    else
    {
        line.assign(buffer, 0, linepos);
    }
    buffer.erase(0, linepos+1);
    return TRUE;
}

/* 从buffer中读取前size个字节 放入str 并从buffer中删除前size个字节 */
BOOL getsize(STRCPP& buffer, STRCPP& str, ULONG size)
{
    str.clear();
    if(buffer.size() < size)
    {
        return FALSE;
    }
    str.assign(buffer, 0 , size);
    buffer.erase(0, size);
    return TRUE;
}


/* 从buffer中删除末尾的 \n或\r\n */
VOID delCRLF(STRCPP& buffer)
{
    ULONG size = buffer.size();

    if(0 < size && '\n' == buffer[size-1])
    {
        size--;
        if(0 < size && '\r' == buffer[size-1])
        {
            size--;
        }
        buffer.assign(buffer, 0, size);
    }
}

/* 以sep分割str 放入strvec中 
   strvec.size() 肯定 >= 1
   如果sep位于str的开始或结尾 则strvec的第一个或最后一个为空字符串 */
VOID strsplit(const STRCPP& str, STRCPP sep, STRVEC& strvec)
{
    ULONG  pos1 = 0;
    ULONG  pos2 = 0;
    STRCPP strtmp;
    strvec.clear();

    pos2 = str.find(sep);
    while(string::npos != pos2)
    {
        strtmp = str.substr(pos1, pos2 - pos1);
        //if(0 != strtmp.size())
        //{
            strvec.push_back(strtmp);
        //}
        pos1 = pos2 + sep.size();
        pos2 = str.find(sep, pos1);
    }
    strtmp = str.substr(pos1);
    //if(0 != strtmp.size())
    //{
        strvec.push_back(strtmp);
    //}
}


3. socket_tcpbase.h

#ifndef _SOCKET_TCP_H
#define _SOCKET_TCP_H

#include "socket_base.h"

class CTcpReq : public CSktReq
{
public:
    BOOL readinfo(STRCPP& buffer);
    BOOL sendinfo(const STRCPP& buffer);
protected:
private:
};


class CTcpApp : public CSktApp
{
public:
    CTcpApp(STRCPP ip, WORD16 port) : CSktApp(ip, port){}
protected:
private:
};

#endif

4. socket_tcpbase.cpp

#include "socket_tcpbase.h"


BOOL CTcpReq::readinfo(STRCPP& info)
{
    CHAR    tmp[1] = {0};  // 接收信息的缓冲区
    SWORD32 len    = 0;    // recv返回值 实际一次接收的长度

    len = recv(m_paddr.fd, tmp, sizeof(tmp), 0);
    if(0 < len)
    {
        info.append(tmp, len);
        return TRUE;
    }
    if(0 == len)
    {
        cout << "connect closed: " << m_paddr.addr() << endl;
        m_paddr.closefd();
        return FALSE;
    }
    else
    {
        if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
        {
            return TRUE;
        }
        return FALSE;
    }
}


BOOL CTcpReq::sendinfo(const STRCPP& info)
{ 
    WORD32  sendLen = 0;                    // 已发送的长度
    SWORD32 tempLen = 0;                    // send返回值 实际一次发送的长度
    const CHAR*   buffer  = info.c_str();   // 要发送的信息
    const WORD32  size    = info.size();    // 要发送的总长度

    if(NULL == buffer)
    {
        return FALSE;
    }
    while(sendLen < size)
    {
        tempLen = send(m_paddr.fd, buffer+sendLen, size-sendLen, MSG_NOSIGNAL);
        if(-1 == tempLen)
        {
            if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
            {
                continue;
            }
            return FALSE;
        }
        else
        {
            sendLen += tempLen;
        }
    }
    return TRUE;
}

5. socket_tcpserver.h

#ifndef _SOCKET_TCPSERVER_H
#define _SOCKET_TCPSERVER_H

#include "socket_tcpbase.h"

const STRCPP TCP_SERVER_IP      = "0.0.0.0";
const WORD16 TCP_SERVER_PORT    = 9000;


class CTcpSvrReq : public CTcpReq
{
public:
    CTcpSvrReq(STRCPP ip, WORD16 port, BOOL block)
    {
        m_saddr.setaddr(INVALIDFD, ip, port, block);
        m_block = block;
    }
    virtual VOID*  clone();
    virtual BOOL   initialize();
    virtual BOOL   activate();
    virtual BOOL   receive();
    virtual BOOL   dispatch();
    virtual BOOL   process();
protected:
private:
};


class CTcpSvrApp : public CTcpApp
{
public:
    CTcpSvrApp(STRCPP svrip, WORD16 svrport) : CTcpApp(svrip, svrport) {}
    virtual VOID initreqtype()
    {
        m_reqtype = new CTcpSvrReq(m_ip, m_port, TRUE);
    }
protected:
    REQUESTLIST m_requests;
private:
};

/* 单线程 阻塞 */
class CTcpServer_singlethread : public CTcpSvrApp
{
public:
    CTcpServer_singlethread(STRCPP svrip, WORD16 svrport) : CTcpSvrApp(svrip, svrport) {}
    virtual VOID working();    
protected:
};


#endif

6. socket_tcpserver.cpp

#include "socket_tcpserver.h"


VOID* CTcpSvrReq::clone()
{
    return new CTcpSvrReq(*this);
}

BOOL CTcpSvrReq::initialize() 
{
    WORD32      opt  = 1;
    SOCKADDRIN  addr = {0};
    SOCKFD      fd   = INVALIDFD;
    //创建socket
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == fd)
    {
        perror("socket failed");
        return FALSE;
    }
    //设置地址重用
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (CHAR*)&opt, sizeof(opt));
    addr.sin_family      = AF_INET;
    addr.sin_port        = htons(m_saddr.port);
    addr.sin_addr.s_addr = ("" == m_saddr.ip ? htonl(INADDR_ANY) : inet_addr(m_saddr.ip.c_str()));
    //绑定ip地址和端口
    if(-1 == bind(fd, (SOCKADDR*)&addr, sizeof(addr)))
    {
        perror("bind failed");
        close(fd);
        return FALSE;
    }
    //开始监听
    if(-1 == listen(fd, 10))
    {
        perror("listen failed");
        close(fd);
        return FALSE;
    }
    m_saddr.setaddr(fd, m_saddr.ip, m_saddr.port, m_block);
    return TRUE;
}

BOOL CTcpSvrReq::activate()
{
    SOCKFD     sockfd = -1;
    SOCKADDRIN addrin = {0};
    WORD32     addlen = sizeof(addrin);

    if(INVALIDFD == m_saddr.fd)
    {
        cout << "activate failed, fd invalid" << endl;
        return FALSE;
    }
    sockfd = accept(m_saddr.fd, (SOCKADDR*)&addrin, &addlen);
    if(-1 == sockfd)
    {
        perror("accept failed");
        return FALSE;
    }
    else
    {
        m_paddr.setaddr(sockfd, inet_ntoa(addrin.sin_addr), ntohs(addrin.sin_port), m_block);
        cout << "connect accept: " << m_paddr.addr() << endl;        
        return TRUE;
    }
}

BOOL CTcpSvrReq::receive()
{
    if(readinfo(m_rbuff))
    {
        return TRUE;
    }
    return FALSE;
}

BOOL CTcpSvrReq::dispatch()
{
    return TRUE;
}

BOOL CTcpSvrReq::process()
{
    STRCPP line;
    if(getline(m_rbuff, line))
    {
        cout << get_current_time() << " ";
        cout << m_paddr.addr() << " ";
        cout << line << endl;  // getline去掉了\n 所以这里需要endl才能输出到屏幕
    }
    return TRUE;
}



/* 单线程单链接 阻塞         */
VOID CTcpServer_singlethread::working()
{
    while(NULL != m_reqtype)
    {
        if(m_reqtype->activate())
        {
            while(INVALIDFD != m_reqtype->peerfd())
            {
                if(m_reqtype->receive())
                {
                    m_reqtype->process();
                }
            }
        }
        else
        {
            return;
        }
    }
}

7. test_tcpserver.cpp

#include "socket_tcpserver.h"

class CTestTcpServer : public CTcpServer_singlethread 
{
public:
    CTestTcpServer(STRCPP ip, WORD16 port) : CTcpServer_singlethread (ip, port) {}   
};


int main()
{
    CTestTcpServer tcpserver("127.0.0.1", 9000);
    tcpserver.startup();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/k117470154/article/details/82749689