标签:http服务器 web server 服务器 c++
本人用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 );
}
}
}
}
<!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服务器,布布扣,bubuko.com
标签:http服务器 web server 服务器 c++
原文地址:http://blog.csdn.net/fangjian1204/article/details/34415651