c++ socket实现多请求响应服务器

虽然这个标题写的很吓人,但其实只是个弱智版。

这个版本相对于上一个版本只是把其中的函数分离成多个文件来编写,增加了我项目的可扩展性,也增加一些对请求报文解析方面的内容,实际上并没有添加什么新内容。但是做完这些倒是帮我整理了一下关于.c和.h文件的关系,以及如何书写简单的makefile文件。

具体代码:

File_ope.h

#ifndef __FILE_OPE_H_
#define __FILE_OPE_H_

int get_file_size(char *file_name);	//得到文件大小	

#endif

File_ope.cpp

#include<cstdio>
#include"File_ope.h"

int get_file_size(char *file_name){	//得到文件大小	
	if(file_name==NULL)
		return -1;
	FILE *fp=fopen(file_name,"r");
	if(!fp) 
		return -1;
	fseek(fp,0,SEEK_END);
	int size=ftell(fp);
	fclose(fp);
	return size;
}

Deal_req.h

#ifndef __DEAL_REQ_H_
#define __DEAL_REQ_H_
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<unistd.h>

struct http_req{
	static const int BUFSIZE=128;
	char *req_line,*req_head,*req_body;
	char file_path[BUFSIZE],file_type[BUFSIZE];	//文件相关
	char *method,*url,*version,*argv;//请求行
	int alive,content_length;
	char *host,*charset,*content_type;//请求头
	int req_break(char *req);	//将请求分解
	int reqline_analyse();	//将报文行分解
	int deal_get();			//处理get请求
	int deal_post();		//处理post请求
	int deal_req(int sock,char* req);	//处理请求的主要函数
};
#endif

Deal_req.cpp

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<unistd.h>
#include<sys/socket.h>
#include"Deal_req.h"
#include"File_ope.h"
using namespace std;


int http_req::req_break(char *req){
	char *tmp=req;
	if(tmp==NULL)
		return -1;
	req_line=tmp;
	tmp=strchr(tmp,'\r');
	if(tmp==NULL)
		return -1;
	*tmp='\0';
	tmp++;
	*tmp='\0';
	tmp++;
	req_head=tmp;
	int cnt=0;
	for(;cnt!=2&&*tmp!='\0';){
		if(*tmp=='\r'){
			cnt++;
		}
		else if(*tmp!='\n')
			cnt=0;
		tmp++;
	}
	*(tmp-1)='\0';
	*tmp='\0';
	tmp++;
	req_body=tmp;
	return 0;
}
int http_req::reqline_analyse(){
	if(req_line==NULL)
		return -1;
	char *tmp=req_line;
	method=tmp;
	tmp=strchr(tmp,' ');
	if(tmp==NULL)
		return -1;
	*tmp='\0';
	tmp++;
	url=tmp;
	tmp=strchr(tmp,' ');
	if(tmp==NULL)
		return -1;
	*tmp='\0';
	tmp++;
	version=tmp;
	return 0;
}
int http_req::deal_get(){
	char *p=strchr(url,'?');
	if(p!=NULL){
		argv=p+1;		//获取get参数
		*p='\0';
	}
	strcpy(file_path,url);
	if(file_path[0]=='\0')
		return -1;
	char *tmp=strrchr(file_path,'.');
	memset(file_type,0,sizeof(file_type));
	if(tmp==NULL)
		strcpy(file_type,"text/plain");
	else
		tmp++;
	if(!strcmp(tmp,"html")||!strcmp(tmp,"htm"))
		strcpy(file_type,"text/html");
	else if(!strcmp(tmp,"css"))
		strcpy(file_type,"text/css");
	else if(!strcmp(tmp,"gif"))
		strcpy(file_type,"image/gif");
	else if(!strcmp(tmp,"jpeg")||!strcmp(tmp,"jpg"))
		strcpy(file_type,"image/jpeg");
	else if(!strcmp(tmp,"png"))
		strcpy(file_type,"image/png");
	else
		strcpy(file_type,"text/plain");
	return 0;
}
int http_req::deal_post(){
	strcpy(file_path,url);
	if(file_path[0]=='\0')
		return -1;
	char *tmp=strrchr(file_path,'.');
	if(tmp==NULL)
		strcpy(file_type,"text/plain");
	else
		tmp++;
	memset(file_type,0,sizeof(file_type));
	if(!strcmp(tmp,"html")||!strcmp(tmp,"htm"))
		strcpy(file_type,"text/html");
	else if(!strcmp(tmp,"css"))
		strcpy(file_type,"text/css");
	else if(!strcmp(tmp,"gif"))
		strcpy(file_type,"image/gif");
	else if(!strcmp(tmp,"jpeg")||!strcmp(tmp,"jpg"))
		strcpy(file_type,"image/jpeg");
	else if(!strcmp(tmp,"png"))
		strcpy(file_type,"image/png");
	else
		strcpy(file_type,"text/plain");
	return 0;
}
int http_req::deal_req(int sock,char *req){
	req_break(req);
	reqline_analyse();
	if(!strcmp(method,"POST")){
		deal_post();
	}
	else if(!strcmp(method,"GET")){
		deal_get();
	}
	FILE *fd=fopen(file_path,"rb");

	if(fd==NULL){
		char Error[]="HTTP/1.0 404 Not Found\r\n\
		 Content-Type: text/plain 404 not found by Manio";
		send(sock,Error,strlen(Error),0);
	}
	else{
		int file_size=get_file_size(file_path);
		//int opened_file=open(fp,O_RDONLY);
		char *memfile=(char *)calloc(file_size,sizeof(char));
		fread(memfile,file_size,1,fd);
		//close(opened_file);		//关闭文件
		
		printf("The file size is:%d\r\n",file_size);
		printf("The context is: \r\n");
		write(1,memfile,file_size);
		printf("\r\n");
		
		char buff[1024],FlTp[256],ConLen[256];
		memset(buff,0,sizeof(buff));
		memset(ConLen,0,sizeof(ConLen));
		memset(FlTp,0,sizeof(FlTp));

		strcat(buff,"\r\nHTTP/1.0 200 ok\r\n");		//响应报文的格式
		sprintf(FlTp,"Content-Type: %s;charset=UTF-8\r\n",file_type);
		strcat(buff,FlTp);
	//	strcat(buff,"Content-Type: text/html;charset=UTF-8\r\n");
		sprintf(ConLen,"Content-Length: %d\r\n\r\n",file_size);
		strcat(buff,ConLen);

		send(sock,buff,strlen(buff),0);
		send(sock,memfile,file_size,0);
		printf("Send successfully\r\n\r\n");
		
		free(memfile);
	}
	return 0;
}

