Muduo_Day1(前期准备)

大型网站架构演变过程

1.web动静资源分离:
动态请求:jsp,php等;动态请求应用服务器处理如:Tomacat
静态请求: html,js,css等,静态请求由http服务器处理,如Nginx.
2.负载均衡
反向代理:使用代理服务器将请求发送给内部服务器,让代理服务器将请求均匀转发给多台内部web服务器之一,从而达到负载均衡的目的.标准代理方式是客户使用代理访问多个外部web服务器,而这种代理方式是多个客户使用它来访问内部web服务器,因此被称为反向代理模式.
3.TCP中的分包,粘包问题:
TCP是一种基于字节流的传输服务,因此,TCP所传输的数据是没有边界的。这不同于UDP提供基于消息的传输服务,其传输的数据是有边界的。TCP的发送方无法保证对等方每次接收到的是一个完整的数据包。
产生的原因:
1.应用层通过调用write()函数,将应用层缓冲区中的数据拷贝到套接字发送缓冲区中。而套接字发送缓冲区有一个SO_SNBUF的限制。若应用层缓冲区中的数据大小大于套接字发送缓冲区的大小,则数据需要进行多次发送;
2.TCP所传输的报文段有MSS(最大报文段长度)的限制,若套接字发送缓冲区的大小大于MSS,也会导致消息的分割发送;
3.由于链路层具有MTU(最大传输单元)的限制,在网络层若数据超出了MTU,会进行数据分片。
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的.
解决的思路:
1.重写read与write函数,使其发送定长包,能够接收固定或者发送固定字节长度的数据包.
2.包尾加上"\r\n"标记,FTP就采用该种方法.
3.在接收数据包时,先接收4个字节的包头,里面包含这包体的长度信息,再根据此长度来接收包体数据.
4.利用消息本身的格式来分包,如XML格式消息中...的配对.解析这种消息格式通常用到状态机.
4:在muduo中,多处使用printf,而不是std::cout,原因在于printf是线程安全的,而std::cout不是.

网络编程的注意事项:
  1. POSIX 信号处理
    信号就是告知某个进程发生了某些事件的通知,于是又被称为软中端.每个信号都有与其相关联的信号处理行为,我们可以提供一个函数,当特定的信号发生时调用该函数,这种行为称为信号的补获,建立信号处置方法的函数是sigaction函数.
  • SIGCHLD信号是由内核在任何一个进程终止的时候发送给它的父进程的一个信号.我们常在服务器编程时忽略该信号,由于忽略该信号,导致子进程进入僵尸状态,设置子进程进入僵尸状态的目的是维护子进程的信息,以便在以后某个时间获取,这样当父进程结束终止时,子进程则直接由init进程接管回收.
  • SIGPIPE信号的存在使得对于disconnect的客户端的默认方式为关闭服务器进程,因此,如果服务器比较繁忙,没有即使处理对方断开连接的情况,有可能会出现在客户端断开连接之后仍然向客户端发送数据的情况.所以我们一般设置为忽略该信号"signal(SIGPIPE,SIG_IGN)".
  1. accept套接字描述符耗尽
    文件描述符是有限的,当描述符耗尽时,会返回EMFILE错误,表示无法继续为连接创建描述符,而且由于没有文件描述符可以表示该连接,那么我们就无法关闭这个连接,可能会使程序陷入busy loop.
    解决的方法:设置一个连接上限,当连接达到上限时之后主动关闭该新连接或者提前创建空闲的fd,采用空闲fd的方法防止已满(原理:准备一个空闲的文件描述符。遇到这种情况,先关闭这个空闲文件,获得一个文件描述符名额;再accept(2)拿到socket连接的文件描述符;随后立刻close(2),这样就优雅地断开了与客户端的连接;最后重新打开空闲文件,把“坑”填上,以备再次出现这种情况时使用).
  2. 如果要主动关闭连接,如何保证对方已经收到了全部的数据:
