基于epoll的简单的http服务器

本人用epoll写了一个简单的http服务器,该服务器在客户端第一次发送数据时可以正确处理,但是当客户端不关闭继续发送数据时,服务器无法读取,请求大家帮忙看看哪里有问题,谢谢

</pre></p><p>server.h</p><p><pre name="code" class="cpp">/*
 * server.h
 *
 *  Created on: Jun 23, 2014
 *      Author: fangjian
 */
#include <netinet/in.h>

#ifndef SERVER_H_
#define SERVER_H_

#define QUERY_INIT_LEN  10
#define REMAIN_BUFFER 5
/* 以下是处理机的状态 */
#define ACCEPT 1
#define READ 2
#define QUERY_LINE 4
#define QUERY_HEAD 8
#define QUERY_BODY 16
#define SEND_DATA 32

struct connection
{
	int fd;
	struct sockaddr_in client_address;
	int state;//当前处理到哪个阶段

	char* querybuf;
	int query_start_index;//请求数据的当前指针
	int query_end_index;//请求数据的下一个位置
	int query_remain_len;//可用空间

	char method[8];
	char uri[128];
	char version[16];

	char host[128];
	char accept[128];
	char conn[20];

};

struct server
{
	int epollfd;
};

void web_epoll_ctl(int epollfd,int ctl,int fd,int flag);
int setnonblocking(int fd);
struct connection* initConnection(int fd);
void state_machine(struct connection& conn);
void web_accept(struct connection& conn);
void read_request(struct connection& conn);
void process_request_line(struct connection& conn);
void process_head(struct connection& conn);
void process_body(struct connection& conn);
void send_response(struct connection& conn);
void try_to_enlarge_buffer(struct connection& conn);
void close_connection(int fd);

#endif /* SERVER_H_ */

server.cpp:

/*
 * server.cpp
 *
 *  Created on: Jun 23, 2014
 *      Author: fangjian
 */

#include "server.h"

#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include<signal.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <iostream>
#include <map>
using namespace std;

#define MAX_EVENT_NUMBER 10000

map<int,connection> map_conn;
struct server server;
int main(int argc,char* argv[])
{
	const char* ip = "172.16.55.67";
	int port =  8083;
	signal(SIGPIPE,SIG_IGN);

	int listenfd = socket(AF_INET,SOCK_STREAM,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);

	bind(listenfd,(struct sockaddr*)&address,sizeof(address));
	listen(listenfd,50);

	epoll_event events[MAX_EVENT_NUMBER];
	server.epollfd = epoll_create(1024);
	web_epoll_ctl(server.epollfd,EPOLL_CTL_ADD,listenfd,EPOLLIN);//添加读连接事件
	setnonblocking(listenfd);

	//fork();

	while(true)
	{
		int number = epoll_wait(server.epollfd,events,MAX_EVENT_NUMBER,-1);
		printf("number=%d\n",number);
		printf("当前进程ID为: %d \n",getpid());

		int i;
		for(i = 0;i < number;i++)
		{
			int socket = events[i].data.fd;//当前触发的fd

			//有新连接到达
			if(socket == listenfd)
			{
				printf("有新连接到达\n");

				//创建一个连接结构体
				struct connection* conn = initConnection(socket);

				//进入状态机处理请求
				state_machine(*conn);
			}
			//读事件到达
			else if(events[i].events & EPOLLIN)
			{
				printf("--------开始处理请求行------------\n");
				state_machine(map_conn[socket]);
				printf("--------处理请求行结束--------------\n");//sleep(2);

				web_epoll_ctl(server.epollfd,EPOLL_CTL_MOD,socket,EPOLLIN);
			}
			//有写事件到达
			else if(events[i].events & EPOLLOUT)
			{
				printf("--------开始发送数据--------------\n");
				state_machine(map_conn[socket]);
				printf("---------发送数据结束--------------\n");
			}
			//异常
			else
			{

			}
		}
	}
}
void state_machine(struct connection& conn)
{
	switch (conn.state)
	{
	case ACCEPT:{web_accept(conn);break;}
	case READ:{read_request(conn);break;}
	case QUERY_LINE:{process_request_line(conn);break;}
	case SEND_DATA:{send_response(conn);break;}
	}
}
/* 调用epoll_ctl处理 */
void web_epoll_ctl(int epollfd,int ctl,int fd,int flag)
{
	epoll_event event;
	event.data.fd =fd;
	event.events = flag;
	epoll_ctl(epollfd,ctl,fd,&event);
}
int setnonblocking(int fd)
{
	int old_option = fcntl(fd,F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd,F_SETFL,new_option);
	return old_option;
}
struct connection* initConnection(int fd)
{
	struct connection* conn  = (struct connection*)malloc(sizeof(struct connection));
	conn->fd = fd;
	conn->state = ACCEPT;

