首页 > Web开发 > 详细

web服务器

时间:2016-02-24 22:51:41      阅读:628      评论:0      收藏:0      [点我收藏+]
// myserver_threadpool.h
#ifndef MYSERVER_THREADPOOL_H #define MYSERVER_THREADPOOL_H #include<list> #include<cstdio> #include<exception> #include<pthread.h> #include"locker.h" template<typename T> class myserver_threadpool { public: myserver_threadpool(int thread_number=0,int max_requests=10000); ~myserver_threadpool(); bool append(T* request); private: static void* worker(void* arg); void run(); private: int m_thread_number; int m_max_requests; pthread_t* m_threads; std::list<T* > m_workqueue; locker m_queuelocker; sem m_queuestat; bool m_stop; }; template<typename T> myserver_threadpool<T>::myserver_threadpool(int thread_number,int max_requests):m_thread_number(thread_number),m_max_requests(max_requests),m_stop(false),m_threads(NULL) { if((thread_number<=0)||(max_requests<=0)) { printf("error1\n"); throw std::exception(); } m_threads=new pthread_t[m_thread_number]; if(!m_threads) { printf("error2\n"); throw std::exception(); } for(int i=0;i<thread_number;i++) { printf("create the %dth thread\n",i); if(pthread_create(m_threads+i,NULL,worker,this)!=0) { delete [] m_threads; throw std::exception(); } if(pthread_detach(m_threads[i])) { delete[] m_threads; throw std::exception(); } } } template<typename T> myserver_threadpool<T>::~myserver_threadpool() { delete[] m_threads; m_stop=true; } template<typename T> bool myserver_threadpool<T>::append(T* request) { m_queuelocker.lock(); if(m_workqueue.size() > m_max_requests) { m_queuelocker.unlock(); return false; } m_workqueue.push_back(request); m_queuelocker.unlock(); m_queuestat.post(); printf("running append() successfully\n"); return true; } template<typename T> void* myserver_threadpool<T>::worker(void* arg) { myserver_threadpool* pool=(myserver_threadpool*)arg; printf("exec pool->run()\n"); pool->run(); printf("finish run()\n"); return pool; } template<typename T> void myserver_threadpool<T>::run() { while(!m_stop) { printf("the next running is wait()\n"); m_queuestat.wait(); printf("finish wait()\n"); m_queuelocker.lock(); if(m_workqueue.empty()) { m_queuelocker.unlock(); continue; } T* request = m_workqueue.front(); m_workqueue.pop_front(); m_queuelocker.unlock(); if(!request) { continue; } printf("the next running is process()\n"); request->process(); printf("finish process()\n"); } } #endif




// my_server.cpp

#include<unistd.h>
#include<signal.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<assert.h>
#include<string.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<stdarg.h>
#include<errno.h>
#include"service.h"
#include"myserver_threadpool.h"
#include"service.cpp"

#define MAX_FD 1024
extern void addfd(int epollfd,int fd,bool one_shot);
extern void modfd(int epollfd,int fd,int ev);
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);
close(connfd);
}

void close_conn(int epollfd,int fd)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0);
close(fd);
}

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);

myserver_threadpool<service>* pool=NULL;
pool=new myserver_threadpool<service>(3);

service* users=new service[MAX_FD];
assert(users);
int user_count=0;

int listenfd=socket(PF_INET,SOCK_STREAM,0);
assert(listenfd>=0);
struct linger tmp={1,0};
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[5];
int epollfd=epoll_create(5);
assert(epollfd!=-1);
addfd(epollfd,listenfd,false);
service::m_epollfd=epollfd;
while(1)
{
int number=epoll_wait(epollfd,events,5,-1);

for(int i=0;i<number;i++)
{
int sockfd=events[i].data.fd;
if(sockfd==listenfd)
{
struct sockaddr_in client_address;
socklen_t client_addrlength=sizeof(client_address);
int connfd=accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
if(connfd<0)
{
printf("errno is: %d\n",errno);
continue;
}
printf("There is a new connection\n");
users[connfd].init(connfd,client_address);

}
else if(events[i].events&(EPOLLRDHUP|EPOLLHUP|EPOLLERR))
{
printf("events is EPOLLRDHUP|EPOLLHUP|EPOLLERR\n");
users[sockfd].close_conn();
}
else if(events[i].events&EPOLLIN)
{
printf("events is EPOLLIN\n");
sockfd=events[i].data.fd;
if(sockfd<0)
continue;
if(users[sockfd].sread())
{
pool->append(users+sockfd);
}
else
{
users[sockfd].close_conn();
}
}
else if(events[i].events&EPOLLOUT)
{
printf("events is EPOLLOUT\n");
sockfd=events[i].data.fd;
if(users[sockfd].swrite())
{
//users[sockfd].close_conn();
}
}
}
}
close(epollfd);
close(listenfd);
delete [] users;
delete pool;
return 0;
}

 