解答:在使用shutdown()关闭写方向的连接时,保留读方向上的连接,即TCP的半关闭状态

4.在非阻塞的网络编程中,需要使用应用层发送缓冲区的原因:

解答:如果应用程序将要发送的数据大于操作系统的发送缓冲区的大小,那么剩下的数据如果一定要等待发送缓冲区有空闲时会阻塞当前的进程,
因此可以选择把未发送完的数据先缓存起来放到TCP连接的应用层缓冲区,等socket变得可写时立刻发送数据.
在这时候如又有新的数据到达,则应将这些新的数据追加到发送缓冲区的末尾,而不能立即write,这样会打乱数据的顺序.

5.在非阻塞的网络编程中,需要使用应用层接收缓冲区的原因:

TCP是一个无边界的字节流协议,接收方必须要处理“收到的数据尚不构成一条完整的消息”和“一次收到两条消息的数据”等情况。因此这些数据应该先暂存在某个地方,以保证收到了一条完整的消息.

Muduo

  1. Muduo的实现并不是面向对象编程,而是基于对象编程的风格,大量使用了boost bind/function.
    2.框架与库的区别在于框架能够提供用户注册一些回调函数,使得框架能够调用我们所编写的回调函数,这就使得控制反转了.
  2. ./inspector_test预留一个http接口,使得任务服务器能够查看当前服务器的运行状态,为了更好的负载均衡.
面向对象风格与基于对象的编程思想:

面向对象的风格会暴露抽象类.
线程对象的生命周期与线程的生命周期不一样,若要实现线程对象在线程的生命周期结束时自动销毁,则需要使用动态对象.

Thread.h

#ifndef __THREAD_H__
#define __THREAD_H__

#include 
#include 
#include 
class Thread
{
    public:
        Thread();
        virtual ~Thread();

        void Start();
        //回收
        void Join();
        //普通的run函数不能作为pthread_create,隐含的第一个参数为this指针,不能作为派生类的入口函数
        //解决方法加一个静态函数,这样就不会有this指针传递过来
        static void* ThreadRoutine(void* arg);
        virtual void Run() = 0;
        void setautodelete(bool autodelete);
    private:
        pthread_t threadId;
        bool autodelete_;
};
#endif

Thread.cpp

#include "thread.h"
#include 
#include 
using namespace std;
Thread::Thread():autodelete_(false)
{
    cout << "Thread...." << endl;

};
Thread::~Thread()
{
    cout << "~thread...." << endl;
};

void Thread::Start()
{
    pthread_create(&threadId, NULL,ThreadRoutine,this);

};
void Thread::setautodelete(bool autodelete)
{
    autodelete_ = autodelete;
}
void Thread::Join()
{
    pthread_join(threadId,NULL);
};
//线程的入口函数应为run,所以在这需要调用run函数,但threadroutine
//函数是静态函数,所以他不能直接在这调用非静态成员函数run,所以这里
//要对this指针进行静态转型
void* Thread::ThreadRoutine(void* arg)
{
    //用基类指针指向派生类对象
    Thread* thread = static_cast(arg);
    //虚函数的多态
    thread->Run();
    if(thread->autodelete_)
        delete thread;
    return NULL;
}

test.cpp

#include "thread.h"
#include 
#include 

using namespace std;

class thread_test : public Thread
{
    public:
        thread_test(int count):count_(count)
        {
            cout << "threadtest...." << endl;
        };
        ~thread_test()
        {
            cout << "~threadtest..." << endl;
        };

        void Run()
        {
            while(count_--)
            {
                cout << "this is a test " << endl;
                sleep(1);
            }
        };
        int count_;
};

int main(void)
{

    /*
    thread_test H(5);
    H.Start();
    H.Join();
    */
    //以上代码创建了一个线程对象,运行完join后,
    //线程被回收,但创建的线程对象H并没有被回收,它会在return之后才销毁,
    //所以我们如果想要实现线程对象H与线程一块销毁,则需要创建动态对象然后
    //delete
    thread_test* H = new thread_test(5);
    H->setautodelete(true);
    H->Start();
    H->Join();
    //使主线程运行时间更长
    for(; ;)
        pause();
    return 0;
}