	conn->querybuf = (char*)malloc(QUERY_INIT_LEN);
	if(!conn->querybuf)
	{
		printf(" malloc error\n");
		return NULL;
	}
	conn->query_start_index = 0;
	conn->query_end_index = 0;
	conn->query_remain_len = QUERY_INIT_LEN;

	return conn;
}
void web_accept(struct connection& conn)
{
	socklen_t client_addrlength = sizeof(conn.client_address);
	int connfd = accept(conn.fd,(struct sockaddr*)&(conn.client_address),&client_addrlength);
	if(connfd == -1)
	{
		printf("accept error\n");
		return;
	}
	web_epoll_ctl(server.epollfd,EPOLL_CTL_DEL,conn.fd,EPOLLIN);//删除监听事件
	close(conn.fd);//关闭监听描述符,因为keep_alive是保持连接描述符不关闭
	conn.fd = connfd;
	conn.state = READ;
	map_conn[connfd] = conn;
	web_epoll_ctl(server.epollfd,EPOLL_CTL_ADD,connfd,EPOLLIN);
	setnonblocking(connfd);
}
void read_request(struct connection& conn)
{
	int len,fd = conn.fd;
	while(true)
	{
		/* 尝试增加缓冲区空间 */
		try_to_enlarge_buffer(conn);

		len= read(fd,conn.querybuf+conn.query_end_index,conn.query_remain_len);
		if(len == -1)
		{
			printf("----数据读完-----\n");
			conn.state = QUERY_LINE;//进入解析阶段
			web_epoll_ctl(server.epollfd,EPOLL_CTL_DEL,conn.fd,EPOLLIN);//删除该连接上的读事件
			break;
		}
		else if(len == 0)
		{
			printf("----客户端关闭连接------\n");
			conn.state = QUERY_LINE;//进入解析阶段
			web_epoll_ctl(server.epollfd,EPOLL_CTL_DEL,conn.fd,EPOLLIN);//删除该连接上的读事件
			break;
		}
		else if(len > 0)
		{
			conn.query_end_index += len;
			conn.query_remain_len -= len;
		}
	}
	cout << "-----客户端的内容是 " << endl;

	cout << conn.querybuf << endl;

	process_request_line(conn);
}
void process_request_line(struct connection& conn)
{
	int len;
	char* ptr = strpbrk(conn.querybuf + conn.query_start_index," \t");
	if( !ptr)
	{
		printf("请求行解析失败\n");
		return;
	}
	len = ptr - conn.querybuf - conn.query_start_index;
	strncpy(conn.method,conn.querybuf + conn.query_start_index,len);
	cout <<"metnod="<<conn.method<<endl;

	conn.query_start_index += (len+1);
	ptr = strpbrk(conn.querybuf + conn.query_start_index," \t");
	if( !ptr)
	{
		printf("请求行解析失败\n");
		return;
	}
	len = ptr - conn.querybuf - conn.query_start_index;
	strncpy(conn.uri,conn.querybuf + conn.query_start_index,len);
	cout << "uri="<<conn.uri<<endl;

	conn.query_start_index += (len+1);
	ptr = strpbrk(conn.querybuf,"\n");//先是回车\r,再是换行\n
	if(!ptr)
	{
		printf("请求行解析失败\n");
		return;
	}
	len = ptr - conn.querybuf - conn.query_start_index;
	strncpy(conn.version,conn.querybuf + conn.query_start_index,len);
	cout << "version="<<conn.version<<endl;
	conn.query_start_index += (len+1);

	cout <<"-----请求行解析完毕----------"<<endl;

	process_head(conn);
}
void process_head(struct connection& conn)
{
	cout << "-------开始解析首部------" << endl;

	char* end_line;
	int len;

	while(true)
	{
		end_line = strpbrk(conn.querybuf + conn.query_start_index,"\n");
		len = end_line - conn.querybuf - conn.query_start_index;
		if(len == 1)
		{
			printf("解析完毕\n");
			conn.query_start_index += (len +1);
			cout << conn.querybuf + conn.query_start_index << endl;
			break;
		}
		else
		{
			if(strncasecmp(conn.querybuf+conn.query_start_index,"Host:",5) == 0)
			{
				strncpy(conn.host,conn.querybuf+conn.query_start_index + 6,len-6);
				cout << "host="<<conn.host<<endl;
			}
			else if(strncasecmp(conn.querybuf+conn.query_start_index,"Accept:",7) == 0)
			{
				strncpy(conn.accept,conn.querybuf+conn.query_start_index + 8,len-8);
				cout <<"accept="<<conn.accept <<endl;
			}
			else if(strncasecmp(conn.querybuf+conn.query_start_index,"Connection:",11) == 0)
			{
				strncpy(conn.conn,conn.querybuf+conn.query_start_index + 12,len-12);
				cout <<"connection="<<conn.conn <<endl;
			}
			else
			{
			}
			conn.query_start_index += (len +1);
		}
	}
	process_body(conn);
	printf("----首部解析完毕----------\n");
}
void process_body(struct connection& conn)
{
	if(conn.query_start_index == conn.query_end_index)
	{
		printf("---包体为空----\n");
	}
	else
	{
		printf("---丢体包体-----\n");
	}
	conn.query_start_index = conn.query_end_index = 0;
	send_response(conn);
}
void send_response(struct connection& conn)
{
	char path[128] = "http";//根目录下的文件夹
	int len = strlen(conn.uri);
	memcpy(path+4,conn.uri,len);
	len += 4;
	path[len] = '\0';//很重要

	int filefd = open(path,O_RDONLY);
	if(filefd < 0)
	{
		cout << "无法打开该文件" <<endl;
		return ;
	}
	struct stat stat_buf;
	fstat(filefd,&stat_buf);
	sendfile(conn.fd,filefd,NULL,stat_buf.st_size);
	close(filefd);

	//close(conn.fd);//如果不关闭该连接socket,则浏览器一直在加载,如何解决,保持keep-alive?

	//web_epoll_ctl(server.epollfd,EPOLL_CTL_MOD,conn.fd,EPOLLIN);
	conn.state = READ;
}
void close_connection(int fd)
{
	map_conn.erase(fd);
	close(fd);
}
void try_to_enlarge_buffer(struct connection& conn)
{
	if(conn.query_remain_len  < REMAIN_BUFFER)
	{
		int new_size = strlen(conn.querybuf) + QUERY_INIT_LEN;
		conn.querybuf = (char*)realloc(conn.querybuf,new_size);
		conn.query_remain_len  = new_size - conn.query_end_index;
	}
}