// service.h

#ifndef SERVICE_H
#define SERVICE_H

#include<unistd.h>
#include<signal.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<assert.h>
#include<string.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
#include<errno.h>
#include<time.h>
#include"locker.h"
class service
{
public:
static const int READ_BUFFER_SIZE=2048;
static const int WRITE_BUFFER_SIZE=1024;
static const int FILE_BUFFER_SIZE=2048;
enum METHOD {GET=0,POST,HEAD,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH};
enum CHECK_STATE {CHECK_STATE_REQUESTLINE=0,CHECK_STATE_HEADER,CHECK_STATE_CONTENT};
enum HTTP_CODE {NO_REQUEST,GET_REQUEST,BAD_REQUEST,NO_RESOURCE,FORBIDDEN_REQUEST,FILE_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};
enum LINE_STATUS{LINE_OK=0,LINE_BAD,LINE_OPEN};
public:
service(){};
~service(){};

public:
void init(int sockfd,const sockaddr_in& addr);
void close_conn(bool real_close=true);
void process();
bool sread();
bool swrite();


private:
void init();
bool send_http_head(int conn_socket, int status, char *s_status, char *filetype);
HTTP_CODE process_read();
int send_html();
void add_page_error(int status,char* s_status,char* msg);
void add_http_head(int status, char *s_status, char *filetype);
bool process_write(HTTP_CODE ret);
bool add_response(const char* format,...);
void do_url();

bool add_content(const char* content);
bool add_status_line(int status,const char* title);
bool add_headers(int content_length);
bool add_content_length(int content_length);
bool add_blank_line();
bool add_date();


char* get_line(){return read_buf+m_start_line;}
LINE_STATUS parse_line();

HTTP_CODE parse_request_line(char* text);

public:
static int m_epollfd;
static int m_user_count;

private:
int m_sockfd;
sockaddr_in m_address;
int m_read_idx;
int m_checked_idx;
int m_start_line;
int m_write_idx;

CHECK_STATE m_check_state;
char* m_method=new char[100];

char* m_url=new char[100];
char* m_version=new char[100];
char* m_host;
int m_content_length;

char read_buf[READ_BUFFER_SIZE];
char write_buf[WRITE_BUFFER_SIZE];
char file_buf[FILE_BUFFER_SIZE];

char* m_file_address;
struct stat m_file_stat;
};
#endif

 

 

// locker.h

#ifndef LOCKER_H
#define LOCKER_H

#include<exception>
#include<pthread.h>
#include<semaphore.h>
class sem
{
public:
sem()
{
if(sem_init(&m_sem,0,0)!=0)
{
throw std::exception();
}
}
~sem()
{
sem_destroy(&m_sem);
}
bool wait()
{
return sem_wait(&m_sem)==0;
}
bool post()
{
return sem_post(&m_sem)==0;
}
private:
sem_t m_sem;
};
class locker
{
public:
locker()
{
if(pthread_mutex_init(&m_mutex,NULL)!=0)
{
throw std::exception();
}
}
~locker()
{
pthread_mutex_destroy(&m_mutex);
}
bool lock()
{
return pthread_mutex_lock(&m_mutex)==0;
}
bool unlock()
{
return pthread_mutex_unlock(&m_mutex)==0;
}
private:
pthread_mutex_t m_mutex;
};
class cond
{
public:
cond()
{
if(pthread_mutex_init(&m_mutex,NULL)!=0)
{
throw std::exception();
}
if(pthread_cond_init(&m_cond,NULL)!=0)
{
pthread_mutex_destroy(&m_mutex);
throw std::exception();
}
}
~cond()
{
pthread_mutex_destroy(&m_mutex);
pthread_cond_destroy(&m_cond);
}
bool wait()
{
int ret=0;
pthread_mutex_lock(&m_mutex);
ret=pthread_cond_wait(&m_cond,&m_mutex);
pthread_mutex_unlock(&m_mutex);
return ret==0;
}

bool signal()
{
return pthread_cond_signal(&m_cond)==0;
}
private:
pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
};
#endif

 