WebServer.h

#ifndef __WEB_SERVER_H_
#define __WEB_SERVER_H_

#define PORT 8848		//端口号
#define BACKLOG 10		//等待队列长度
#define BUFFSIZE 1024
#define MAXEVENT 8

#endif

WebServer.cpp

#include<cstdio>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<sys/event.h>
#include<sys/time.h>
#include<fcntl.h>
#include"WebServer.h"
#include"Deal_req.h"
#include"File_ope.h"
using namespace std;

int main(int argc,char *argv[]){
	struct sockaddr_in addr,naddr;			//在头文件<netinet/in.h>中
	socklen_t len;
	struct kevent *chlist;		//监听事件
	struct kevent *evlist; 		//触发事件
	int kq;						//kqueue队列

	int sockfd=socket(PF_INET,SOCK_STREAM,0);//建立套接字
	if(sockfd==-1){
		perror("Socket");
		exit(1);
	}

	addr.sin_family=AF_INET;
	addr.sin_port=htons(PORT);			//转化为网络字节序
	addr.sin_addr.s_addr=INADDR_ANY; 	//初始化为我的IP
	bzero(&(addr.sin_zero),sizeof(addr.sin_zero));	//多余的字节初始为0
	
	//将本地端口和套接字绑定
	if(bind(sockfd,(const struct sockaddr*)&addr,sizeof(addr))==-1){
		perror("bind");
		exit(1);
	}

	if(listen(sockfd,BACKLOG)==-1){	//第二个参数是等待队列的长度
		perror("listen");
		exit(1);
	}
	printf("listening...\n");

	kq=kqueue();
	if(kq==-1){
		perror("kqueue");
		exit(1);
	}
	//初始化kevent结构体
	chlist=(struct kevent*)malloc(sizeof(struct kevent));
	evlist=(struct kevent*)malloc(sizeof(struct kevent)*MAXEVENT);

	EV_SET(chlist,sockfd,EVFILT_READ,EV_ADD|EV_ENABLE,0,0,0);	//注册事件
	while(true){
		char *buff=(char *)calloc(BUFFSIZE,sizeof(char));	//初始化buff
		int nev=kevent(kq,chlist,1,evlist,MAXEVENT,NULL);	//无限阻塞
		if(nev<0){
			perror("kevent");
		}
		else{
			printf("------------------------------------------------\n");
			printf("The num of req is:%d\n",nev);
			if(evlist[0].flags&EV_EOF){		//读取socket关闭指示
				exit(EXIT_FAILURE);
			}
			for(int i=0;i<nev;i++){
				if(evlist[i].flags&EV_ERROR){
					printf("EV_ERROR:%s\n",strerror(evlist[i].data));
					exit(EXIT_FAILURE);
				}
				if(evlist[i].ident==sockfd){
					int nsockfd=accept(sockfd,(struct sockaddr*)&naddr,&len);	//呼叫地址
					if(nsockfd==-1){
						perror("accept");
						exit(1);
					}
					printf("client connected\n\n");

					recv(nsockfd,buff,BUFFSIZE,0);		//recv将接收到的数据放到buff中
				//	send(nsockfd,buff,BUFFSIZE,0);		//向客户端发送buff中的内容

					printf("Recive message from client: \n%s\n",buff);

					if(buff!=NULL){
						printf("Got request.\n");
						http_req new_req;
						new_req.deal_req(nsockfd,buff);
					}
					close(nsockfd);				//关闭网络连接,在unistd.h中
				}
			}
			printf("------------------------------------------------\n");
			//	sleep(100);
		}
		free(buff);
	}

	free(chlist);				//内存释放
	free(evlist);
	close(kq);
	close(sockfd);				//close用以关闭一般的文件描述符
	return 0;
}

但是在测试的时候又发现了一点小问题。由于之前测试都是拿我自己的电脑上的浏览器测试的,写这个项目的时候拿其他人的电脑浏览器发现并不能访问我电脑中的页面。不知道是为什么

希望能在后续的过程中解决。

猜你喜欢

转载自blog.csdn.net/Monster_ixx/article/details/88366659