客户端代码:

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

static const char* request = "GET /index.html HTTP/1.1\r\nConnection: keep-alive\r\n\r\nxxxxxxxxxxxx";

int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

void addfd( int epoll_fd, int fd )
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLOUT | EPOLLET | EPOLLERR;
    epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event );
    setnonblocking( fd );
}

bool write_nbytes( int sockfd, const char* buffer, int len )
{
    int bytes_write = 0;
    printf( "write out %d bytes to socket %d\n", len, sockfd );
    while( 1 )
    {
        bytes_write = send( sockfd, buffer, len, 0 );
        if ( bytes_write == -1 )
        {
            return false;
        }
        else if ( bytes_write == 0 )
        {
            return false;
        }   

        len -= bytes_write;
        buffer = buffer + bytes_write;
        if ( len <= 0 )
        {
            return true;
        }
    }
}

bool read_once( int sockfd, char* buffer, int len )
{
    int bytes_read = 0;
    memset( buffer, '\0', len );
    bytes_read = recv( sockfd, buffer, len, 0 );
    if ( bytes_read == -1 )
    {
        return false;
    }
    else if ( bytes_read == 0 )
    {
        return false;
    }
	printf( "read in %d bytes from socket %d with content: %s\n", bytes_read, sockfd, buffer );

    return true;
}

