本文主要讲述如何用C/C++在Linux环境下写一个简单的支持并发的web服务器,并不考虑服务器的健壮性、安全性、性能等一系列因素。 
在本文中,该服务器仅支持GET请求。

项目地址:https://github.com/imndszy/webserver 
开发环境:ubuntu 16.04,

在编写一个服务器之前,我们需要对socket以及网络协议尤其是http协议有基础的了解,如果不了解,请参阅Beej’s Guide to Network ProgrammingUNIX网络编程卷一

当我们打开一个页面时,浏览器会向相应URL所绑定的IP地址发送请求,然后远端的webserver就解析并处理这个请求,比如说如果是GET请求,就会返回一个页面或其他数据。 
那么一个webserver的工作流程是怎样的呢?在此我们只讨论一个最简单的服务器且对单个进程而言。 
 
上图中,socket()用于创建一个套接字,该套接字用于监听某个端口,在bind()中套接字与端口绑定,当然还有一些其他参数,随后通过listen()进行监听,这时候就进入服务器程序的主循环,当有连接建立后,accpet()被调用并返回一个新的套接字用于处理连接,这时派生一个子进程进行处理,子进程中recv()从缓冲区读取数据交由相关函数处理,处理完毕后的结果通过send()发送出去,随后关闭用于处理的套接字,子进程退出。

至此,一个简易webserver的雏形就显现出来了。

首先是如何创建及设置套接字的代码:

int sockfd;
char *port;
struct addrinfo hints, *servinfo, *p;
int rv; //从命令行参数读取要绑定到的端口号
if(argc != 2){
fprintf(stderr,"usage:%s <port>\n",argv[0]);
exit(1);
}
port = argv[1]; memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; if((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0){
fprintf(stderr, "getaddrinfo:%s\n", gai_strerror(rv));
return 1;
} for(p = servinfo;p != NULL;p = p->ai_next){
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("server: socket");
continue;
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
exit(1);
}
if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
close(sockfd);
perror("server:bind");
continue;
} break;
}

接下来是主循环部分:

while(1){
sin_size = sizeof(other_addr);
connfd = accept(sockfd, (struct sockaddr *)&other_addr, &sin_size);
if(connfd == -1){
perror("accept");
continue;
} inet_ntop(other_addr.ss_family, get_in_addr((struct sockaddr*)&other_addr), s, sizeof(s));
printf("server:got connection from %s\n",s); if(!fork()){
close(sockfd); //fork以后子进程中也会有一个sockfd
if(recv(connfd, buf, MAXBUF, 0) == -1) {
perror("receive");
close(connfd);
exit(1);
} sscanf(buf,"%s %s %s",method,uri,version); if(!strcasecmp(method, "GET")) //如果是GET请求
process_http_get(connfd,uri,filename);
else
client_error(connfd,method,"501","Not Implemented","Webserver does not implement this method"); close(connfd);
exit(0);
}
close(connfd);
}

如何写一个简单的webserver(一):最简实现的更多相关文章

  1. 用Python写一个简单的Web框架

    一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...

  2. 如何写一个简单的http服务器

    最近几天用C++写了一个简单的HTTP服务器,作为学习网络编程和Linux环境编程的练手项目,这篇文章记录我在写一个HTTP服务器过程中遇到的问题和学习到的知识. 服务器的源代码放在Github. H ...

  3. 如何写一个简单的shell

    如何写一个简单的shell 看完<UNIX环境高级编程>后我就一直想写一个简单的shell来作为练习,因为有事断断续续的写了好几个月,如今写了差不多来总结一下. 源代码放在了Github: ...

  4. 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”

    这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...

  5. 一步一步写一个简单通用的makefile(三)

    上一篇一步一步写一个简单通用的makefile(二) 里面的makefile 实现对通用的代码进行编译,这一章我将会对上一次的makefile 进行进一步的优化. 优化后的makefile: #Hel ...

  6. Java写一个简单学生管理系统

    其实作为一名Java的程序猿,无论你是初学也好,大神也罢,学生管理系统一直都是一个非常好的例子,初学者主要是用数组.List等等来写出一个简易的学生管理系统,二.牛逼一点的大神则用数据库+swing来 ...

  7. (2)自己写一个简单的servle容器

    自己写一个简单的servlet,能够跑一个简单的servlet,说明一下逻辑. 首先是写一个简单的servlet,这就关联到javax.servlet和javax.servlet.http这两个包的类 ...

  8. express 写一个简单的web app

    之前写过一个简单的web app, 能够完成注册登录,展示列表,CURD 但是版本好像旧了,今天想写一个简单的API 供移动端调用 1.下载最新的node https://nodejs.org/zh- ...

  9. 写一个简单的C词法分析器

    写一个简单的C词法分析器 在写本文过程中,我参考了<词法分析器的实现>中的一些内容.这里我们主要讨论写一个C语言的词法分析器. 一.关键字 首先,C语言中关键字有: auto.break. ...

随机推荐

  1. Mybatis_映射文件配置

    获取自增主键的值 若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标 ...

  2. 编写高质量代码改善C#程序的157个建议——建议24:迭代器应该是只读的

    建议24:迭代器应该是只读的 如果注意观察会发现,FCL中的迭代器只有GetEnumerator方法,没有SetEnumerator方法,所有的集合类也没有一个可以写的迭代器属性.原因有二: 一:这违 ...

  3. javaScript入门之常用事件

    JS中的常用事件 onfocus/onblur:聚焦离焦事件,用于表单校验的时候比较合适. onclick/ondblclick:鼠标单击和双击事件 onkeydown/onkeypress:搜索引擎 ...

  4. Jquery delegate的理解

    $(".step4TagList .albumShow", "#divStep4").delegate(" .abmFct .icoRt", ...

  5. C#构造函数用法

    1.实例构造函数 2.静态构造函数 3.私有构造函数 例: 创建一个类 using System; using System.Collections.Generic; using System.Lin ...

  6. .net core 图片合并,图片水印,等比例缩小,SixLabors.ImageSharp

    需要引用 SixLabors.ImageSharp 和SixLabors.ImageSharp.Drawing 引用方法 NuGet包管理 添加程序包来源 https://www.myget.org/ ...

  7. Regex 常用的正则表达式

    .校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^\d{m,n}$ 零和非零开头的数字:^(0|[1-9][0-9]*)$ 非 ...

  8. NSRect 位置和尺寸

    前言 结构体 这个结构体用来表示事物的坐标点和宽高度. typedef CGRect NSRect; struct CGRect { CGPoint origin; CGSize size; }; t ...

  9. Size Assertion

    Size Assertion每一个响应包含的字节大小,可以设置大小等于,大于,小于,不等于给定的字节数. Apply to:应用范围,一般勾选Main samle only即可. Response S ...

  10. java 图书馆系统 练习

    话不多说 娱乐 ================================================== book 类(书本的基础属性) package 图书管理系统01; /** * @ ...