• 功能:目前只支持对资源的访问.

  • 使用的模型:多线程加epoll,与传统的一个连接请求一个线程处理不同的是,这个模型只为那些需要服务的连接请求调用线程进行处理,

  • 整个模型的大致流程

    • 创建一个线程持对象,将每一个线程池设为脱离线程,这样,在线程结束后,可以自动回收资源,每一个调用线程都在等一个信号,这个线程池有一个工作队列,每往里面加一个连接请求,就让信号量 加一,得到这个信号量的线程就开始处理连接请求。
    • 创建一个epoll句柄,先将server socket fd加入到epoll中,进行监听,当server socket fd处于可读状态时,表明有一个连接请求过来(或者是已经连接,然后发了一个请求报文),就调用accept,获取连接的client的fd,将它也加入epoll中,然后初始化一个连接对象。
    • 监听epoll中的每一个文件描述符。如果client fd可读(处于EPOLLIN状态),表明客户端发送了一个请求报文,就将这个请求加入到线程池对象的工作队列中,等待处理。
    • 如果client fd 可写(处于EPOLLOUT状态),就继续发送数据,(一般处于这个状态,是应为发送的数据大于内核中的写缓冲区,需要分多次发送,所以会有EPOLLOUT这个状态)。

      +如果client fd处于异常状态,就关闭连接。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <cassert>
#include <sys/epoll.h>
#include <semaphore.h>
#include "locker.h"
#include "threadpool.h"
#include "http_conn.h"
#define MAX_FD 65536
#define MAX_EVENT_NUMBER 10000 extern int addfd(int epollfd,int fd,bool one_shot);
extern int removefd(int epollfd,int fd); void addsig( int sig, void( handler )(int), bool restart = true )
{
struct sigaction sa;
memset( &sa, '\0', sizeof( sa ) );
sa.sa_handler = handler;
if( restart )
{
sa.sa_flags |= SA_RESTART;
}
sigfillset( &sa.sa_mask );
assert( sigaction( sig, &sa, NULL ) != -1 );
} void show_error( int connfd, const char* info )
{
printf( "%s", info );
send( connfd, info, strlen( info ), 0 ); //向socket中写入出错信息
close( connfd );
} int main( int argc, char* argv[] )
{
if( argc <= 2 )
{
printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] ); addsig( SIGPIPE, SIG_IGN ); //忽略SIGPIPE信号 threadpool< http_conn >* pool = NULL;
try
{
pool = new threadpool< http_conn >;//创建一个线程池对象
}
catch( ... )
{
return 1;
} http_conn* users = new http_conn[ MAX_FD ]; //定义用户的连接数量
assert( users );
int user_count = 0; int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
assert( listenfd >= 0 );
struct linger tmp = { 1, 0 };//close()立刻返回,但不会发送未发送完成的数据,而是通过一个REST包强制的关闭socket描述符,即强制退出。 setsockopt( listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof( tmp ) ); int ret = 0;
struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port ); ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret >= 0 ); ret = listen( listenfd, 5 );
assert( ret >= 0 ); epoll_event events[ MAX_EVENT_NUMBER ];
int epollfd = epoll_create( 5 );//参数只是起提示作用
assert( epollfd != -1 );
addfd( epollfd, listenfd, false );//把服务器端fd添加到epollfd中
http_conn::m_epollfd = epollfd;//把epoll文件句柄赋值给http_conn类中的静态变量m_epollfd while( true )
{
int number = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );//等待服务器端fd是否就绪
if ( ( number < 0 ) && ( errno != EINTR ) )
{
printf( "epoll failure\n" );
break;
} for ( int i = 0; i < number; i++ )
{
int sockfd = events[i].data.fd;
if( sockfd == listenfd )//如果服务器端fd就绪表明有人链接
{
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof( client_address );
int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );//获取客户端fd
if ( connfd < 0 )
{
printf( "errno is: %d\n", errno );
continue;
}
if( http_conn::m_user_count >= MAX_FD )//如果连接数以满,就拒绝连接
{
show_error( connfd, "Internal server busy" );
continue;
} users[connfd].init( connfd, client_address );//初始化一个连接。会将连接用户的fd添加到epoll_fd中
}
else if( events[i].events & ( EPOLLRDHUP | EPOLLHUP | EPOLLERR ) )//如果监听的fd中有人断开连接,就关闭这个链接
{
users[sockfd].close_conn();
}
else if( events[i].events & EPOLLIN ) //如果监听的fd可以读
{
if( users[sockfd].read() )
{
pool->append( users + sockfd );
}
else
{
users[sockfd].close_conn(); //如果读出错,就关闭连接
}
}
else if( events[i].events & EPOLLOUT ) //如果监听的fd可以写
{
if( !users[sockfd].write() )
{
users[sockfd].close_conn();
}
}
else
{}
}
} close( epollfd );
close( listenfd );
delete [] users;
delete pool;
return 0;
}