void start_conn( int epoll_fd, int num, const char* ip, int port )
{
    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 );

    for ( int i = 0; i < num; ++i )
    {
        sleep( 1 );
        int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
        printf( "create 1 sock\n" );
        if( sockfd < 0 )
        {
            continue;
        }

        if (  connect( sockfd, ( struct sockaddr* )&address, sizeof( address ) ) == 0  )
        {
            printf( "build connection %d\n", i );
            addfd( epoll_fd, sockfd );
        }
    }
}

void close_conn( int epoll_fd, int sockfd )
{
    epoll_ctl( epoll_fd, EPOLL_CTL_DEL, sockfd, 0 );
    close( sockfd );
}

int main( int argc, char* argv[] )
{
    assert( argc == 4 );
    int epoll_fd = epoll_create( 100 );
    start_conn( epoll_fd, atoi( argv[ 3 ] ), argv[1], atoi( argv[2] ) );
    epoll_event events[ 10000 ];
    char buffer[ 2048 ];
    while ( 1 )
    {
        int fds = epoll_wait( epoll_fd, events, 10000, 2000 );
        for ( int i = 0; i < fds; i++ )
        {
            int sockfd = events[i].data.fd;
            if ( events[i].events & EPOLLIN )
            { printf("----服务器发来数据-----\n");
                if ( ! read_once( sockfd, buffer, 2048 ) )
                {
                    close_conn( epoll_fd, sockfd );
                }
                struct epoll_event event;
                event.events = EPOLLOUT | EPOLLET | EPOLLERR;
                event.data.fd = sockfd;
                epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event );
            }
            else if( events[i].events & EPOLLOUT )
            {printf("---------向服务器发送数据-----------\n");
                if ( ! write_nbytes( sockfd, request, strlen( request ) ) )
                {
                    close_conn( epoll_fd, sockfd );
                }
				printf("--------数据发送完毕-------------\n");
                struct epoll_event event;
                event.events = EPOLLIN | EPOLLET | EPOLLERR;
                event.data.fd = sockfd;
                epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event );
            }
            else if( events[i].events & EPOLLERR )
            {
                close_conn( epoll_fd, sockfd );
            }
        }
    }
}

测试方法,在服务器项目中建一个名字为"http"的文件夹,里面放一个index.html静态文件,如:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

然后将服务器的IP改为自己的IP,即可运行服务器;客户端直接运行 ./main IP 8083 1 即可,谢谢大家的帮忙

基于epoll的简单的http服务器

时间: 2024-10-28 15:12:05

基于epoll的简单的http服务器的相关文章

基于epoll的简单服务器

一.epoll 1.epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用 (1)int epoll_create(int size); 创建一个epoll的句柄.自从linux2.6.8之后,size参数是被忽略的.需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽. (2)int epoll

