上一小节通过阅读开源的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. python连接mysql、sqlserver、oracle、postgresql数据库的一些封装

    包括python连接数据库,以及django下配置连接数据库 # -*- coding:utf-8 -*- import psycopg2 import pymysql import pymssql ...

  2. mybatis DATE_FORMAT 格式化时间输出

    参考:http://www.cnblogs.com/yangy608/p/3950095.html 一.在oracle中,当想把字符串为‘2011-09-20 08:30:45’的格式转化为日期格式, ...

  3. hdu 3579 Hello Kiki【中国剩余定理】(模数不要求互素)(模板题)

    <题目链接> 题目大意: 给你一些模数和余数,让你求出满足这些要求的最小的数的值. 解题分析: 中国剩余定理(模数不一定互质)模板题 #include<stdio.h> usi ...

  4. 机器学习数据处理时label错位对未来数据做预测

    这篇文章继上篇机器学习经典模型简单使用及归一化(标准化)影响,通过将测试集label(行)错位,将部分数据作为对未来的预测,观察其效果. 实验方式 以不同方式划分数据集和测试集 使用不同的归一化(标准 ...

  5. 多线程出现 java.lang.NumberFormatException: multiple points

    多线程下导入数据,发现同一个文件每次导入成功的数据量都不一致,经检查,某些数据偶尔会报错  java.lang.NumberFormatException: multiple points 原因是导入 ...

  6. android monitor 汉化

    作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com E-mail: 313134555 @qq.com === == ============= = ...

  7. 4572: [Scoi2016]围棋 轮廓线DP KMP

    国际惯例的题面:这种题目显然DP了,看到M这么小显然要状压.然后就是具体怎么DP的问题.首先我们可以暴力状压上一行状态,然后逐行转移.复杂度n*3^m+3^(m*2),显然过不去. 考虑状态的特殊性, ...

  8. 非常简单的方法实现ViewPager自动循环轮播

    非常简单的方法实现ViewPager自动循环轮播,见红色代码部分,其它的代码可以忽略不看. 简洁高效是我解决问题的首要出发点. package com.shuivy.happylendandreadb ...

  9. python高级特性:切片/迭代/列表生成式/生成器

    廖雪峰老师的教程上学来的,地址:python高级特性 下面以几个具体示例演示用法: 一.切片 1.1 利用切片实现trim def trim(s): while s[:1] == " &qu ...

  10. 状态压缩+矩阵乘法hdu-4332-Constructing Chimney

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4332 题目意思: 用1*1*2的长方体构造一个中间为空的底面3*3的立体烟囱. 解题思路: 实际上就 ...