一个简单的Http服务器

3 篇文章 1 订阅

计算机网络的课程设计代码,使用基本的socket编写的,采用多线程处理访问

写了 一下午,本来想把php链进来,结果之后就不想碰了~~~委屈

环境是 VS2010

主要是两个类:

HttpServer:用来创建监听
response:用来响应请求
能够处理GET 和 POST报文
只是因为没有后台的脚本语言处理,所以把请求参数显示在了控制台的界面上
完整的代码和可运行程序在github~~~~~
  class HttpServer
  {
  public:
	  int PORT;
	  string HTTP_FILE;
	  string stopsymbol;
	  WSAData wsadata;
	  HttpServer(int a):PORT(a)
	  {}
	  void start();
	  void stop();

	 

		static void make_response(SOCKET s,char *buffer,int len)
		{
		  response res(s,string(buffer));
		  map <string, string>::iterator m1_Iter;
		  for ( m1_Iter=res.value.begin();m1_Iter!=res.value.end();m1_Iter++)
			 cout<<m1_Iter->first<<"  : "<<m1_Iter->second<<endl;;
		}
		static  bool GetAddressBySocket(SOCKET m_socket,SOCKADDR_IN &m_address)//读取IP和Port
		{
			memset(&m_address, 0, sizeof(m_address));
			int nAddrLen = sizeof(m_address);
			if(::getpeername(m_socket, (SOCKADDR*)&m_address, &nAddrLen) != 0)
			{
			 printf("Get IP address by socket failed!n");
			 return false;
			}
			//cout<<"IP: "<<::inet_ntoa(m_address.sin_addr)<<"  PORT: "<<ntohs(m_address.sin_port)<<endl;
			return true;
		}

	 static DWORD WINAPI AnswerThread(LPVOID lparam)
		{
		int len = 0;  
		char buffer[SIZE_BUFFER]; 
		SOCKET  ClientSocket=(SOCKET)(LPVOID)lparam;

		memset(buffer,0,SIZE_BUFFER);

		 if((len = recv(ClientSocket,buffer,SIZE_BUFFER,0))>0){
			 SOCKADDR_IN ad;
			 GetAddressBySocket(ClientSocket,ad);
			 cout<<"from  "<<::inet_ntoa(ad.sin_addr)<<"  request for: ";
			 make_response(ClientSocket,buffer,len);
			//printf("%s",buffer); 
			//send(ClientSocket, protocolHead,strlen(protocolHead),0);
			}
		
		return 1; 
		}
  };

#include "StdAfx.h"
#include "server.h"
void HttpServer::start()
{
	stopsymbol="";
	cout<<"start service successful"<<endl;
	WSAStartup(MAKEWORD(2,0),&wsadata);  
	SOCKET ssocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//创建一个TCP的Socket,但是没有绑定  
	struct sockaddr_in server;  
	server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  //inet_addr("192.168.1.107");  
	server.sin_family = AF_INET;  
	server.sin_port = htons(PORT);//创建一个端口地址,指定地址和端口号  
	   
	if(bind(ssocket,(sockaddr*)&server,sizeof(sockaddr_in)) == SOCKET_ERROR)
	{  
		std::cout<<"error bind"<<std::endl;  
	}//绑定TCP Socket到指定的端口地址  
	if(listen(ssocket, 4) == SOCKET_ERROR)
	{  
		std::cout<<"listen error"<<std::endl;  
	}  
	fd_set read_fdset;//读文件描述符  
	std::vector<int> sockets;//读端口的集合,每监听到一个请求,就会新建一个端口,并且存放到该集合中  
	HANDLE       hThread;
	DWORD        dwThreadId; 
	SOCKET       sClient; 
	while(true){  
		if (stopsymbol=="stop")
		{
			closesocket(ssocket);
			break;
		}
		FD_ZERO(&read_fdset);  
		FD_SET(ssocket,&read_fdset); 
		int n = select(0,&read_fdset,NULL,NULL,0);  
		if(FD_ISSET(ssocket,&read_fdset))
		{//如果监听端口接收到了读的请求,也就是客户端发起了请求连接  
			SOCKET sClient = accept(ssocket,NULL,0);//创建一个新的端口 

			if(n!=SOCKET_ERROR)
			{  
				hThread=CreateThread(NULL,NULL,HttpServer::AnswerThread,(LPVOID)sClient, 0, &dwThreadId); 
			//if(hThread==NULL)    
			//	printf("CreatThread AnswerThread() failed./n"); 
			//else 
			//	printf("CreateThread OK./n");    
			}  
		}  
	}  

}