基于对象的编程思想,其虽然用到类,但没有涉及到类的继承,多态,虚函数等概念,在Muduo中大多数基于对象编程都是用boost::bind与boost::function来实现的:
boost::bind函数可以把一种函数接口转换为另一种类型的函数接口,该接口可以由boost::function来接收.参数可以自定义,通过占位符.boost::ref(foo)代表用引用.
如:

boost::functionfp = boost::bind(&foo::memberfunc(),&foo,0.5,_1(占位符),10);
fp(100);

该行中的表示:void f(int)函数接口,boost::function来接收.

Thread.h

#ifndef __THREAD_H__
#define __THREAD_H__

#include 
#include 
#include 
#include 
#include 
class Thread
{
    public:
        typedef boost::function threadfunc;
//使用显示构造函数避免了只含一个参数的构造函数采用的缺省转换操作
        explicit Thread(const threadfunc& func);
        ~Thread();

        void Start();
        //回收
        void Join();
        //普通的run函数不能作为pthread_create,隐含的第一个参数为this指针,不能作为派生类的入口函数
        //解决方法加一个静态函数,这样就不会有this指针传递过来
        static void* ThreadRoutine(void* arg);
        void Run();
        void setautodelete(bool autodelete);
    private:
        pthread_t threadId;
        bool autodelete_;
        threadfunc func_;
};
#endif

Thread.cpp

#include "thread.h"
#include 
#include 
#include 

using namespace std;
Thread::Thread(const threadfunc& func):autodelete_(false),func_(func)
{
    cout << "Thread...." << endl;
};
Thread::~Thread()
{
    cout << "~thread...." << endl;
};

void Thread::Start()
{
    pthread_create(&threadId, NULL,ThreadRoutine,this);

};
void Thread::setautodelete(bool autodelete)
{
    autodelete_ = autodelete;
}
void Thread::Join()
{
    pthread_join(threadId,NULL);
};
//线程的入口函数应为run,所以在这需要调用run函数,但threadroutine
//函数是静态函数,所以他不能直接在这调用非静态成员函数run,所以这里
//要对this指针进行静态转型
void* Thread::ThreadRoutine(void* arg)
{
    //用基类指针指向派生类对象
    Thread* thread = static_cast(arg);
    //虚函数的多态
    thread->Run();
    if(thread->autodelete_)
        delete thread;
    return NULL;
}
void Thread::Run()
{
    func_();
}

test.cpp

#include "thread.h"
#include 
#include 
#include 

using namespace std;

class foo
{
    public:
        foo(int count):count_(count)
    {

    };
    void memberfunc()
    {
        while(count_--)
        {
            cout << "this is a test" << endl;
            sleep(1);
        }
    }
    int count_;
};

void threadfunc()
{
    cout << "threadfunc......." << endl;
}

void threadfunc2(int cnt)
{
    while(cnt--)
    {
        cout << "threadfunc2..." << endl;
        sleep(1);
    }
}
int main(void)
{
   //绑定全局函数
    Thread t1(threadfunc);
    Thread t2(boost::bind(threadfunc2,3));
    //绑定成员函数
    foo f(3);
    Thread t3(boost::bind(&foo::memberfunc,&f));
    t1.Start();
    t2.Start();
    t3.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    return 0;
}

对于TCPserver与Echoserver之间接口的三种编程风格的比较:
C语言编程风格,会注册三个全局函数到网络库,网络库通过函数指针来回调.
C++面向对象的编程风格,用一个Echoserver继承TCPserver(抽象类),实现三个接口onConnection,onMessage与onClose.
C++基于对象的编程风格,用一个Echoserver包含一个TCPserver(具体类)对象,在构造函数中用boost::bind来注册三个成员函数.

你可能感兴趣的:(Muduo_Day1(前期准备))