// my_server.cpp

#include<unistd.h>
#include<signal.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<assert.h>
#include<string.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<stdarg.h>
#include<errno.h>
#include"service.h"
#include"myserver_threadpool.h"
#include"service.cpp"

#define MAX_FD 1024
extern void addfd(int epollfd,int fd,bool one_shot);
extern void modfd(int epollfd,int fd,int ev);
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);
close(connfd);
}

void close_conn(int epollfd,int fd)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0);
close(fd);
}

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);

myserver_threadpool<service>* pool=NULL;
pool=new myserver_threadpool<service>(3);

service* users=new service[MAX_FD];
assert(users);
int user_count=0;

int listenfd=socket(PF_INET,SOCK_STREAM,0);
assert(listenfd>=0);
struct linger tmp={1,0};
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[5];
int epollfd=epoll_create(5);
assert(epollfd!=-1);
addfd(epollfd,listenfd,false);
service::m_epollfd=epollfd;
while(1)
{
int number=epoll_wait(epollfd,events,5,-1);

for(int i=0;i<number;i++)
{
int sockfd=events[i].data.fd;
if(sockfd==listenfd)
{
struct sockaddr_in client_address;
socklen_t client_addrlength=sizeof(client_address);
int connfd=accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
if(connfd<0)
{
printf("errno is: %d\n",errno);
continue;
}
printf("There is a new connection\n");
users[connfd].init(connfd,client_address);

}
else if(events[i].events&(EPOLLRDHUP|EPOLLHUP|EPOLLERR))
{
printf("events is EPOLLRDHUP|EPOLLHUP|EPOLLERR\n");
users[sockfd].close_conn();
}
else if(events[i].events&EPOLLIN)
{
printf("events is EPOLLIN\n");
sockfd=events[i].data.fd;
if(sockfd<0)
continue;
if(users[sockfd].sread())
{
pool->append(users+sockfd);
}
else
{
users[sockfd].close_conn();
}
}
else if(events[i].events&EPOLLOUT)
{
printf("events is EPOLLOUT\n");
sockfd=events[i].data.fd;
if(users[sockfd].swrite())
{
//users[sockfd].close_conn();
}
}
}
}
close(epollfd);
close(listenfd);
delete [] users;
delete pool;
return 0;
}

 

// service.cpp

#include"service.h"

const char* ok_200_title="OK";
const char* error_400_title="Bad Request";
const char* error_400_form="Your request has bad syntax or is inherently impossible to satisfy.\n";
const char* error_403_title="Forbidden";
const char* error_403_form="You do not have permission to get file from this server.\n";
const char* error_404_title="Not Found";
const char* error_404_form="The requested file was not found on this server.\n";
const char* error_500_title="Internal Error";
const char* error_500_form="There was an unusual problem serving the requested file.\n";
const char* doc_root="/var/www/html";

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 epollfd,int fd,bool one_shot)
{
epoll_event event;
event.data.fd=fd;
event.events=EPOLLIN|EPOLLET|EPOLLRDHUP|EPOLLOUT|EPOLLERR;
if(one_shot)
{
event.events|=EPOLLONESHOT;
}
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblocking(fd);
}

void modfd(int epollfd,int fd,int ev)
{
epoll_event event;;
event.data.fd=fd;
event.events=ev|EPOLLET|EPOLLONESHOT|EPOLLRDHUP;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event);
}


void removefd(int epollfd,int fd)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0);
close(fd);
}


int service::m_user_count=0;
int service::m_epollfd=-1;
void service::close_conn(bool real_close)
{
if(real_close&&(m_sockfd!=-1))
{
removefd(m_epollfd,m_sockfd);
m_sockfd=-1;
m_user_count--;
}
}