void HttpServer::stop()
{
	stopsymbol="stop";
	cout<<"stop service successful"<<endl;
}


response类:
#ifndef __RESPONSE_H_
#define __RESPONSE_H_
#include <string>
#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
#include <vector>
#include <fcntl.h>
#include <map>
#include <fstream>
#include <string>
using namespace std;

const string src("a");
class response
{
public:
	string response_code;
	SOCKET s;
	string request;
	int Content_Length;
	vector<string> line_request;
	map<string,string> value;
	string Content_Type;
	string file;
	string extension;
	string Connection;
	string Server;
	void work()
	{
		fstream f;
		string sss=src+file;
		cout<<file<<endl;
		f.open(sss,ios::in);
		if(!f)
		{
			response_code="HTTP/1.1 404 Not Found";
			output_header("a/404.html");
			output_file("a/404.html");
			f.close();
			return;
		}
		response_code="HTTP/1.1 200 OK";
		if (extension=="php")
		{
			//sss=phps::make_php(sss);
		}
		output_header(sss.c_str());
		output_file(sss.c_str());
		f.close();
	}
	void output_header(const char *f)
	{
		int length;
		ifstream is;
		is.open (f, ios::binary);
		is.seekg (0, ios::end);
		length = is.tellg();
		length+=2;
		char pp[25];
		itoa(length,pp,10);
		string contentlength="Content-Length: "+string(pp)+"\r\n";

		string temp=response_code+"\r\n"+Server+"\r\n"+contentlength+"Content-Type: "+Content_Type+"\r\n"
			       +Connection+"\r\n";

		char *bufs=new char[ temp.length() + 1 ];
		strcpy( bufs,temp.c_str());
		int len=temp.length();
		sendall(s,bufs,&len);
	}
	void output_file(const char* f)
	{
		 FILE* read_from; 
		 int readed = -1;   
		 read_from = fopen(f, "rb"); 
		 if(read_from != NULL) 
		 {        
			char read_buf[128];        //读文件时的字节缓存数组

			send(s, "\r\n", 2, 0);      //再加一个"\r\n" 不能缺少 格式要求
			while(!feof(read_from)) 
			{      //判断文件是否已经结束
				fread(read_buf, 16,8, read_from);   //读取
				int len = sizeof(read_buf);
				if (sendall(s, read_buf, &len) == -1) 
				{ //发送数据
					printf("Sending error!");    //出现发送错误显示到控制台继续发送
					continue;
				}
			}
		}
		 closesocket(s);
		//send(s, "\r\n", 2, 0); 
	}
	static int sendall(int s, char *buf, int *len) 
	  {
		 //cout<<buf;
		int total = 0;           // 已经发送字节数
		int bytesleft = *len;                                   //还剩余多少字节
		int n=0;
		while(total < *len) 
		{
			n = send(s, buf+total, bytesleft, 0);
			if (n == -1) { break; }
			total += n;
			bytesleft -= n;
		}
		*len = total;           // 返回实际发送出去的字节数
		return n==-1?-1:0;          // 成功发送返回0 失败-1
	 }

