基于epoll的简略的http服务器

基于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 即可,谢谢大家的帮忙