void service::init(int sockfd,const sockaddr_in& addr)
{
m_sockfd=sockfd;
m_address=addr;
addfd(m_epollfd,sockfd,true);
m_user_count++;
init();
}

void service::init()
{
memset(read_buf,‘\0‘,READ_BUFFER_SIZE);
memset(write_buf,‘\0‘,WRITE_BUFFER_SIZE);
memset(file_buf,‘\0‘,FILE_BUFFER_SIZE);
m_check_state=CHECK_STATE_REQUESTLINE;

m_content_length=0;
m_host=0;
m_start_line=0;
m_checked_idx=0;
m_read_idx=0;
m_write_idx=0;
}

bool service::sread()
{
if(m_read_idx>=READ_BUFFER_SIZE)
{
return false;
}
int bytes_read=0;
while(true)
{
bytes_read=recv(m_sockfd,read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,0);
printf("read_buf is %s\n",read_buf);
if(bytes_read==-1)
{
if(errno==EAGAIN||errno==EWOULDBLOCK)
{
break;
}
return false;
}
else if(bytes_read==0)
{
return false;
}
m_read_idx+=bytes_read;
}
return true;
}

bool service::swrite()
{
printf("This is write() m_write_idx=%d\n",m_write_idx);
printf("In swrite() write_buf is %s \n",write_buf);
int temp=0;
int bytes_have_send=0;
int bytes_to_send=m_write_idx;
if(bytes_to_send==0)
{
printf("bytes_to_send = 0\n");
modfd(m_epollfd,m_sockfd,EPOLLIN);
init();
return false;
}

while(1)
{
temp=write(m_sockfd,write_buf,bytes_to_send);
if(temp<=-1)
{
if(errno==EAGAIN)
{
modfd(m_epollfd,m_sockfd,EPOLLOUT);
return true;
}
return false;
}
bytes_to_send-=temp;
bytes_have_send+=temp;
if(bytes_to_send==0)
{
init();
modfd(m_epollfd,m_sockfd,EPOLLIN);
return true;
}
}

}

void service::do_url()
{
char *p;
p=strchr(m_url+1, ‘?‘);
if(p)
{
*p = 0;
p++;
}
}

void service::add_page_error(int status,char* s_status,char* msg)
{
add_response("<html><head></head><body><h1> %s </h1><hr>Reage Web Server 0.01</body></head>", msg);
add_http_head(status, s_status, "text/html");
}

void service::add_http_head(int status, char *s_status, char *filetype)
{
add_status_line(status, s_status);
add_date();
add_content_length(m_file_stat.st_size);
add_blank_line();

}

bool service::add_status_line(int status,const char* title)
{
return add_response("%s %d %s\r\n","HTTP/1.1,",status,title);
}

bool service::add_content_length(int content_len)
{
return add_response("Content-Length: %d \r\n",content_len);
}

bool service::add_blank_line()
{
return add_response("%s","\r\n");
}

bool service::add_content(const char* content)
{
return add_response("%s",content);
}

bool service::add_date()
{
time_t timep;
time (&timep);
return add_response("Date: %s",ctime(&timep));
}


bool service::add_response(const char* format,...)
{
if(m_write_idx>=WRITE_BUFFER_SIZE)
{
return false;
}
va_list arg_list;
va_start(arg_list,format);
int len=vsnprintf(write_buf+m_write_idx,WRITE_BUFFER_SIZE-1-m_write_idx,format,arg_list);
if(len>=(WRITE_BUFFER_SIZE-1-m_write_idx))
{
return false;
}
m_write_idx+=len;
va_end(arg_list);
return true;
}