	static vector<string> split(string& str,const char* c)
	{
		char *cstr, *p;
		vector<string> res;
		cstr = new char[str.size()+1];
		strcpy(cstr,str.c_str());
		p = strtok(cstr,c);
		while(p!=NULL)
		{
			res.push_back(p);
			p = strtok(NULL,c);
		}
		return res;
	}
	response(SOCKET ss,string a)
	{
		s=ss;
		request=a;
		line_request=split(request,"\n");
		//for (int i=0;i<line_request.size();i++)
		//	cout<<line_request[i]<<endl;
		Server="Server: DHS/1.0";
		Connection="Connection: close";
		if (line_request[0][0]=='G' || line_request[0][0]=='g')
		{
			make_get_request();
		} else
		if (line_request[0][0]=='P' || line_request[0][0]=='p')
		{
			make_post_request();
		} else
		make_bad_request();
		work();
	}
	void make_bad_request(){};
	void make_post_request()
	{
		vector<string> content=split(line_request[0]," ");
		string vv;
		int ct;
		for (int i=0;i<line_request.size();i++)
			if(line_request[i].substr(0,14)=="Content-Length")
			{
				ct=atoi(line_request[i].substr(15,string::npos).c_str());
				break;
			}
		//cout<<ct<<endl;
		file=content[1];
		if (file.length()==1 && file[0]=='/')
			file="/index.html";
		int position=request.find_first_of("\n\n");
		if (position==string::npos)
			position=request.find_first_of("\r\n\r\n");
		vv=request.substr(request.length()-ct,ct);
		vector<string> v=split(vv,"&");
		//cout<<vv<<endl;
		for (int i=0;i<v.size();i++)
		{
			int p=v[i].find('=');
			value[v[i].substr(0,p)]=v[i].substr(p+1,v[i].length()-p-1);
		}

		extension="";
		for (int i=file.length()-1;i>=0;i--)
		{
			if (file[i]=='.')	
			{
				extension=file.substr(i+1,file.length()-i-1);
				break;
			}
		}
		//map <string, string>::iterator m1_Iter;
		//for ( m1_Iter=value.begin();m1_Iter!=value.end();m1_Iter++)
		//	cout<<m1_Iter->first<<"  "<<m1_Iter->second<<endl;
		if (extension=="jpg" || extension=="jpeg" || extension=="jpe")
			Content_Type = "image/jpeg";
			else if (extension=="gif")
			Content_Type = "image/gif";
			else if (extension=="png")
			Content_Type="image/png";
			else if (extension=="pdf")
			Content_Type="application/pdf";
			else Content_Type="text/html";
	};
	void make_get_request()
	{
		vector<string> content=split(line_request[0]," ");
		vector<string> con;
		if (content[1].find('?')!=string::npos)
		{
			con=split(content[1],"?");
			if (con[1].length()>0)
			{
				vector<string> v=split(con[1],"&");
				for (int i=0;i<v.size();i++)
				{
					int p=v[i].find('=');
					value[v[i].substr(0,p)]=v[i].substr(p+1,v[i].length()-p-1);
				}
			}
			file=con[0];
		} else
			file=content[1];

		if (file.length()==1 && file[0]=='/')
			file="/index.html";

		extension="";
		for (int i=file.length()-1;i>=0;i--)
		{
			if (file[i]=='.')	
			{
				extension=file.substr(i+1,file.length()-i-1);
				break;
			}
		}
		//map <string, string>::iterator m1_Iter;
		//for ( m1_Iter=value.begin();m1_Iter!=value.end();m1_Iter++)
		//	cout<<m1_Iter->first<<"  "<<m1_Iter->second<<endl;
		if (extension=="jpg" || extension=="jpeg" || extension=="jpe")
			Content_Type = "image/jpeg";
			else if (extension=="gif")
			Content_Type = "image/gif";
			else if (extension=="png")
			Content_Type="image/png";
			else if (extension=="pdf")
			Content_Type="application/pdf";
			else Content_Type="text/html";
		
	}
};

#endif


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值