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

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

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

  简单创建web服务器

  webserver.h

  1 #include <iostream>
  2 #include <string>
  3 #include <string.h>
  4 #include <stdio.h>
  5 #include <stdlib.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/socket.h>
  9 #include <netinet/in.h>
 10 #include <arpa/inet.h>
 11 #include <unistd.h>
 12 #include <pthread.h>
 13 #include <thread>//使用c++11的多线程
 14
 15 using namespace std;
 16
 17 class WebServer
 18 {
 19     public:
 20         WebServer();
 21         ~WebServer();
 22         int ServerInit(u_short port);
 23         int ServerError(string str);
 24         int ServerAccept();
 25         int ServerClose();
 26         int ServerRequest(int cli_fd);
 27         int get_line(int cli_fd,char * buf,int size);//来自tinyhttpd
 28
 29         int Page_200(int cli_fd);
 30         int Page_501(int cli_fd);
 31     private:
 32         int httpd;
 33 };
 34
 35 int WebServer::ServerRequest(int cli_fd)
 36 {
 37     char buf[1024];
 38     int size=1024;
 39     int i=1;
 40     memset(buf,0,sizeof(buf));
 41     while((i>0)&&strcmp("\n",buf))
 42     {
 43         i=get_line(cli_fd,buf,sizeof(buf));
 44         cout<<buf;
 45     }
 46     if(fork()==0)
 47     {
 48         //处理阶段
 49         execl("/bin/ls","ls","/home/myuser/",NULL);
 50     }
 51     Page_200(cli_fd);
 52     close(cli_fd);
 53     return 0;
 54 }
 55 int WebServer::ServerAccept()
 56 {
 57     struct sockaddr_in cli_sin;
 58     socklen_t cli_len=sizeof(cli_sin);
 59     int cli_fd;
 60     cli_fd=accept(httpd,(struct sockaddr *)&cli_sin,&cli_len);//阻塞等待连接
 61     if(cli_fd==-1)
 62         ServerError("Fail to accept");
 63     cout<<"连接进来的IP: "<<inet_ntoa(cli_sin.sin_addr)<<":"<<ntohs(cli_sin.sin_port)<<endl;
 64     return cli_fd;
 65 }
 66 int WebServer::ServerInit(u_short port)
 67 {
 68     struct sockaddr_in sin;
 69     int on;
 70     httpd=socket(PF_INET,SOCK_STREAM,0);
 71     if(httpd==-1)
 72         ServerError("Fail to Socket");
 73     //init sockaddr_in
 74     sin.sin_family=AF_INET;
 75     sin.sin_port=htons(port);
 76     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 77     bzero(&(sin.sin_zero),8);
 78     setsockopt(httpd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
 79     if(::bind(httpd,(struct sockaddr *)&sin,sizeof(struct sockaddr))==-1)
 80         ServerError("Fail to bind");
 81     //如果port指定为零那么就随机打开一个端口
 82     if(port==0)
 83     {
 84         socklen_t len=sizeof(sin);
 85         if(getsockname(httpd,(struct sockaddr *)&sin,&len)==-1)
 86             ServerError("Fail to getsockname");
 87         port=ntohs(sin.sin_port);
 88     }
 89     if(listen(httpd,100)<0)
 90         ServerError("Fail to listen");
 91     return port;
 92 }
 93 ///////////////
 94 int WebServer::get_line(int cli_fd,char * buf,int size)
 95 {
 96     int i=0;
 97     char c=‘\0‘;
 98     int n;
 99     while((i<size-1)&&(c!=‘\n‘))
100     {
101         n=recv(cli_fd,&c,1,0);
102         if(n>0)
103         {
104             if(c==‘\r‘)
105             {
106                 n=recv(cli_fd,&c,1,MSG_PEEK);
107                 if((n>0)&&(c==‘\n‘))
108                     recv(cli_fd,&c,1,0);
109                 else
110                     c=‘\n‘;
111             }
112             buf[i]=c;
113             i++;
114         }
115         else
116             c=‘\n‘;
117     }
118     buf[i]=‘\0‘;
119     return i;
120 }
121 int WebServer::ServerError(string str)
122 {
123     perror(str.c_str());
124     exit(-1);
125 }
126 int WebServer::ServerClose()
127 {
128     close(httpd);
129     return 0;
130 }
131 int WebServer::Page_200(int cli_fd)
132 {
133     char buf[1024];
134     sprintf(buf, "HTTP/1.1 200 OK\r\n");
135     send(cli_fd, buf, strlen(buf), 0);
136     sprintf(buf, "Server:wunaozai.cnblogs.com\r\n");
137     send(cli_fd, buf, strlen(buf), 0);
138     sprintf(buf, "Content-Type: text/html\r\n");
139     send(cli_fd, buf, strlen(buf), 0);
140     sprintf(buf, "\r\n");
141     send(cli_fd, buf, strlen(buf), 0);
142     sprintf(buf, "<HTML><HEAD><TITLE>Hello World\r\n");
143     send(cli_fd, buf, strlen(buf), 0);
144     sprintf(buf, "</TITLE></HEAD>\r\n");
145     send(cli_fd, buf, strlen(buf), 0);
146     sprintf(buf, "<BODY><h1>Hello World</h1>\r\n");
147     send(cli_fd, buf, strlen(buf), 0);
148     sprintf(buf, "</BODY></HTML>\r\n");
149     send(cli_fd, buf, strlen(buf), 0);
150 }
151 int WebServer::Page_501(int cli_fd)
152 {
153     char buf[1024];
154     sprintf(buf, "HTTP/1.1 501 Method Not Implemented\r\n");
155     send(cli_fd, buf, strlen(buf), 0);
156     sprintf(buf, "Server:wunaozai.cnblogs.com");
157     send(cli_fd, buf, strlen(buf), 0);
158     sprintf(buf, "Content-Type: text/html\r\n");
159     send(cli_fd, buf, strlen(buf), 0);
160     sprintf(buf, "\r\n");
161     send(cli_fd, buf, strlen(buf), 0);
162     sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
163     send(cli_fd, buf, strlen(buf), 0);
164     sprintf(buf, "</TITLE></HEAD>\r\n");
165     send(cli_fd, buf, strlen(buf), 0);
166     sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
167     send(cli_fd, buf, strlen(buf), 0);
168     sprintf(buf, "</BODY></HTML>\r\n");
169     send(cli_fd, buf, strlen(buf), 0);
170 }
171 WebServer::~WebServer()
172 {
173 }
174 WebServer::WebServer()
175 {
176 }

  webserver.cpp

 1 #include "webserver.h"
 2
 3 int main(int argc,char **argv)
 4 {
 5     WebServer ws;//实例化web服务器
 6     ws.ServerInit(8080);//打开8080端口
 7     pid_t pid;
 8     int cli_fd;
 9     while(1)
10     {
11         cli_fd=ws.ServerAccept();//程序会在这个函数阻塞
12         ws.ServerRequest(cli_fd);//这个函数会创建一个进程对请求头进行处理并发送应答信息给客户端
13     }
14     ws.ServerClose();//关闭服务器
15
16     return 0;
17 }

  makefile

1 main:
2         g++ webserver.cpp -std=c++0x -g -o webserver
3 run:
4         ./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服务器

 1 int WebServer::ServerRequest(int cli_fd)
 2 {
 3     char buf[1024];
 4     int size=1024;
 5     int i,j;
 6     char method[255];//用于保存请求方式
 7     char url[512];
 8     memset(buf,0,sizeof(buf));
 9     //获取第一行请求信息 一般格式为: GET / HTTP/1.1
10     //                               POST / HTTP/1.1
11     size=get_line(cli_fd,buf,sizeof(buf));
12     cout<<"\t\t"<<buf<<endl;
13     i=0,j=0;
14     //截取第一个单词
15     while(!isspace(buf[j]) && (i<sizeof(method)-1))
16     {
17         method[i]=buf[j];
18         i++;j++;
19     }
20     method[i]=‘\0‘;
21     //取第一个与第二个单词之间的空格
22     while(isspace(buf[j]) && (j<sizeof(buf)))
23         j++;
24     //截取第二个单词
25     i=0;
26     while(!isspace(buf[j]) && (i<sizeof(url)-1) && (j<sizeof(buf)))
27     {
28         url[i]=buf[j];
29         i++;j++;
30     }
31     url[i]=‘\0‘;
32
33     if(strcasecmp(method,"GET") && strcasecmp(method,"POST"))
34     {
35         Page_501(cli_fd);
36         return -1;
37     }
38
39     if(strcasecmp(method,"GET")==0)
40     {
41         cout<<"此次请求的方式是GET方法"<<endl;
42     }
43     else if(strcasecmp(method,"POST")==0)
44     {
45         cout<<"此次请求的方式是POST方法"<<endl;
46     }
47     cout<<"此次请求的地址为:"<<url<<endl;
48
49     while((size>0)&&strcmp("\n",buf))
50     {
51         size=get_line(cli_fd,buf,sizeof(buf));
52     }
53
54     if(fork()==0)
55     {
56         //处理阶段
57         //execl("/bin/ls","ls","/home/myuser/",NULL);
58         Page_200(cli_fd);
59     }
60     close(cli_fd);
61     return 0;
62 }

  运行的结果

  可以看出只要在浏览器地址栏写上什么就可以在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

时间: 2024-10-25 03:36:04

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

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

这一次的Socket系列准备讲Web服务器.就是编写一个简单的Web服务器,具体怎么做呢?我也不是很清楚流程,所以我找来了一个开源的小的Web服务器--tinyhttpd.这个服务器才500多行的代码,使用C语言.这一小节就不讲别的内容了.就对这个程序进行一些注释和讲解了. 主函数: 1 int main(void) 2 { 3 int server_sock = -1; 4 u_short port = 0; 5 int client_sock = -1; 6 struct sockaddr_

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

本来是想实现ssl连接的,但是弄了好久都不成功,就索性不做了,等以后有能力再做了.所以这一小节就是本次的最后一节了.就简单的说几个注意点. 1.加个配置文件 使用单例模式,使用一个类,该类保存一些信息,例如一个配置类的一个属性为PAGE404的字符串,该字符串保存一个文件地址,然后我们的Page_404函数就可以用access判断PAGE404这个字符串对应的文件是否存在,如果存在那么如果是404页面那么就cat这个文件,而不是默认的404函数里面的页面.还有个端口什么的都是通过一个类似宏定义一

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

上一小节已经实现了对图片的传输,接下来就是判断文件是否为js,css,png等格式.我们增加一个函数用于判断格式 1 int WebServer::get_filetype(char *type,char *path)//用于判断该url指向文件的后缀 2 { 3 if(strstr(path,".html")) 4 strcpy(type,"text/html"); 5 else if(strstr(path,".gif")) 6 strcpy

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

这一小节我们将实现服务器对get和post的请求进行对cgi程序的调用.对于web服务器以前的章节已经实现了对get和post请求的调用接口,接下来给出对应接口的实现. 1 int WebServer::ServerGetFunction(int cli_fd,char *path,char *args) 2 { 3 ServerExecuteCGI(cli_fd,path,args); 4 return 0; 5 } 6 int WebServer::ServerPostFunction(in

【Web后端笔记】基于Socket实现的简单Web服务器搭建

我们在地址栏中输入的内容称为通用资源标记符(Universal Resource Identifier,URI)它有很多种样式,在Web中我们通常称为统一资源定位符(Uniform Resource Locator,URL)的形式,它的格式如下: 协议://主机[.端口号][绝对路径[?参数]] 在http://www.cnblogs.com/DebugLZQ/中,http表示协议名称;www.cnblogs.com表示主机的地址:可选的端口号没有出现,那么,将使用http协议默认的端口号80:

Java网络编程 - 浅析web服务器与浏览器的实现原理

我们基本每天都在通过WEB浏览器,去浏览一些新闻,看看视频之类的. 众所周知,这就是所谓的B/S结构(Browser/Server,浏览器/服务器模式),是WEB兴起后的一种网络结构模式,WEB浏览器是客户端最主要的应用软件. 那顺道就来简单的看一下,所谓的Web服务器(例如知名的Tomcat)与浏览器,基本的实现原理是什么样的呢? 首先可以明确的就是,例如我们所做的通过浏览器输入一个地址,访问一个网页的操作. 实际对应的底层操作简单来说就是:客户端(浏览器)面向于WEB服务器的网络通信. 那么

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

原文地址:https://www.cnblogs.com/mq0036/p/6656888.html 最近有个web的小项目,但公司的电脑无法安装IIS,所以就想自己来实现个Web server服务器,原本想了下,也就是socket处理http请求,于是就在博客园中搜索了"socket实现web server",结果还真搜索到一些文章,于是从中找了几个做参考,如下: C#中使用Socket实现简单Web服务器 C#中使用Socket模拟请求Web服务器过程 C#中自己动手创建一个Web

Socket实现简单Web服务器

上一篇博客中介绍了怎样使用socket访问web服务器.关键有两个: 1)熟悉Socket编程: 2)熟悉HTTP协议. 上一篇主要是通过socket来模拟浏览器向(任何)Web服务器发送(HTTP)请求,重点在浏览器端.本篇博客则反过来讲一下怎样使用socket来实现Web服务器,怎样去接收.分析.处理最后回复来自浏览器的HTTP请求. HTTP协议是浏览器和Web服务器都需要遵守的一种通信规范,如果我们编写一个程序,正确遵守了HTTP协议,那么理论上讲,这个程序可以具备浏览器.甚至Web服务

Socket网络编程初探

MarkdownPad Document Socket网络编程初探 客户端/服务器架构 即C/S架构,其实web服务在某种意义上也算是C/S架构 一个特点是服务器端持续运行对外提供服务 为何学习socket一定要先学习互联网协议: C/S架构的软件是基于网络进行通信的 网络的核心就是一堆协议,即标准,想要开发一款基于网络通信的软件就必须遵循这些标准 socket是处在应用层和传输层中间的一组接口 说到这,那么socket到底是个什么呢?Socket是应用层与TCP/IP协议族通信的中间软件抽象层