自己动手写http服务器——主程序(三)的更多相关文章

  1. 自己动手写RTP服务器——关于RTP协议

    转自:http://blog.csdn.net/baby313/article/details/7353605 本文会带领着你一步步动手实现一个简单的RTP传输服务器,旨在了解RTP流媒体传输协议以及 ...

  2. 自己动手写RTP服务器——传输所有格式的视频

    上一篇文章我们介绍了如何用一个简单的UDP socket搭建一个RTP服务器.我把这份80行的代码呈现到客户面前的时候,就有人不满意了. 还有人在参考的时候会问:“楼主你的TS格式的文件是哪里来的?应 ...

  3. 自己动手写RTP服务器——用RTP协议传输TS流

    上一篇文章我们介绍了关于RTP协议的知识,那么我们现在就自己写一个简单的传输TS流媒体的RTP服务器吧. 预备知识 关于TS流的格式:TS流封装的具体格式请参考文档ISO/IEC 13818-1.这里 ...

  4. 自己动手写http服务器——处理http连接(二)

    关于http报文格式请看这篇文章 //http_conn.h #ifndef HTTPCONNECTION_H #define HTTPCONNECTION_H #include <unistd ...

  5. 自己动手写http服务器——线程池(一)

    创建一个线程池,每有一个连接对象就将它添加到工作队列中,线程池中的线程通过竞争来取得任务并执行它(它是通过信号量实现的). //filename threadpool.h #ifndef THREAD ...

  6. 自己动手写了第三阶段的处理器——教学OpenMIPS处理器蓝图

    我们会继续上传新书<自己动手写处理器>(未公布).今天是第十条.我每星期试试4 从本章開始将一步一步地实现教学版OpenMIPS处理器.本章给出了教学版OpenMIPS的系统蓝图,首先介绍 ...

  7. Java自己动手写连接池三

    Java自己动手写连接池三,核心代码; package com.kama.cn; import java.sql.Connection;import java.util.ArrayList;impor ...

  8. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

  9. 【原创】自己动手写控件----XSmartNote控件

    一.前面的话 在上一篇博文自己动手写工具----XSmartNote [Beta 3.0]中,用到了若干个自定义控件,其中包含用于显示Note内容的简单的Label扩展控件,用于展示标签内容的labe ...

随机推荐

  1. 手工搭建ABP框架(1) - Web项目

    为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7295533.html ABP是 ASP.NET Boilerplate Proj ...

  2. SQLAlchemy复杂查询

    最近个人用python + flask搞了一个小项目,ORM用到的是SQLAlchemy.   SQLAlchemy的查询方式非常灵活,你所能想像到的复杂SQL 语句,基本上都可以实现.这里简单的总结 ...

  3. 技嘉 gigabyte b75m d3v 主板 定时开机无效问题解决

    BIOS 里面设置定时开机后发现到点并没有正常启动~~~  百思不得解.后来发现原来是WIN8系统下的控制面板的关机并非正常关机,而是不保存设置的非正常关机,在开始菜单右键——关闭或注销——关闭计算机 ...

  4. 修改oracle服务器端字符集

    ----设置字符集步聚------- conn /as sysdba; shutdown immediate; startup mount; alter system enable restricte ...

  5. CSS和文档

    1. 块级元素: p,div,ul,ol,h1,h2 . . . h6等.块级元素独占一行,旁边不能有其他元素. 2. 行内元素:span,a,strong,em等. display属性可以使块级元素 ...

  6. 我在学JavaScript中的循环

    for (var num1 = 1;num1 < 10;num1++ ){ for (var num2 = 1;num2< 10;num2++ ){ console.log(num1+'* ...

  7. Kotlin(二) 函数定义

    1.不带参数,不返回值的函数 fun sum(){} 2.带参数,不带返回值的函数 fun sum(a:Int){} 3.带参数,带返回值的函数 fun sum(a:Int,b:Int) : Int{ ...

  8. ORA-01745: 无效的主机/绑定变量名 ORA-00917: 缺失的逗号 oracle日期格式错误

    今天在oracle中执行插入语句的时候报了一个奇怪的错误,在程序中报的错误是ORA-01745: 无效的主机/绑定变量名,网上一查说是缺失逗号,在查询分析器执行的时候报缺失的逗号,仔细看了一下也没有缺 ...

  9. alex python of day2

      模块 sys模块:sys模块是用c语言写的,所以在lib下是不会有sys.py这个文件存在 1 import sys 2 print(sys.path) #打印环境变量 3 print(sys.a ...

  10. 巧学DBhelper

    这几天在教我很重要的人学习,她属于那种超级小白,很超级的那种. 教她的过程中 发现有的知识点 不管这么教都不会.DBhelper就是不知道怎么记. 当时我就想到 杰哥(程杰)的出的大话系列,和他写书的 ...