上一小节通过阅读开源的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. P1019 单词接龙 字符串回溯

    题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合 ...

  2. python的time模块总结

    python的time模块与random模块 目录 time模块 time模块 三种时间表示 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp) : 通常来说,时间戳表示的是 ...

  3. 简述synchronized和java.util.concurrent.locks.Lock的异同?

    主要相同点:Lock能完成synchronized所实现的所有功能 . 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能.synchronized会自动释放锁,而Lock一 ...

  4. Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(三)--前后端环境配置

    前言 之前都是介绍一些基础知识,在这一节,我们就要开始实战coding了.正所谓磨刀不误砍柴工,准备工作显得尤为重要.很多demo只是追求效果的实现,并不注重整个demo的架构性.从我个人的角度看来, ...

  5. [BZOJ3674]可持久化并查集加强版&[BZOJ3673]可持久化并查集 by zky

    思路: 用主席树维护并查集森林,每次连接时新增结点. 似乎并不需要启发式合并,我随随便便写了一个就跑到了3674第一页?3673是这题的弱化版,本来写个暴力就能过,现在借用加强版的代码(去掉异或),直 ...

  6. java给时间格式化

    package com.apress.springrecipes.sequence; import java.text.DateFormat;import java.text.SimpleDateFo ...

  7. 你不知道的js

    1.<a>标签 (1)href属性包含超链接或超链接指向的URL或URL片段,URL 片段是哈希标记(#)前面的名称,哈希标记指定当前文档中的内部目标位置(HTML 元素的 ID).URL ...

  8. register form

    <code class="language-html"><div class="width100 marT15 content_news_list&qu ...

  9. HTML篇之CSS样式:<button></button>按钮变成超链接<a></a>的样式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 使用OClint进行iOS项目的静态代码扫描

    使用OClint进行iOS项目的静态代码扫描 原文链接:http://blog.yourtion.com/static-code-analysis-ios-using-oclint.html 最近需要 ...