精华内容
下载资源
问答
  • ""deploy web app with complete installation (nginx, systemctl services and uwsgi)"" run only install.sh in your server to install ocserv and ocserv user managemnet pannel dont forgot this command : ...
  • Secure Your Node.js Web Application Keep Attackers Out and Users Happy 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除
  • 最畅销的书 保护您的Web应用程序免受黑客和攻击者 Defending your web applications against hackers and attackers The top-selling book Web Application Hacker's ... Battling Hackers and Protecting Users .
  • Web Application Defender’s Cookbook_ Battling Hackers and Protecting Users
  • Secure Your Node.js Web Application Keep Attackers Out and Users Happy 英文mobi 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除
  • Secure Your Node.js Web Application Keep Attackers Out and Users Happy 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 ...
  • 编写一个Web服务器---代码模块详细讲解(上)config 独立参数模块ET与LT模式config.h代码解读config.cpp代码解读main 模块 这里的参照的代码是https://github.com/qinguoyi/TinyWebServer 对于原代码的不足之处,...

    这里的参照的代码是https://github.com/qinguoyi/TinyWebServer

    • 对于原代码的不足之处,我会在之后的文章中给出改进代码
    • 在笔者fork的这版中,原代码作者对于代码作出了更细化的分类

    细节问题可以参考《APUE》《Linux高性能服务器编程》或者我之前的博客

    阅读任何源码一定要先从readme入手,如果没有readme,请从main入口入手。

    config 独立参数模块

    首先映入眼帘的是一个config的头文件,根据标识可以知道这是一个用户自定义头,所以我们先跳进去看看有什么东西。
    我在每个每个条目中都给出了注释。

    这里体现出整个项目的优点:模式切换
    也就是说这是一个复合的ET/LT。作者在Listenfd上和cfd上都建立了两种模式,意味着我们有四种组合方式。

    ET与LT模式

    lfd的ET代表着一次性接受所有连接,笔者认为这里是考虑到网络接入量巨大,瞬间占到Max_size的情况。LT代表一次取一个,当然这是默认的方式也是最常见的方式。

    cfd的两种方式也就是对应了epoll的方式,默认的LT和手动的ET

    config.h代码解读

    #ifndef CONFIG_H
    #define CONFIG_H
    
    #include "webserver.h" //懒得引用一堆头文件了
    
    using namespace std;
    
    class Config
    {
    public:
        Config();
        ~Config(){};
    
        void parse_arg(int argc, char*argv[]);
    
        //端口号
        int PORT;
    
        //日志写入方式
        int LOGWrite;
    
        //触发组合模式
        int TRIGMode;
    
        //listenfd触发模式
        int LISTENTrigmode;
    
        //connfd触发模式
        int CONNTrigmode;
    
        //优雅关闭链接
        int OPT_LINGER;
    
        //数据库连接池数量
        int sql_num;
    
        //线程池内的线程数量
        int thread_num;
    
        //是否关闭日志
        int close_log;
    
        //并发模型选择
        int actor_model;
    };
    
    #endif
    

    config.cpp代码解读

    其实很简单,在构造函数里作出了对于各种初始模式的设定
    并且在这版代码中,对于并发模式的处理,作者给出了reactor和preactor两种方式。(后面会详细讲解)
    原作者的测试环境为4核8线,所以这里给出了池内线程为8
    TRIGMode默认为最低效模式,可以改为1,实现服务器的最高性能,大概实现10wQPS

    #include "config.h"
    
    Config::Config(){
        //端口号,默认9006
        PORT = 9006;
    
        //日志写入方式,默认同步
        LOGWrite = 0;
    
        //触发组合模式,默认listenfd LT + connfd LT
        TRIGMode = 0;
    
        //listenfd触发模式,默认LT
        LISTENTrigmode = 0;
    
        //connfd触发模式,默认LT
        CONNTrigmode = 0;
    
        //优雅关闭链接,默认不使用
        OPT_LINGER = 0;
    
        //数据库连接池数量,默认8
        sql_num = 8;
    
        //线程池内的线程数量,默认8
        thread_num = 8;
    
        //关闭日志,默认不关闭
        close_log = 0;
    
        //并发模型,默认是proactor
        actor_model = 0;
    }
    
    void Config::parse_arg(int argc, char*argv[]){
        int opt;
        const char *str = "p:l:m:o:s:t:c:a:";
        while ((opt = getopt(argc, argv, str)) != -1)
        {
            switch (opt)
            {
            case 'p':
            {
                PORT = atoi(optarg);
                break;
            }
            case 'l':
            {
                LOGWrite = atoi(optarg);
                break;
            }
            case 'm':
            {
                TRIGMode = atoi(optarg);
                break;
            }
            case 'o':
            {
                OPT_LINGER = atoi(optarg);
                break;
            }
            case 's':
            {
                sql_num = atoi(optarg);
                break;
            }
            case 't':
            {
                thread_num = atoi(optarg);
                break;
            }
            case 'c':
            {
                close_log = atoi(optarg);
                break;
            }
            case 'a':
            {
                actor_model = atoi(optarg);
                break;
            }
            default:
                break;
            }
        }
    }
    

    总结: 简单的初始化形式的分割,改动参数的时候我只需要改动config.cpp就行了。

    main 模块

    main模块的主要功能是,驱动Sever。
    WebServer被单独作为一个类实现,并且封装好了调度函数。
    也就是说,main函数相当于一个开关,我打开了服务器的开关让他进入了listen状态,同时也转身打开了数据库的开关。
    当然,这个开关的信息,来自于config

    main.cpp代码解读

    这里的命令行解析是给数据库的运行方式传参,当然你可以什么都不传。
    定义,之后初始化了一个服务器对象。
    首先打开线程池,然后设置运行模式,之后就是启动监听和进入工作循环(事务循环)

    #include "config.h"
    
    int main(int argc, char *argv[])
    {
        //需要修改的数据库信息,登录名,密码,库名
        string user = "root";
        string passwd = "1215";
        string databasename = "Liweb_db";
    
        //命令行解析
        Config config;
        config.parse_arg(argc, argv);
    
        WebServer server;
    
        //初始化
        server.init(config.PORT, user, passwd, databasename, config.LOGWrite, 
                    config.OPT_LINGER, config.TRIGMode,  config.sql_num,  config.thread_num, 
                    config.close_log, config.actor_model);
        
    
        //日志
        server.log_write();
    
        //数据库
        server.sql_pool();
    
        //线程池
        server.thread_pool();
    
        //触发模式
        server.trig_mode();
    
        //监听
        server.eventListen();
    
        //运行
        server.eventLoop();
    
        return 0;
    }
    

    总结: 其实这一版的好处就是,给main瘦身。main本质上就是提供了入口,入口不需要太复杂。

    WebServer模块

    对于这种复杂模块,我会尽量根据调度顺序进行每个函数的分析,对于优点部分,我会重点标出。
    线程池是同步部分,数据库是额外部分这两个后面再讲
    目前是要搞清楚,怎么弄个反应堆打到我可以拿到事务,处理的问题稍后再说。目前只需要知道,我有个处理业务逻辑的池。

    trig_mode函数

    不用解释,对应不同功能

    void WebServer::trig_mode()
    {
        //LT + LT
        if (0 == m_TRIGMode)
        {
            m_LISTENTrigmode = 0;
            m_CONNTrigmode = 0;
        }
        //LT + ET
        else if (1 == m_TRIGMode)
        {
            m_LISTENTrigmode = 0;
            m_CONNTrigmode = 1;
        }
        //ET + LT
        else if (2 == m_TRIGMode)
        {
            m_LISTENTrigmode = 1;
            m_CONNTrigmode = 0;
        }
        //ET + ET
        else if (3 == m_TRIGMode)
        {
            m_LISTENTrigmode = 1;
            m_CONNTrigmode = 1;
        }
    }
    

    在仔细阅读这种复杂功能的模块之前,一定要理解清除调用逻辑。
    首先看main中分别调用了eventListen与eventLoop。根据见名知意的原则,我们可以推测出这是实现了listen部分与事务处理部分。

    eventListen函数

    如果你是初学者,不需要关注什么叫优雅的关闭连接这一部分,具体可以参考我的文章:Linux网络编程:知识点补充
    简单的民工三连调用不需要解释,作者加入了assert,提升了健壮性。
    之后就是调用epoll的三连了,将lfd上树,这里的上树封装为了addfd目的是为了可以更改模式。(cfd需要one_shot而lfd不需要)

    之后就是创建了管道,这里牵扯到进程间通信的问题。这么做的好处就是统一事件源。因为正常情况下,信号处理与IO处理不走一条路。
    这里的信号主要是超时问题
    具体的做法是,信号处理函数使用管道将信号传递给主循环,信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出信号值,使用I/O复用系统调用来监听管道读端的可读事件,这样信号事件与其他文件描述符都可以通过epoll来监测,从而实现统一处理。

    void WebServer::eventListen()
    {
        //网络编程基础步骤
        m_listenfd = socket(PF_INET, SOCK_STREAM, 0);
        //如果它的条件返回错误,则终止程序执行
        assert(m_listenfd >= 0);
    
        //TCP连接断开的时候调用closesocket函数,有优雅的断开和强制断开两种方式
        
        //优雅关闭连接
        if (0 == m_OPT_LINGER)
        {
            //底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出
            struct linger tmp = {0, 1};
            setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
        }
        else if (1 == m_OPT_LINGER)
        {
            //这种方式下,在调用closesocket的时候不会立刻返回,内核会延迟一段时间,这个时间就由l_linger得值来决定。
            //如果超时时间到达之前,发送完未发送的数据(包括FIN包)并得到另一端的确认,closesocket会返回正确,socket描述符优雅性退出。
            //否则,closesocket会直接返回 错误值,未发送数据丢失,socket描述符被强制性退出。需要注意的时,如果socket描述符被设置为非堵塞型,则closesocket会直接返回值。
            struct linger tmp = {1, 1};
            setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
        }
    
        int ret = 0;
        struct sockaddr_in address;
        // bzero() 会将内存块(字符串)的前n个字节清零;
        // s为内存(字符串)指针,n 为需要清零的字节数。
        // 在网络编程中会经常用到。
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = htonl(INADDR_ANY);
        address.sin_port = htons(m_port);
    
        int flag = 1;
        //允许重用本地地址和端口
        setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
        //传统绑定步骤
        ret = bind(m_listenfd, (struct sockaddr *)&address, sizeof(address));
        //>=0的设定 因为只有小于0才是错误情况
        assert(ret >= 0);
        //传统监听步骤
        ret = listen(m_listenfd, 5);
        assert(ret >= 0);
    
        utils.init(TIMESLOT);
    
        //epoll创建内核事件表
        epoll_event events[MAX_EVENT_NUMBER];
        m_epollfd = epoll_create(6);
        assert(m_epollfd != -1);
    
        //将lfd上树
        utils.addfd(m_epollfd, m_listenfd, false, m_LISTENTrigmode);
        http_conn::m_epollfd = m_epollfd;
    
        //创建管道套接字
        ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd);
        assert(ret != -1);
        //设置管道写端为非阻塞,为什么写端要非阻塞?
        //send是将信息发送给套接字缓冲区,如果缓冲区满了,则会阻塞,
        //这时候会进一步增加信号处理函数的执行时间,为此,将其修改为非阻塞。
        utils.setnonblocking(m_pipefd[1]);
    
        //设置管道读端为ET非阻塞 统一事件源
        utils.addfd(m_epollfd, m_pipefd[0], false, 0);
    
        utils.addsig(SIGPIPE, SIG_IGN);
    
        //传递给主循环的信号值,这里只关注SIGALRM和SIGTERM
        utils.addsig(SIGALRM, utils.sig_handler, false);
        utils.addsig(SIGTERM, utils.sig_handler, false);
    
        //每隔TIMESLOT时间触发SIGALRM信号
        alarm(TIMESLOT);
    
        //工具类,信号和描述符基础操作
        Utils::u_pipefd = m_pipefd;
        Utils::u_epollfd = m_epollfd;
    }
    

    总结: 完成了设置lfd与统一事件源,并且创建了带有一个节点的epoll树,同时完成了超时设定

    eventLoop函数

    这个函数可以说是始终伴随着程序始终。只要服务器不关,我就一直不退出,因为我退出了,main也退出了。
    明显看出,这一函数的逻辑就是不断的处理产生事件的节点(思考一下这里为什么这么叫)。
    而在epoll_wait返回后,我们主要处理三种事件:io事件,信号,新的连接
    也就是在for循环中的三次判断,并且每次处理完一组后,我们会刷新定时。

    void WebServer::eventLoop()
    {
        bool timeout = false;
        bool stop_server = false;
    
        while (!stop_server)
        {
            //监测发生事件的文件描述符
            int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1);
            if (number < 0 && errno != EINTR)
            {
                LOG_ERROR("%s", "epoll failure");
                break;
            }
    
            //轮询有事件产生的文件描述符
            for (int i = 0; i < number; i++)
            {
                int sockfd = events[i].data.fd;
    
                //处理新到的客户连接
                if (sockfd == m_listenfd)
                {
                    bool flag = dealclinetdata();
                    if (false == flag)
                        continue;
                }
                else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
                {
                    //服务器端关闭连接,移除对应的定时器
                    util_timer *timer = users_timer[sockfd].timer;
                    deal_timer(timer, sockfd);
                }
                //处理信号
                //管道读端对应文件描述符发生读事件
                //因为统一了事件源,信号处理当成读事件来处理
                //怎么统一?就是信号回调函数哪里不立即处理而是写到:pipe的写端
                else if ((sockfd == m_pipefd[0]) && (events[i].events & EPOLLIN))
                {
                    bool flag = dealwithsignal(timeout, stop_server);
                    if (false == flag)
                        LOG_ERROR("%s", "dealclientdata failure");
                }
                //处理客户连接上接收到的数据
                else if (events[i].events & EPOLLIN)
                {
                    dealwithread(sockfd);
                }
                else if (events[i].events & EPOLLOUT)
                {
                    dealwithwrite(sockfd);
                }
            }
            if (timeout)
            {
                utils.timer_handler();
    
                LOG_INFO("%s", "timer tick");
    
                timeout = false;
            }
        }
    }
    

    dealclinetdata函数

    其实笔者认为不加data更加符合情境,因为当前只是建立连接。
    就像刚才的模式介绍一样,lfd的两种模式。其实ET的存在就是应对存在服务器应付不过来连接请求,来提高效率的办法。
    建议LT使用。
    其实LT的额外循环是在epollwait部分,并且延迟体验是在用户端,用户可能傻傻的觉得自己卡了,问题不大

    bool WebServer::dealclinetdata()
    {
        struct sockaddr_in client_address;
        socklen_t client_addrlength = sizeof(client_address);
        //LT
        if (0 == m_LISTENTrigmode)
        {
            int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);
            if (connfd < 0)
            {
                LOG_ERROR("%s:errno is:%d", "accept error", errno);
                return false;
            }
            if (http_conn::m_user_count >= MAX_FD)
            {
                utils.show_error(connfd, "Internal server busy");
                LOG_ERROR("%s", "Internal server busy");
                return false;
            }
            timer(connfd, client_address);
        }
    
        else
        {
            //ET
            while (1)
            {
                int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);
                if (connfd < 0)
                {
                    LOG_ERROR("%s:errno is:%d", "accept error", errno);
                    break;
                }
                if (http_conn::m_user_count >= MAX_FD)
                {
                    utils.show_error(connfd, "Internal server busy");
                    LOG_ERROR("%s", "Internal server busy");
                    break;
                }
                timer(connfd, client_address);
            }
            return false;
        }
        return true;
    }
    

    dealwithread函数

    按照之前的思想,对于整个并发模式的思路,存在两个模式的切换:reactor与preactor(同步io模拟出)。它们的区别是对于数据的读取者是谁,对于reactor是同步线程来完成,整个读就绪放在请求列表上;而对于preactor则是由主线程,也就是当前的WebServer进行一次调用,读取后将读完成纳入请求队列上。

    同样,对于当前的fd我们要对他进行时间片的调整。同样的,当时间到期时,在定时器对象中,会有对应的下树操作。

    void WebServer::dealwithread(int sockfd)
    {
        util_timer *timer = users_timer[sockfd].timer;
    
        //reactor
        if (1 == m_actormodel)
        {
            if (timer)
            {
                adjust_timer(timer);
            }
    
            //若监测到读事件,将该事件放入请求队列
            m_pool->append(users + sockfd, 0);
    
            while (true)
            {
                if (1 == users[sockfd].improv)
                {
                    if (1 == users[sockfd].timer_flag)
                    {
                        deal_timer(timer, sockfd);
                        users[sockfd].timer_flag = 0;
                    }
                    users[sockfd].improv = 0;
                    break;
                }
            }
        }
        else
        {
            //proactor
            if (users[sockfd].read_once())
            {
                LOG_INFO("deal with the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr));
    
                //读完成事件,将该事件放入请求队列
                m_pool->append_p(users + sockfd);
    
                if (timer)
                {
                    adjust_timer(timer);
                }
            }
            else
            {
                deal_timer(timer, sockfd);
            }
        }
    }
    

    dealwithwrite函数

    逻辑与模式大致相同

    void WebServer::dealwithwrite(int sockfd)
    {
        util_timer *timer = users_timer[sockfd].timer;
        //reactor
        if (1 == m_actormodel)
        {
            if (timer)
            {
                adjust_timer(timer);
            }
    
            m_pool->append(users + sockfd, 1);
    
            while (true)
            {
                if (1 == users[sockfd].improv)
                {
                    if (1 == users[sockfd].timer_flag)
                    {
                        deal_timer(timer, sockfd);
                        users[sockfd].timer_flag = 0;
                    }
                    users[sockfd].improv = 0;
                    break;
                }
            }
        }
        else
        {
            //proactor
            if (users[sockfd].write())
            {
                LOG_INFO("send data to the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr));
    
                if (timer)
                {
                    adjust_timer(timer);
                }
            }
            else
            {
                deal_timer(timer, sockfd);
            }
        }
    }
    

    dealwithsignal函数

    与读写不同的是,这里的signal是处理函数,它不需要上队列。这里是通过管道的方式来告知WebServer。管道由epoll监控

    bool WebServer::dealwithsignal(bool &timeout, bool &stop_server)
    {
        int ret = 0;
        int sig;
        char signals[1024];
    
        //从管道读端读出信号值,成功返回字节数,失败返回-1
        //正常情况下,这里的ret返回值总是1,只有14和15两个ASCII码对应的字符
        ret = recv(m_pipefd[0], signals, sizeof(signals), 0);
        if (ret == -1)
        {
            return false;
        }
        else if (ret == 0)
        {
            return false;
        }
        else
        {
            //处理信号值对应的逻辑
            for (int i = 0; i < ret; ++i)
            {
                //这里面明明是字符
                switch (signals[i])
                {
                //这里是整型
                case SIGALRM:
                {
                    timeout = true;
                    break;
                }
                case SIGTERM:
                {
                    stop_server = true;
                    break;
                }
                }
            }
        }
        return true;
    }
    

    总结:目前我们只了解append是一个加入请求队列的函数,不去探究具体实现,当然在编程过程中我们应该也是这种思想,按照模块平行编程。而不是我想到什么功能就一定要先实现出来。递归编程容易把自己搞乱

    time函数集

    同样,我们把与时间片相关的调用,放在WebServer里,但是里面的细节,通过time这个类来实现。

    timer函数

    首先搞清楚,timer在什么时候调用?答案是在accept得到cfd的时候。这时候通过timer函数不只是初始化了cfd的时间,而且整体初始化。
    也就是说,当前服务器已经认可了这一连接,完成了三次握手,并且得到了用户标识,允许传输数据。
    这里为了提升性能,给到了3倍阈值的超时。

    void WebServer::timer(int connfd, struct sockaddr_in client_address)
    {
        users[connfd].init(connfd, client_address, m_root, m_CONNTrigmode, m_close_log, m_user, m_passWord, m_databaseName);
    
        //初始化client_data数据
        //创建定时器,设置回调函数和超时时间,绑定用户数据,将定时器添加到链表中
        users_timer[connfd].address = client_address;
        users_timer[connfd].sockfd = connfd;
        util_timer *timer = new util_timer;
        timer->user_data = &users_timer[connfd];
        timer->cb_func = cb_func;
        time_t cur = time(NULL);
        timer->expire = cur + 3 * TIMESLOT;
        users_timer[connfd].timer = timer;
        utils.m_timer_lst.add_timer(timer);
    }
    

    adjust_timer与deal_timer

    也是区分清楚什么时候调用。
    adjust可以看到是在产生事件之后,我为了还能传输数据,再给你刷新一下你的时间或者给你延长;而deal则是对于坏连接的一个处理。
    如果你对此不理解,可以跳过,你只需要知道what it is。至于how and why我会在time部分详细解析。

    void WebServer::adjust_timer(util_timer *timer)
    {
        time_t cur = time(NULL);
        timer->expire = cur + 3 * TIMESLOT;
        utils.m_timer_lst.adjust_timer(timer);
    
        LOG_INFO("%s", "adjust timer once");
    }
    void WebServer::deal_timer(util_timer *timer, int sockfd)
    {
        timer->cb_func(&users_timer[sockfd]);
        if (timer)
        {
            utils.m_timer_lst.del_timer(timer);
        }
        LOG_INFO("close fd %d", users_timer[sockfd].sockfd);
    }
    

    总结

    WebServer可以说是全功能的一个大集合,也就是说我们构建出了一个领导角色,左手epoll右手线程池。我尽量按照调用顺序来讲解函数,方便读者阅读代码。

    WebServer.h 头文件

    #ifndef WEBSERVER_H
    #define WEBSERVER_H
    
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <cassert>
    #include <sys/epoll.h>
    
    #include "./threadpool/threadpool.h"
    #include "./http/http_conn.h"
    
    const int MAX_FD = 65536;           //最大文件描述符
    const int MAX_EVENT_NUMBER = 10000; //最大事件数
    const int TIMESLOT = 5;             //最小超时单位
    
    class WebServer
    {
    public:
        WebServer();
        ~WebServer();
    
        void init(int port , string user, string passWord, string databaseName,
                  int log_write , int opt_linger, int trigmode, int sql_num,
                  int thread_num, int close_log, int actor_model);
        //线程池函数
        void thread_pool(); 
        //数据库池函数
        void sql_pool();    
        void log_write(); 
        //更改模式  
        void trig_mode();  
        //创建lfd 
        void eventListen(); 
        //当服务器非关闭状态 用于处理事件
        void eventLoop();   
        
        //定时器的操作
        void timer(int connfd, struct sockaddr_in client_address); 
        void adjust_timer(util_timer *timer);
        void deal_timer(util_timer *timer, int sockfd); 
    
        //处理用户数据 这里原作者存在拼写错误
        bool dealclinetdata(); 
        //信号
        bool dealwithsignal(bool& timeout, bool& stop_server); 
        //读事件
        void dealwithread(int sockfd);
        //写事件
        void dealwithwrite(int sockfd);
    
    public:
        //基础
        //监听端口
        int m_port;
        char *m_root;
        //日志
        int m_log_write;
        int m_close_log;
        //触发模式
        int m_actormodel;
    
        //进程通信模块
        int m_pipefd[2];
        //epoll根
        int m_epollfd;
        
        //用于接受用户连接
        http_conn *users;
    
        //数据库相关
        connection_pool *m_connPool;
        string m_user;         //登陆数据库用户名
        string m_passWord;     //登陆数据库密码
        string m_databaseName; //使用数据库名
        int m_sql_num;
    
        //http线程池
        threadpool<http_conn> *m_pool;
        int m_thread_num;
    
        //epoll_event 注册节点事件  
        epoll_event events[MAX_EVENT_NUMBER];
    
        int m_listenfd; //监听fd 申请一次
        int m_OPT_LINGER;
        int m_TRIGMode; //触发模式 ET+LT LT+LT LT+ET  ET+ET 
        int m_LISTENTrigmode; // 监听 ET/LT
        int m_CONNTrigmode;   // 连接 ET/LT
    
        //定时器相关
        client_data *users_timer;
        Utils utils;
    };
    #endif
    

    WebServer.cpp 完整代码

    #include "webserver.h"
    
    WebServer::WebServer()
    {
        //用来调用指定fd的所需功能模块
        users = new http_conn[MAX_FD];
    
        //root文件夹路径
        char server_path[200];
        getcwd(server_path, 200);
        char root[6] = "/root";
        m_root = (char *)malloc(strlen(server_path) + strlen(root) + 1);
        strcpy(m_root, server_path);
        strcat(m_root, root);
    
        //定时器
        users_timer = new client_data[MAX_FD];
    }
    
    WebServer::~WebServer()
    {
        close(m_epollfd);
        close(m_listenfd);
        close(m_pipefd[1]);
        close(m_pipefd[0]);
        delete[] users;
        delete[] users_timer;
        delete m_pool;
    }
    
    void WebServer::init(int port, string user, string passWord, string databaseName, int log_write, 
                         int opt_linger, int trigmode, int sql_num, int thread_num, int close_log, int actor_model)
    {
        m_port = port;
        m_user = user;
        m_passWord = passWord;
        m_databaseName = databaseName;
        m_sql_num = sql_num;
        m_thread_num = thread_num;
        m_log_write = log_write;
        m_OPT_LINGER = opt_linger;
        m_TRIGMode = trigmode;
        m_close_log = close_log;
        m_actormodel = actor_model;
    }
    
    void WebServer::trig_mode()
    {
        //LT + LT
        if (0 == m_TRIGMode)
        {
            m_LISTENTrigmode = 0;
            m_CONNTrigmode = 0;
        }
        //LT + ET
        else if (1 == m_TRIGMode)
        {
            m_LISTENTrigmode = 0;
            m_CONNTrigmode = 1;
        }
        //ET + LT
        else if (2 == m_TRIGMode)
        {
            m_LISTENTrigmode = 1;
            m_CONNTrigmode = 0;
        }
        //ET + ET
        else if (3 == m_TRIGMode)
        {
            m_LISTENTrigmode = 1;
            m_CONNTrigmode = 1;
        }
    }
    
    void WebServer::log_write()
    {
        if (0 == m_close_log)
        {
            //初始化日志
            if (1 == m_log_write)
                Log::get_instance()->init("./ServerLog", m_close_log, 2000, 800000, 800);
            else
                Log::get_instance()->init("./ServerLog", m_close_log, 2000, 800000, 0);
        }
    }
    
    void WebServer::sql_pool()
    {
        //初始化数据库连接池
        m_connPool = connection_pool::GetInstance();
        m_connPool->init("localhost", m_user, m_passWord, m_databaseName, 3306, m_sql_num, m_close_log);
    
        //初始化数据库读取表
        users->initmysql_result(m_connPool);
    }
    
    void WebServer::thread_pool()
    {
        //线程池
        m_pool = new threadpool<http_conn>(m_actormodel, m_connPool, m_thread_num);
    }
    
    void WebServer::eventListen()
    {
        //网络编程基础步骤
        m_listenfd = socket(PF_INET, SOCK_STREAM, 0);
        //如果它的条件返回错误,则终止程序执行
        assert(m_listenfd >= 0);
    
        //TCP连接断开的时候调用closesocket函数,有优雅的断开和强制断开两种方式
        
        //优雅关闭连接
        if (0 == m_OPT_LINGER)
        {
            //底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出
            struct linger tmp = {0, 1};
            setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
        }
        else if (1 == m_OPT_LINGER)
        {
            //这种方式下,在调用closesocket的时候不会立刻返回,内核会延迟一段时间,这个时间就由l_linger得值来决定。
            //如果超时时间到达之前,发送完未发送的数据(包括FIN包)并得到另一端的确认,closesocket会返回正确,socket描述符优雅性退出。
            //否则,closesocket会直接返回 错误值,未发送数据丢失,socket描述符被强制性退出。需要注意的时,如果socket描述符被设置为非堵塞型,则closesocket会直接返回值。
            struct linger tmp = {1, 1};
            setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
        }
    
        int ret = 0;
        struct sockaddr_in address;
        // bzero() 会将内存块(字符串)的前n个字节清零;
        // s为内存(字符串)指针,n 为需要清零的字节数。
        // 在网络编程中会经常用到。
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = htonl(INADDR_ANY);
        address.sin_port = htons(m_port);
    
        int flag = 1;
        //允许重用本地地址和端口
        setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
        //传统绑定步骤
        ret = bind(m_listenfd, (struct sockaddr *)&address, sizeof(address));
        //>=0的设定 因为只有小于0才是错误情况
        assert(ret >= 0);
        //传统监听步骤
        ret = listen(m_listenfd, 5);
        assert(ret >= 0);
    
        utils.init(TIMESLOT);
    
        //epoll创建内核事件表
        epoll_event events[MAX_EVENT_NUMBER];
        m_epollfd = epoll_create(6);
        assert(m_epollfd != -1);
    
        //将lfd上树
        utils.addfd(m_epollfd, m_listenfd, false, m_LISTENTrigmode);
        http_conn::m_epollfd = m_epollfd;
    
        //创建管道套接字
        ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd);
        assert(ret != -1);
        //设置管道写端为非阻塞,为什么写端要非阻塞?
        //send是将信息发送给套接字缓冲区,如果缓冲区满了,则会阻塞,
        //这时候会进一步增加信号处理函数的执行时间,为此,将其修改为非阻塞。
        utils.setnonblocking(m_pipefd[1]);
    
        //设置管道读端为ET非阻塞 统一事件源
        utils.addfd(m_epollfd, m_pipefd[0], false, 0);
    
        utils.addsig(SIGPIPE, SIG_IGN);
    
        //传递给主循环的信号值,这里只关注SIGALRM和SIGTERM
        utils.addsig(SIGALRM, utils.sig_handler, false);
        utils.addsig(SIGTERM, utils.sig_handler, false);
    
        //每隔TIMESLOT时间触发SIGALRM信号
        alarm(TIMESLOT);
    
        //工具类,信号和描述符基础操作
        Utils::u_pipefd = m_pipefd;
        Utils::u_epollfd = m_epollfd;
    }
    
    void WebServer::timer(int connfd, struct sockaddr_in client_address)
    {
        users[connfd].init(connfd, client_address, m_root, m_CONNTrigmode, m_close_log, m_user, m_passWord, m_databaseName);
    
        //初始化client_data数据
        //创建定时器,设置回调函数和超时时间,绑定用户数据,将定时器添加到链表中
        users_timer[connfd].address = client_address;
        users_timer[connfd].sockfd = connfd;
        util_timer *timer = new util_timer;
        timer->user_data = &users_timer[connfd];
        //时间到了踢出树
        timer->cb_func = cb_func;
        time_t cur = time(NULL);
        timer->expire = cur + 3 * TIMESLOT;
        users_timer[connfd].timer = timer;
        utils.m_timer_lst.add_timer(timer);
    }
    
    //若有数据传输,则将定时器往后延迟3个单位
    //并对新的定时器在链表上的位置进行调整
    void WebServer::adjust_timer(util_timer *timer)
    {
        time_t cur = time(NULL);
        timer->expire = cur + 3 * TIMESLOT;
        utils.m_timer_lst.adjust_timer(timer);
    
        LOG_INFO("%s", "adjust timer once");
    }
    
    void WebServer::deal_timer(util_timer *timer, int sockfd)
    {
        timer->cb_func(&users_timer[sockfd]);
        if (timer)
        {
            utils.m_timer_lst.del_timer(timer);
        }
    
        LOG_INFO("close fd %d", users_timer[sockfd].sockfd);
    }
    
    
    bool WebServer::dealclinetdata()
    {
        struct sockaddr_in client_address;
        socklen_t client_addrlength = sizeof(client_address);
        //LT
        if (0 == m_LISTENTrigmode)
        {
            int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);
            if (connfd < 0)
            {
                LOG_ERROR("%s:errno is:%d", "accept error", errno);
                return false;
            }
            if (http_conn::m_user_count >= MAX_FD)
            {
                utils.show_error(connfd, "Internal server busy");
                LOG_ERROR("%s", "Internal server busy");
                return false;
            }
            timer(connfd, client_address);
        }
    
        else
        {
            //ET
            while (1)
            {
                int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);
                if (connfd < 0)
                {
                    LOG_ERROR("%s:errno is:%d", "accept error", errno);
                    break;
                }
                if (http_conn::m_user_count >= MAX_FD)
                {
                    utils.show_error(connfd, "Internal server busy");
                    LOG_ERROR("%s", "Internal server busy");
                    break;
                }
                timer(connfd, client_address);
            }
            return false;
        }
        return true;
    }
    
    bool WebServer::dealwithsignal(bool &timeout, bool &stop_server)
    {
        int ret = 0;
        int sig;
        char signals[1024];
    
        //从管道读端读出信号值,成功返回字节数,失败返回-1
        //正常情况下,这里的ret返回值总是1,只有14和15两个ASCII码对应的字符
        ret = recv(m_pipefd[0], signals, sizeof(signals), 0);
        if (ret == -1)
        {
            return false;
        }
        else if (ret == 0)
        {
            return false;
        }
        else
        {
            //处理信号值对应的逻辑
            for (int i = 0; i < ret; ++i)
            {
                //这里面明明是字符
                switch (signals[i])
                {
                //这里是整型
                case SIGALRM:
                {
                    timeout = true;
                    break;
                }
                case SIGTERM:
                {
                    stop_server = true;
                    break;
                }
                }
            }
        }
        return true;
    }
    
    void WebServer::dealwithread(int sockfd)
    {
        util_timer *timer = users_timer[sockfd].timer;
    
        //reactor
        if (1 == m_actormodel)
        {
            if (timer)
            {
                adjust_timer(timer);
            }
    
            //若监测到读事件,将该事件放入请求队列
            m_pool->append(users + sockfd, 0);
    
            while (true)
            {
                if (1 == users[sockfd].improv)
                {
                    if (1 == users[sockfd].timer_flag)
                    {
                        deal_timer(timer, sockfd);
                        users[sockfd].timer_flag = 0;
                    }
                    users[sockfd].improv = 0;
                    break;
                }
            }
        }
        else
        {
            //proactor
            if (users[sockfd].read_once())
            {
                LOG_INFO("deal with the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr));
    
                //读完成事件,将该事件放入请求队列
                m_pool->append_p(users + sockfd);
    
                if (timer)
                {
                    adjust_timer(timer);
                }
            }
            else
            {
                deal_timer(timer, sockfd);
            }
        }
    }
    
    void WebServer::dealwithwrite(int sockfd)
    {
        util_timer *timer = users_timer[sockfd].timer;
        //reactor
        if (1 == m_actormodel)
        {
            if (timer)
            {
                adjust_timer(timer);
            }
    
            m_pool->append(users + sockfd, 1);
    
            while (true)
            {
                if (1 == users[sockfd].improv)
                {
                    if (1 == users[sockfd].timer_flag)
                    {
                        deal_timer(timer, sockfd);
                        users[sockfd].timer_flag = 0;
                    }
                    users[sockfd].improv = 0;
                    break;
                }
            }
        }
        else
        {
            //proactor
            if (users[sockfd].write())
            {
                LOG_INFO("send data to the client(%s)", inet_ntoa(users[sockfd].get_address()->sin_addr));
    
                if (timer)
                {
                    adjust_timer(timer);
                }
            }
            else
            {
                deal_timer(timer, sockfd);
            }
        }
    }
    
    void WebServer::eventLoop()
    
    展开全文
  • Using WebPageTest - Web Performance Testing for Novices and Power Users This pdf is converted from azw3 format
  • <Building Django 2.0 Web Applications: Create enterprise-grade, scalable Python web applications easily with Django 2.0> Go from the initial idea to a production-deployed web app using Django 2.0. Key...
  • 报错信息简短版:OperationalError: (sqlite3.OperationalError) no such table: users报错信息完整版:(venv_flask_henry) henry@henry-virtual-machine:~/dev/flask_henry$ python manage.py shell /home/henry/...

    报错信息简短版:

    OperationalError: (sqlite3.OperationalError) no such table: users

    报错信息完整版:

    (venv_flask_henry) henry@henry-virtual-machine:~/dev/flask_henry$ python manage.py shell
    /home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py:794: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True or False to suppress this warning.
      'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
    u
    >>> u = User(email='john@example.com', username='john', password='cat')
    >>> db.session.add(u)
    >>> db.session.commit()
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 157, in do
        return getattr(self.registry(), name)(*args, **kwargs)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 921, in commit
        self.transaction.commit()
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 461, in commit
        self._prepare_impl()
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 441, in _prepare_impl
        self.session.flush()
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2192, in flush
        self._flush(objects)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2312, in _flush
        transaction.rollback(_capture_exception=True)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
        compat.reraise(exc_type, exc_value, exc_tb)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2276, in _flush
        flush_context.execute()
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 389, in execute
        rec.execute(self)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 548, in execute
        uow
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 181, in save_obj
        mapper, table, insert)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 835, in _emit_insert_statements
        execute(statement, params)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 945, in execute
        return meth(self, multiparams, params)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection
        return connection._execute_clauseelement(self, multiparams, params)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
        compiled_sql, distilled_params
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context
        context)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1402, in _handle_dbapi_exception
        exc_info
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 203, in raise_from_cause
        reraise(type(exception), exception, tb=exc_tb, cause=cause)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
        context)
      File "/home/henry/dev/flask_henry/venv_flask_henry/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 470, in do_execute
        cursor.execute(statement, parameters)
    OperationalError: (sqlite3.OperationalError) no such table: users [SQL: u'INSERT INTO users (email, username, role_id, password_hash) VALUES (?, ?, ?, ?)'] [parameters: ('john@example.com', 'john', None, 'pbkdf2:sha256:50000$LCWwNeCk$aeb7ff6a9000bc72cb48378d6d0b495be60015fe49f9edd6bbe6df6642584137')]

    Google搜到的:
    “还没有执行数据库初始化,没有创建表吧?”

    解决办法:

    $ python manage.py shell
    >>> db.create_all()

    (参考《Flask Web开发》5.8.1 创建表,P50

    参考文献:
    1.db插入不了数据
    2.《Flask Web开发》5.8.1 创建表,P50

    展开全文
  • 现在,ORM框架、Web框架和配置都已就绪,我们可以开始编写一个最简单的MVC,把它们全部启动起来。 通过Web框架的@decorator和ORM框架的Model支持,可以很容易地编写一个处理首页URL的函数: # urls.py from ...
  • Windows下安装Web.py快速指南

    千次阅读 2017-07-19 14:04:55
    - Python 3.6Web.py介绍web.py是Python在Web领域一个轻量级的解决方案,所谓轻量,就是非常简单快速满足Web页面开发的需求。 首先来感受一下其轻量的程度吧:import weburls = ( '/(.*)', 'hello' )class hello: ...

    环境介绍

    首先介绍下安装使用的环境
    - Windows 7
    - Python 3.6

    Web.py介绍

    web.py是Python在Web领域一个轻量级的解决方案,所谓轻量,就是非常简单快速满足Web页面开发的需求。
    首先来感受一下其轻量的程度吧:

    import web
    
    urls = (
        '/(.*)', 'hello'
    )
    
    class hello:
        def GET(self, name):
            i = web.input(times=1)
            if not name: name = 'world'
            for c in xrange(int(i.times)): print 'Hello,', name+'!'
    
    if __name__ == "__main__": web.run(urls, globals())

    综合来看,是不是非常的轻量….

    安装指南

    目前默认的web.py版本是0.38, 只要是针对python2而打包的,针对Python3是无法通过pip3的方式来直接安装的:
    在安装之前,需要提前安装2个依赖包:

    pip3 install db
    pip install utils

    直接安装web.py会报出若干错误信息:

    C:\Users\xx>pip3 install web.py
    Collecting web.py
    Using cached web.py-0.38.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
    File “”, line 1, in
    File “C:\Users\CHENJU~1\AppData\Local\Temp\pip-build-vxm84t4s\web.py\setup.py”, line 6, in
    from web import version
    File “C:\Users\CHENJU~1\AppData\Local\Temp\pip-build-vxm84t4s\web.py\web__init__.py”, line 14, in
    import utils, db, net, wsgi, http, webapi, httpserver, debugerror
    File “d:\program files\python\lib\site-packages\db__init__.py”, line 69
    print “var”, var
    ^
    SyntaxError: Missing parentheses in call to ‘print’

    ----------------------------------------
    

    Command “python setup.py egg_info” failed with error code 1 in C:\Users\xx~1\AppData\Local\Temp\pip-build-vxm84t
    从错误信息来看,其错误为print在不同的Python版本下的语法上的差异造成的。

    所以,我们直接从github上下载源代码,进行编译安装。
    切换到python3的分支上,直接下载器源代码:
    打开其地址: https://github.com/webpy/webpy/tree/python3

    git clone https://github.com/webpy/webpy.git

    待下载完成之后,进入下载目录,进行安装:

    python setup.py install

    结果信息:

    D:\OpenSource\webpy>python setup.py install
    D:\Program Files\python\lib\site-packages\setuptools\dist.py:331: UserWarning: Normalizing ‘0.40-dev0’ to ‘0.40.dev0’
    normalized_version,
    running install
    running bdist_egg
    running egg_info
    creating web.py.egg-info
    writing web.py.egg-info\PKG-INFO
    writing dependency_links to web.py.egg-info\dependency_links.txt
    writing top-level names to web.py.egg-info\top_level.txt
    writing manifest file ‘web.py.egg-info\SOURCES.txt’
    reading manifest file ‘web.py.egg-info\SOURCES.txt’
    writing manifest file ‘web.py.egg-info\SOURCES.txt’
    installing library code to build\bdist.win-amd64\egg
    running install_lib
    running build_py
    creating build
    creating build\lib
    creating build\lib\web
    copying web\application.py -> build\lib\web
    copying web\browser.py -> build\lib\web
    copying web\db.py -> build\lib\web
    copying web\debugerror.py -> build\lib\web
    copying web\form.py -> build\lib\web
    copying web\http.py -> build\lib\web
    copying web\httpserver.py -> build\lib\web
    copying web\net.py -> build\lib\web
    copying web\py3helpers.py -> build\lib\web
    copying web\session.py -> build\lib\web
    copying web\template.py -> build\lib\web
    copying web\test.py -> build\lib\web
    copying web\utils.py -> build\lib\web
    copying web\webapi.py -> build\lib\web
    copying web\webopenid.py -> build\lib\web
    copying web\wsgi.py -> build\lib\web
    copying web__init__.py -> build\lib\web
    creating build\lib\web\wsgiserver
    copying web\wsgiserver\ssl_builtin.py -> build\lib\web\wsgiserver
    copying web\wsgiserver\ssl_pyopenssl.py -> build\lib\web\wsgiserver
    copying web\wsgiserver\wsgiserver2.py -> build\lib\web\wsgiserver
    copying web\wsgiserver\wsgiserver3.py -> build\lib\web\wsgiserver
    copying web\wsgiserver__init__.py -> build\lib\web\wsgiserver
    creating build\lib\web\contrib
    copying web\contrib\template.py -> build\lib\web\contrib
    copying web\contrib__init__.py -> build\lib\web\contrib
    creating build\bdist.win-amd64
    creating build\bdist.win-amd64\egg
    creating build\bdist.win-amd64\egg\web
    copying build\lib\web\application.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\browser.py -> build\bdist.win-amd64\egg\web
    creating build\bdist.win-amd64\egg\web\contrib
    copying build\lib\web\contrib\template.py -> build\bdist.win-amd64\egg\web\contrib
    copying build\lib\web\contrib__init__.py -> build\bdist.win-amd64\egg\web\contrib
    copying build\lib\web\db.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\debugerror.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\form.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\http.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\httpserver.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\net.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\py3helpers.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\session.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\template.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\test.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\utils.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\webapi.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\webopenid.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\wsgi.py -> build\bdist.win-amd64\egg\web
    creating build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\ssl_builtin.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\ssl_pyopenssl.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\wsgiserver2.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\wsgiserver3.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver__init__.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web__init__.py -> build\bdist.win-amd64\egg\web
    byte-compiling build\bdist.win-amd64\egg\web\application.py to application.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\browser.py to browser.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\contrib\template.py to template.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\contrib__init__.py to init.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\db.py to db.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\debugerror.py to debugerror.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\form.py to form.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\http.py to http.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\httpserver.py to httpserver.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\net.py to net.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\py3helpers.py to py3helpers.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\session.py to session.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\template.py to template.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\test.py to test.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\utils.py to utils.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\webapi.py to webapi.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\webopenid.py to webopenid.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgi.py to wsgi.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\ssl_builtin.py to ssl_builtin.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\ssl_pyopenssl.py to ssl_pyopenssl.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\wsgiserver2.py to wsgiserver2.cpython-36.pyc
    File “build\bdist.win-amd64\egg\web\wsgiserver\wsgiserver2.py”, line 1019
    except socket.error, e:
    ^
    SyntaxError: invalid syntax

    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\wsgiserver3.py to wsgiserver3.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver__init__.py to init.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web__init__.py to init.cpython-36.pyc
    creating build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\PKG-INFO -> build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\SOURCES.txt -> build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\dependency_links.txt -> build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\top_level.txt -> build\bdist.win-amd64\egg\EGG-INFO
    zip_safe flag not set; analyzing archive contents…
    web.pycache.application.cpython-36: module references file
    web.pycache.debugerror.cpython-36: module references file
    creating dist
    creating ‘dist\web.py-0.40.dev0-py3.6.egg’ and adding ‘build\bdist.win-amd64\egg’ to it
    removing ‘build\bdist.win-amd64\egg’ (and everything under it)
    Processing web.py-0.40.dev0-py3.6.egg
    creating d:\program files\python\lib\site-packages\web.py-0.40.dev0-py3.6.egg
    Extracting web.py-0.40.dev0-py3.6.egg to d:\program files\python\lib\site-packages
    File “d:\program files\python\lib\site-packages\web.py-0.40.dev0-py3.6.egg\web\wsgiserver\wsgiserver2.py”, line 1019
    except socket.error, e:
    ^
    SyntaxError: invalid syntax

    Adding web.py 0.40.dev0 to easy-install.pth file

    Installed d:\program files\python\lib\site-packages\web.py-0.40.dev0-py3.6.egg
    Processing dependencies for web.py==0.40.dev0
    Finished processing dependencies for web.py==0.40.dev0

    从结果信息来看,其中依然存在若干错误信息,对于Python3而言,这些都是在python2下的语法在Python 3下无法被正确解析的问题,估计在后续版本,可以将python2的内容在python 3的分支上逐步更新。

    目前的这个分支还是dev分支,存在若干问题还是可以理解的。

    总结

    web.py的这位作者已经仙逝,继任者还需努力,继续更新呀,从github上最后一次提交是在2017年初,不知道是否绝版了……

    展开全文
  • Extracting Keywords of Web Users' Interests and Visualizing their Routine Visits.pdf
  • This paper mashes up Web Information Fusion and FOAF (Friend of a Friend) to design a belief reasoning recommendation system for user service. Belief reasoning recommendation user service takes into ...
  • 1.web.py 简介web.py是一个轻量级的python web框架,简单而且功能强大。相对flask和Django,web.py更适合初学者来学习和了解web开发的基础知识。2.web.py的安装不用说,前提条件是安装了python环境。官方网站:...
     1.web.py 简介
    
    web.py是一个轻量级的python web框架,简单而且功能强大。相对flask和Django,web.py更适合初学者来学习和了解web开发的基础知识。


    2.web.py的安装
    不用说,前提条件是安装了python环境。官方网站:http://webpy.org/, 可以看看文档,介绍如何安装,和入门教程等资料。


    3. Windows下安装环境
    python 3.6
    从网站:https://github.com/9073204qq/webpy 下载源码


    pip install db
    pip install utils


    到下载源码的目录:
    python setup.py install

    运行这个命令行,会输出如下:

    D:\AI\python\web\webpy>python setup.py install
    C:\Users\AppData\Local\Programs\Python\Python36\lib\site-packages\setuptools\dist.py:355: UserWarning: Normalizing '0.40-dev0' to '0.40.dev0'
      normalized_version,
    running install
    running bdist_egg
    running egg_info
    writing web.py.egg-info\PKG-INFO
    writing dependency_links to web.py.egg-info\dependency_links.txt
    writing top-level names to web.py.egg-info\top_level.txt
    reading manifest file 'web.py.egg-info\SOURCES.txt'
    writing manifest file 'web.py.egg-info\SOURCES.txt'
    installing library code to build\bdist.win-amd64\egg
    running install_lib
    running build_py
    copying web\wsgiserver\wsgiserver2.py -> build\lib\web\wsgiserver
    creating build\bdist.win-amd64\egg
    creating build\bdist.win-amd64\egg\web
    copying build\lib\web\application.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\browser.py -> build\bdist.win-amd64\egg\web
    creating build\bdist.win-amd64\egg\web\contrib
    copying build\lib\web\contrib\template.py -> build\bdist.win-amd64\egg\web\contrib
    copying build\lib\web\contrib\__init__.py -> build\bdist.win-amd64\egg\web\contrib
    copying build\lib\web\db.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\debugerror.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\form.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\http.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\httpserver.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\net.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\py3helpers.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\session.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\template.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\test.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\utils.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\webapi.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\webopenid.py -> build\bdist.win-amd64\egg\web
    copying build\lib\web\wsgi.py -> build\bdist.win-amd64\egg\web
    creating build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\ssl_builtin.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\ssl_pyopenssl.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\wsgiserver2.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\wsgiserver3.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\wsgiserver\__init__.py -> build\bdist.win-amd64\egg\web\wsgiserver
    copying build\lib\web\__init__.py -> build\bdist.win-amd64\egg\web
    byte-compiling build\bdist.win-amd64\egg\web\application.py to application.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\browser.py to browser.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\contrib\template.py to template.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\contrib\__init__.py to __init__.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\db.py to db.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\debugerror.py to debugerror.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\form.py to form.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\http.py to http.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\httpserver.py to httpserver.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\net.py to net.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\py3helpers.py to py3helpers.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\session.py to session.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\template.py to template.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\test.py to test.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\utils.py to utils.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\webapi.py to webapi.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\webopenid.py to webopenid.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgi.py to wsgi.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\ssl_builtin.py to ssl_builtin.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\ssl_pyopenssl.py to ssl_pyopenssl.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\wsgiserver2.py to wsgiserver2.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\wsgiserver3.py to wsgiserver3.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\wsgiserver\__init__.py to __init__.cpython-36.pyc
    byte-compiling build\bdist.win-amd64\egg\web\__init__.py to __init__.cpython-36.pyc
    creating build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\PKG-INFO -> build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\SOURCES.txt -> build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\dependency_links.txt -> build\bdist.win-amd64\egg\EGG-INFO
    copying web.py.egg-info\top_level.txt -> build\bdist.win-amd64\egg\EGG-INFO
    zip_safe flag not set; analyzing archive contents...
    web.__pycache__.application.cpython-36: module references __file__
    web.__pycache__.debugerror.cpython-36: module references __file__
    creating 'dist\web.py-0.40.dev0-py3.6.egg' and adding 'build\bdist.win-amd64\egg' to it
    removing 'build\bdist.win-amd64\egg' (and everything under it)
    Processing web.py-0.40.dev0-py3.6.egg
    removing 'c:\users\appdata\local\programs\python\python36\lib\site-packages\web.py-0.40.dev0-py3.6.egg' (and everything under it)
    creating c:\users\appdata\local\programs\python\python36\lib\site-packages\web.py-0.40.dev0-py3.6.egg
    Extracting web.py-0.40.dev0-py3.6.egg to c:\users\appdata\local\programs\python\python36\lib\site-packages
    web.py 0.40.dev0 is already the active version in easy-install.pth


    Installed c:\users\appdata\local\programs\python\python36\lib\site-packages\web.py-0.40.dev0-py3.6.egg
    Processing dependencies for web.py==0.40.dev0
    Finished processing dependencies for web.py==0.40.dev0


    4.测试安装是否正常

    创建测试代码:

    import web
    
    urls = (
        '/(.*)', 'hello'
    )
    
    app = web.application(urls, globals())
    
    class hello:
        def GET(self, name):
            if not name:
                name = 'World'
            return 'Hello, ' + name + '!'
        
    
    if __name__ == "__main__":
        app.run()
    结果输出如下:

    比特币源码入门教程

    https://edu.csdn.net/course/detail/6998

    深入浅出Matplotlib
    https://edu.csdn.net/course/detail/6859

    深入浅出Numpy
    http://edu.csdn.net/course/detail/6149 

    Python游戏开发入门

    你也能动手修改C编译器

    纸牌游戏开发

    http://edu.csdn.net/course/detail/5538 

    五子棋游戏开发

    http://edu.csdn.net/course/detail/5487
    RPG游戏从入门到精通
    http://edu.csdn.net/course/detail/5246




    展开全文
  • Applying Web Usage Mining Techniques to Discover Potential Browsing Problems of Users
  • PythonWeb:UNIQUE constraint failed: users.id

    千次阅读 2019-04-01 16:20:14
    pythonweb报错 :UNIQUE constraint failed: users.id 解释:数据库中已存在待插入的数据(id冲突)
  • Traceback (most recent call last):

    万次阅读 2019-05-12 14:13:15
    |Traceback (most recent call last) EOFError: EOF when reading a line Traceback (most recent call last): File "C:\Users\31368\Desktop\post-1.py", line 24, in <module> response=urllib.reques...
  • 什么是web框架
  • BitTorrent_webui.zip

    2014-04-11 22:12:41
    放到BT目录下,如:C:\Users\Administrator\AppData\Roaming\BitTorrent\webui.zip 启动webui即可在浏览器中通过http://IP:port/gui方式访问BT
  • ArcGIS Web Development--2014

    2018-05-03 09:11:46
    What’s Inside Build web-based GIS applications Customize the ArcGIS Javascript API tools Bring ArcGIS data to the web Create secure logins for mobile app users
  • web常见安全漏洞

    万次阅读 2018-09-08 10:04:25
    随着Web2.0、网络社交等一系列新型的互联网产品的诞生,基于Web环境的互联网应用越来越广泛,企业信息化的过程中,越来越多的应用都架设在Web平台上。Web业务的迅速发展吸引了黑客们的强烈关注,接踵而至的就是Web...
  • 使用java版本的web3j生成公私钥等信息 web3j的java版本支持直接通过java代码生成以太坊钱包的公私钥地址等信息,生成的地址信息存放于本地文件当中。同时,提供了针对该文件的读取等操作。 实例说明 创建maven项目并...
  • Android以太坊钱包全部功能-基于web3j实现

    千次阅读 热门讨论 2018-09-19 22:42:20
    文章目录需要用到的工具GanacheMetamask钱包功能的具体实现引入依赖创建钱包第一种创建...这段时间学习了利用web3j来实现Android以太坊钱包的功能。所以这里做一些相关记录,方便以后查看。 需要用到的工具 用工具...
  • 写论文要用到web of science,写下这篇笔记。 正常情况下打开网页,显示结果如下 首先想到去tao宝买一个账号,果然 选择“wos(JCR ESI德文特)”,对应的就是web of science的账号 然后客服会发给你一个注册码...
  • Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。主要作用是:做一些初始化的内容添加工作、...
  • 背景: INFO Starting development server… 10% building 2/2 modules ... i 「wds」: webpack output is served from / i 「wds」: Content not from webpack is served from C:\Users\Administrator\Webstor..
  • volunteers_web-源码

    2021-06-18 05:47:41
    GET /api/users 描述 - 以 JSON 数组的形式检索所有用户。 HTTP 方法 - GET URL 路径 - /api/users POST /api/users 描述 - 添加一个用户并返回添加的带有 _id 键的用户。 HTTP 方法 - POST URL 路径 - /api/...
  • web.xml web.xml是Tomcat的部署描述符文件,配置文件 指定路径用的 web.xml文件是用来配置:欢迎页、servlet、filter等的。当你的web工程没用到这些时,你可以不用web.xml文件来配置你的web工程 web.xml的...
  • #jaws-users-crud-ddb-jwt-js 包含一组执行用户操作的lambda的jaws-module 。 用途: AWS Lambda Java脚本 DynamoDB JSON Web令牌

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 273,084
精华内容 109,233
关键字:

usersweb