一个基于VB的简单IRC机器人服务器 - GeneBot &amp; EvilBot

(版权提醒:此软件遵守GPLv3协议开放源码,任何引用.使用.传播.修改该软件的程序或工程须在其发行版后附加其全部源代码) GeneBot是我在使用IRC聊天的时候的设想.现在实现了. 之前用python的irc框架sopel写过一个机器人,但在windows上运行诸多不便. VB看似被众多“大佬”嘲笑,但是简单的windows图形界面快速编程远胜过qt. 下面贴几张图: 效果图(还有很多小彩蛋哦): 实现原理:VB下Winsock的TCP连接.host=irc.freenode.net:por

epoll高并发多路复用,基于epoll的高性能服务器

并发测试工具ab使用 linux命令安装这个工具:apt-get install apache2 windows中装好apache之后就会再带一个工具 windows命令使用方法 ab -n 200 -c 5 http://www.baidu.com/ 1000就是测试的数量 -c 10 就是开启的线程数 测试的地址 反回了一些测试信息,如 使用时间,每次要多久等信息. linux也是一样用的. epoll多路复用IO 高并发 epoll多路复用是专门用来处理高并发的,在linux多路复用中有多

基于EPOLL模型的局域网聊天室和Echo服务器

一.EPOLL的优点 在Linux中,select/poll/epoll是I/O多路复用的三种方式,epoll是Linux系统上独有的高效率I/O多路复用方式,区别于select/poll.先说select/poll的缺点,以体现epoll的优点. select: (1)可监听的socket受到限制,在32位的系统中,默认最大值为1024. (2)采用轮询方式,当要监听的sock数量很大时,效率低. (3)随着要监听socket数据的增加,要维护一个存放大量fd的数据结构,系统开销太大. pol

基于Java实现简单Http服务器(转)

基于Java实现简单Http服务器(转) 本文将详细介绍如何基于java语言实现一个简单的Http服务器,文中将主要介绍三个方面的内容:1)Http协议的基本知识.2)java.net.Socket类.3)java.net.ServerSocket类,读完本文后你可以把这个服务器用多线程的技术重新编写一个更好的服务器.           由于Web服务器使用Http协议通信的因此也把它叫做Http服务器,Http使用可靠的TCP连接来工作,它是面向连接的通信方式,这意味着客户端和服务器每次通信

Mina、Netty、Twisted一起学:实现简单的TCP服务器

MINA.Netty.Twisted为什么放在一起学习?首先,不妨先看一下他们官方网站对其的介绍: MINA: Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract event-driven asynchronous API

Mina、Netty、Twisted一起学(一):实现简单的TCP服务器

MINA.Netty.Twisted为什么放在一起学习?首先,不妨先看一下他们官方网站对其的介绍: MINA: Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract event-driven asynchronous API

自己动手模拟开发一个简单的Web服务器

开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的Web服务器来体会一下. 一.请求-处理-响应模型 1.1 基本过程介绍 每一个HTTP请求都会经历三个步凑:请求-处理-响应:每当我们在浏览器中输入一个URL时都会被封装为一个HTTP请求报文发送到Web服务器,而Web服务器则接收并解析HTTP请求报文,然后针对请求进行处理(返回指定的HTML页面

http协议的无状态性及基于memcached-session-manager构建tomcat cluster会话服务器

一.http协议的无状态性 1.HTTP协议一共有五大特点 1)支持客户/服务器模式 2)简单快速 当客户端向服务器端发送请求时,只是简单的填写请求路径和请求方法即可,然后就可以通过浏览器或其他方式将该请求发送就行了.比较常用的请求方法有三种,分别是:GET.HEAD.POST.不同的请求方法使得客户端和服务器端联系的方式各不相同.因为HTTP协议比较简单,所以HTTP服务器的程序规模相对比较小,从而使得通信的速度非常快. 3)灵活 Http协议允许客户端和服务器端传输任意类型任意格式的数据对象