上一小节通过阅读开源的Web服务器--tinyhttpd。大概知道了一次交互的请求信息和应答信息的具体过程。接下来我就自己简单的实现一个Web服务器。

  下面这个程序只是实现一个简单的框架出来。这次先实现能够Accept客户端的请求。

  简单创建web服务器

  webserver.h

 #include <iostream>
#include <string>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <thread>//使用c++11的多线程 using namespace std; class WebServer
{
public:
WebServer();
~WebServer();
int ServerInit(u_short port);
int ServerError(string str);
int ServerAccept();
int ServerClose();
int ServerRequest(int cli_fd);
int get_line(int cli_fd,char * buf,int size);//来自tinyhttpd int Page_200(int cli_fd);
int Page_501(int cli_fd);
private:
int httpd;
}; int WebServer::ServerRequest(int cli_fd)
{
char buf[];
int size=;
int i=;
memset(buf,,sizeof(buf));
while((i>)&&strcmp("\n",buf))
{
i=get_line(cli_fd,buf,sizeof(buf));
cout<<buf;
}
if(fork()==)
{
//处理阶段
execl("/bin/ls","ls","/home/myuser/",NULL);
}
Page_200(cli_fd);
close(cli_fd);
return ;
}
int WebServer::ServerAccept()
{
struct sockaddr_in cli_sin;
socklen_t cli_len=sizeof(cli_sin);
int cli_fd;
cli_fd=accept(httpd,(struct sockaddr *)&cli_sin,&cli_len);//阻塞等待连接
if(cli_fd==-)
ServerError("Fail to accept");
cout<<"连接进来的IP: "<<inet_ntoa(cli_sin.sin_addr)<<":"<<ntohs(cli_sin.sin_port)<<endl;
return cli_fd;
}
int WebServer::ServerInit(u_short port)
{
struct sockaddr_in sin;
int on;
httpd=socket(PF_INET,SOCK_STREAM,);
if(httpd==-)
ServerError("Fail to Socket");
//init sockaddr_in
sin.sin_family=AF_INET;
sin.sin_port=htons(port);
sin.sin_addr.s_addr=htonl(INADDR_ANY);
bzero(&(sin.sin_zero),);
setsockopt(httpd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(::bind(httpd,(struct sockaddr *)&sin,sizeof(struct sockaddr))==-)
ServerError("Fail to bind");
//如果port指定为零那么就随机打开一个端口
if(port==)
{
socklen_t len=sizeof(sin);
if(getsockname(httpd,(struct sockaddr *)&sin,&len)==-)
ServerError("Fail to getsockname");
port=ntohs(sin.sin_port);
}
if(listen(httpd,)<)
ServerError("Fail to listen");
return port;
}
///////////////
int WebServer::get_line(int cli_fd,char * buf,int size)
{
int i=;
char c='\0';
int n;
while((i<size-)&&(c!='\n'))
{
n=recv(cli_fd,&c,,);
if(n>)
{
if(c=='\r')
{
n=recv(cli_fd,&c,,MSG_PEEK);
if((n>)&&(c=='\n'))
recv(cli_fd,&c,,);
else
c='\n';
}
buf[i]=c;
i++;
}
else
c='\n';
}
buf[i]='\0';
return i;
}
int WebServer::ServerError(string str)
{
perror(str.c_str());
exit(-);
}
int WebServer::ServerClose()
{
close(httpd);
return ;
}
int WebServer::Page_200(int cli_fd)
{
char buf[];
sprintf(buf, "HTTP/1.1 200 OK\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Server:wunaozai.cnblogs.com\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Content-Type: text/html\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "<HTML><HEAD><TITLE>Hello World\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "</TITLE></HEAD>\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "<BODY><h1>Hello World</h1>\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "</BODY></HTML>\r\n");
send(cli_fd, buf, strlen(buf), );
}
int WebServer::Page_501(int cli_fd)
{
char buf[];
sprintf(buf, "HTTP/1.1 501 Method Not Implemented\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Server:wunaozai.cnblogs.com");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Content-Type: text/html\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "</TITLE></HEAD>\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "</BODY></HTML>\r\n");
send(cli_fd, buf, strlen(buf), );
}
WebServer::~WebServer()
{
}
WebServer::WebServer()
{
}

  webserver.cpp

 #include "webserver.h"

 int main(int argc,char **argv)
{
WebServer ws;//实例化web服务器
ws.ServerInit();//打开8080端口
pid_t pid;
int cli_fd;
while()
{
cli_fd=ws.ServerAccept();//程序会在这个函数阻塞
ws.ServerRequest(cli_fd);//这个函数会创建一个进程对请求头进行处理并发送应答信息给客户端
}
ws.ServerClose();//关闭服务器 return ;
}

  makefile

 main:
g++ webserver.cpp -std=c++0x -g -o webserver
run:
./webserver

  下面这个是运行时的截图

  增加了几个函数get_line(由于socket的读取方式好像没有一行一行的读取)各种Page信息还有一个ServerRequest函数。

  ServerRequest:这个函数里面有一个fork函数创建多进程。一开始我是把fork的创建放在主函数的,然后ServerRequest不用fork函数。但是最后会出现一个问题就是,每次在客户端发出请求后服务器一直没有给出应答,客户端浏览器一直处于加载状态,然后强制性终止程序,浏览器才会有反映。不知道原因,弄了很久。一直在想以前写的那篇HTTP是没有问题的。一查才知道原来我以前用的请求头Connection:close 而浏览器现在这个Connection默认的值是keep-alive。是长连接。所以才会出现这个情况。

  get_line:由于socket没有一整行的读取数据,所以这里使用tinyhttpd这个程序里的代码。

  Page_200 Page_501 Page_404 ... ...

  到这里服务器可以简单的返回一个200ok的页面了。接下来要实现的是实现对第一行请求信息的处理,接下来的处理基本都是在ServerRequest这个函数里进行。

  带处理get/post方法的WEB服务器

 int WebServer::ServerRequest(int cli_fd)
{
char buf[];
int size=;
int i,j;
char method[];//用于保存请求方式
char url[];
memset(buf,,sizeof(buf));
//获取第一行请求信息 一般格式为: GET / HTTP/1.1
// POST / HTTP/1.1
size=get_line(cli_fd,buf,sizeof(buf));
cout<<"\t\t"<<buf<<endl;
i=,j=;
//截取第一个单词
while(!isspace(buf[j]) && (i<sizeof(method)-))
{
method[i]=buf[j];
i++;j++;
}
method[i]='\0';
//取第一个与第二个单词之间的空格
while(isspace(buf[j]) && (j<sizeof(buf)))
j++;
//截取第二个单词
i=;
while(!isspace(buf[j]) && (i<sizeof(url)-) && (j<sizeof(buf)))
{
url[i]=buf[j];
i++;j++;
}
url[i]='\0'; if(strcasecmp(method,"GET") && strcasecmp(method,"POST"))
{
Page_501(cli_fd);
return -;
} if(strcasecmp(method,"GET")==)
{
cout<<"此次请求的方式是GET方法"<<endl;
}
else if(strcasecmp(method,"POST")==)
{
cout<<"此次请求的方式是POST方法"<<endl;
}
cout<<"此次请求的地址为:"<<url<<endl; while((size>)&&strcmp("\n",buf))
{
size=get_line(cli_fd,buf,sizeof(buf));
} if(fork()==)
{
//处理阶段
//execl("/bin/ls","ls","/home/myuser/",NULL);
Page_200(cli_fd);
}
close(cli_fd);
return ;
}

  运行的结果

  可以看出只要在浏览器地址栏写上什么就可以在GET后截取到,只是中文就显示成16进制了

  还有这个成功获取第一个页面后会有一个获取/favicon.ico这个请求,这个是自动的,我没有在地址栏输入的。如果有学过静态页面HTML编写的就知道,这个是网页的图标,一般在主目录的根目录下。

  在这里没有看到图标是由于这个favicon.ico不是通过简单text/html的Content-Type显示的所以这里就没有,等以后实现image发送就可以看到了。好了这一小节就到这里了。

  参考资料: http://blog.csdn.net/hanchaoman/article/details/5685582

  本文地址: http://www.cnblogs.com/wunaozai/p/3936295.html

Socket网络编程--简单Web服务器(2)的更多相关文章

  1. Socket网络编程--简单Web服务器(6)

    本来是想实现ssl连接的,但是弄了好久都不成功,就索性不做了,等以后有能力再做了.所以这一小节就是本次的最后一节了.就简单的说几个注意点. 1.加个配置文件 使用单例模式,使用一个类,该类保存一些信息 ...

  2. Socket网络编程--简单Web服务器(1)

    这一次的Socket系列准备讲Web服务器.就是编写一个简单的Web服务器,具体怎么做呢?我也不是很清楚流程,所以我找来了一个开源的小的Web服务器--tinyhttpd.这个服务器才500多行的代码 ...

  3. Socket网络编程--简单Web服务器(3)

    上一小节已经实现了浏览器发送请求,然后服务器给出应答信息,然后浏览器显示出服务器发送过来的网页.一切看起来都是那么的美好.这一小节就准备实现可以根据地址栏url的不同来返回指定的网页.目前还不考虑带参 ...

  4. Socket网络编程--简单Web服务器(4)

    上一小节已经实现了对图片的传输,接下来就是判断文件是否为js,css,png等格式.我们增加一个函数用于判断格式 int WebServer::get_filetype(char *type,char ...

  5. Socket网络编程--简单Web服务器(5)

    这一小节我们将实现服务器对get和post的请求进行对cgi程序的调用.对于web服务器以前的章节已经实现了对get和post请求的调用接口,接下来给出对应接口的实现. int WebServer:: ...

  6. C++ socket 网络编程 简单聊天室

    操作系统里的进程通讯方式有6种:(有名/匿名)管道.信号.消息队列.信号量.内存(最快).套接字(最常用),这里我们来介绍用socket来实现进程通讯. 1.简单实现一个单向发送与接收 这是套接字的工 ...

  7. C#中使用Socket实现简单Web服务器

    上一篇博客中介绍了怎样使用socket访问web服务器.关键有两个: 熟悉Socket编程: 熟悉HTTP协议. 上一篇主要是通过socket来模拟浏览器向(任何)Web服务器发送(HTTP)请求,重 ...

  8. Java Web 基础(一) 基于TCP的Socket网络编程

    一.Socket简单介绍 Socket通信作为Java网络通讯的基础内容,集中了异常.I/O流模式等众多知识点.学习Socket通信,既能够了解真正的网络通讯原理,也能够增强对I/O流模式的理解. 1 ...

  9. python之Socket网络编程

    什么是网络? 网络是由节点和连线构成,表示诸多对象及其相互联系.在数学上,网络是一种图,一般认为专指加权图.网络除了数学定义外,还有具体的物理含义,即网络是从某种相同类型的实际问题中抽象出来的模型.在 ...

随机推荐

  1. [OpenCV-Python] OpenCV 中的图像处理 部分 IV (二)

    部分 IVOpenCV 中的图像处理 OpenCV-Python 中文教程(搬运)目录 16 图像平滑 目标 • 学习使用不同的低通滤波器对图像进行模糊 • 使用自定义的滤波器对图像进行卷积(2D 卷 ...

  2. python使用 requirements.txt 管理所需的包

    使用 requirements.txt 管理所需的包 2019/01/28 作者 若要与其他人共享项目.使用生成系统,或打算将项目复制到需要在其中还原环境的其他任何位置,必须指定项目需要的外部包. 建 ...

  3. sharc dsp 学习记录1---2014-07-30

    从今天开始记录学习sharc dsp过程中的点点滴滴吧.   DPI:Digital Peripheral Interface DAI:Digital Audio Interface   SHARC ...

  4. metasploit常用服务扫描和利用模块

    metasploit常用服务扫描和利用模块 SMB扫描 smb枚举auxiliary/scanner/smb/smb_enumusers 扫描命名管道auxiliary/scanner/smb/pip ...

  5. MySQL 5.7.14 net start mysql 服务无法启动

    解决方法: 1.mysqld  --initialize 初始化data目录 2.重新输入net start mysql命令 补充,服务停止的方法:net stop mysql

  6. 说出ArrayList,Vector, LinkedList的存储性能和特性

     ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插 ...

  7. C# DataGridView插入DB

    public static bool ContrastColumns(DataColumnCollection co1, DataGridViewColumnCollection co2) { boo ...

  8. 获取AFP服务信息

    获取AFP服务信息   如果苹果系统开放TCP 548端口,说明其开启了AFP服务.这个时候,可以使用Nmap的afp-serverinfo脚本获取对应的服务信息.获取的信息包括服务名.机器类型.AF ...

  9. 安卓 运行、调试 配置 android Run/debug configurations

    android  运行.调试 配置 android  Run/debug configurations 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq. ...

  10. SolidWorks知识积累系列-01

    Solidworks学习 1. 基本知识点总结 基准视图 主视图:从前往后看,前视基准 俯视图:从上往下看,上视基准 侧视图:从右向左看,右视基准 草图要求 单封闭性,草图要依附于某个位置 绘制大概形 ...