int service::send_html()
{

int f;
int tmp;
printf("m_url = %s\n",m_url);
m_url=strchr(m_url,‘/‘);
if(strlen(m_url)==1||!strcasecmp(m_url,"/favicon.ico"))
{
printf("exec strcpy\n");
strcpy(m_url,"index.html");
}
else
{
printf("exec strchr\n");
m_url=m_url+1;
printf("m_url is %s\n",m_url);
}
if(stat(m_url,&m_file_stat)>=0)
{
//add_page_error(404,"Not found", "Not found<br/> Reage does not implement this mothod\n");
printf("file exist\n");

}
else if(!(S_ISREG(m_file_stat.st_mode))||!(S_IRUSR&m_file_stat.st_mode))
{
add_page_error(403 , "Forbidden", "Forbidden<br/> Reage couldn‘t read the file\n");
return 1;
}
add_http_head( 200, "OK", "text/html" );
f=open(m_url,O_RDONLY);
if(f<0)
{
add_page_error(404, "Not found", "Not found<br/> Reage couldn‘t read the file\n");
}

tmp=read(f,write_buf+m_write_idx,m_file_stat.st_size);
m_write_idx+=m_file_stat.st_size;
return 1;
}


service::HTTP_CODE service::process_read()
{
if(sscanf(read_buf,"%s %s %s",m_method,m_url,m_version))
{
printf("the sscanf data is %s %s %s\n",m_method,m_url,m_version);
printf("request correct\n");
}
else
printf("request error\n");
if(!strcasecmp(m_method, "get"))
do_url();
return FILE_REQUEST;
}

bool service::process_write(HTTP_CODE ret)
{
if(ret==FILE_REQUEST)
{
if(send_html())
return true;
else
return false;
}
return false;
}

void service::process()
{
HTTP_CODE read_ret=process_read();
printf("process() read_ret=%d\n",read_ret);
if(read_ret==NO_REQUEST)
{
modfd(m_epollfd,m_sockfd,EPOLLIN);
return;
}
bool write_ret=process_write(read_ret);
if(!write_ret)
{
close_conn();
}
modfd(m_epollfd,m_sockfd,EPOLLOUT);
}

 

// service.h

#ifndef SERVICE_H
#define SERVICE_H

#include<unistd.h>
#include<signal.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<assert.h>
#include<string.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
#include<errno.h>
#include<time.h>
#include"locker.h"
class service
{
public:
static const int READ_BUFFER_SIZE=2048;
static const int WRITE_BUFFER_SIZE=1024;
static const int FILE_BUFFER_SIZE=2048;
enum METHOD {GET=0,POST,HEAD,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH};
enum CHECK_STATE {CHECK_STATE_REQUESTLINE=0,CHECK_STATE_HEADER,CHECK_STATE_CONTENT};
enum HTTP_CODE {NO_REQUEST,GET_REQUEST,BAD_REQUEST,NO_RESOURCE,FORBIDDEN_REQUEST,FILE_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};
enum LINE_STATUS{LINE_OK=0,LINE_BAD,LINE_OPEN};
public:
service(){};
~service(){};

public:
void init(int sockfd,const sockaddr_in& addr);
void close_conn(bool real_close=true);
void process();
bool sread();
bool swrite();


private:
void init();
bool send_http_head(int conn_socket, int status, char *s_status, char *filetype);
HTTP_CODE process_read();
int send_html();
void add_page_error(int status,char* s_status,char* msg);
void add_http_head(int status, char *s_status, char *filetype);
bool process_write(HTTP_CODE ret);
bool add_response(const char* format,...);
void do_url();

bool add_content(const char* content);
bool add_status_line(int status,const char* title);
bool add_headers(int content_length);
bool add_content_length(int content_length);
bool add_blank_line();
bool add_date();


char* get_line(){return read_buf+m_start_line;}
LINE_STATUS parse_line();

HTTP_CODE parse_request_line(char* text);

public:
static int m_epollfd;
static int m_user_count;

private:
int m_sockfd;
sockaddr_in m_address;
int m_read_idx;
int m_checked_idx;
int m_start_line;
int m_write_idx;

CHECK_STATE m_check_state;
char* m_method=new char[100];

char* m_url=new char[100];
char* m_version=new char[100];
char* m_host;
int m_content_length;

char read_buf[READ_BUFFER_SIZE];
char write_buf[WRITE_BUFFER_SIZE];
char file_buf[FILE_BUFFER_SIZE];

char* m_file_address;
struct stat m_file_stat;

 

};
#endif

  

web服务器

原文:http://www.cnblogs.com/biaofeiren/p/5215343.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!