前言Mongoose是一个开源的web服务器程序,只有两个文件mongoose。它是用C语言编写的,因此非常适合在嵌入式设备中应用。目前Mongoose可以支持嵌入到C/C++、Python、C#中。本文主要介绍如何在Linux环境下将Mongoose代码嵌入到C应用程序中,完成服务器的部署。为了更快的熟悉Web服务器的特性,在介绍服务器的部署之前,有必要介绍一下服务器与客户端之间网络通信的基本传输技术。在TCP连接过程中,浏览器和Web服务器之间的请求和响应是使用HTTP协议进行的,但是HTTP协议只是应用层使用的协议,传输层的数据通信由TCP协议。TCP协议是面向连接的协议。它一般包括三个过程:连接建立、数据传输、连接关闭。使用“三次握手”完成连接的建立。连接成功后,就可以发送数据了。在关闭连接之前,为了保证数据正确传输完成后,需要“四挥手”关闭连接。我们来分析一下“三次握手”和“四次挥手”的过程。1、客户端(192.20.1.12)“三次握手”连接服务器(192.20.1.122)第一次握手:客户端向服务器发送连接请求报文SYN(flag)=1,表示客户端请求建立连接Seq=x,序列号随机生成第二次握手:服务器收到报文后确认连接请求,并向客户端返回响应报文SYN(flag)=1ACK(flag)=1,表示服务器确认请求ACK(num)=x+1(ClientSeq+1)Seq=y,随机序列号第三次握手:客户端收到响应后,检查ACK(num)和ACK(flag)值,并给服务器确认ACK(flag)=1。校验无误,客户端发送确认ACK(num)=y+1(serverSeq+1),客户端发送确认号Seq=x+1完成三次握手,客户端和服务器开始传输数据2、客户端(192.20.1.12)与服务器(192.20.1.122)“四次挥手”断开连接。客户端和服务端都可以主动发起断开TCP连接的请求。这里以客户端发起的请求为例。第一波:客户端向服务器发送释放请求报文段,但不是在发送数据FIN(flag)=1,客户端请求终止连接ACK(num)=z,上传数据的Seq值Seq=x、上传数据DataACK(num)值第二次挥手:服务器收到释放连接请求后,返回响应报文给客户端。此时连接处于半关闭状态,即客户端不再向服务端发送数据,但是如果服务端还有数据要发送给客户端,还是可以发送的。client只要正确接收到数据,还是应该向server发送一个acknowledgementACK(flag)=1,表示server确认请求ACK(num)=x+1(ClientSeq+1)Seq=y,随机生成一个序列号第三次挥手:如果服务器不再向客户端发送数据,服务器发送一个连接释放响应给客户端,关闭反向连接FIN(flag)=1,服务器请求终止connectionACK(flag)=1,服务器发送确认ACK(num)=x+1,与第二次挥手一致Seq=y,与第二次挥手一致,第四次挥手:客户端收到后给服务器确认响应,并释放从服务器到客户端的连接。ACK(flag)=1,校验无误,客户端发送确认ACK(num)=y+1(serverSeq+1),客户端发送确认号Seq=x+1完成四次挥手,客户端和服务端都连接完成释放有人可能会有疑问,为什么连接协议的建立是“三次握手”,而关闭连接是“四次挥手”呢?由于TCP连接是全双工的,当连接关闭时,服务端收到客户端的FIN报文通知,这只代表客户端没有数据要发送给服务端,但并不是服务端的所有数据都有了已发送给客户端。发送一些数据后关闭连接。所以ACK报文和FIN报文分开发送,形成“四次挥手”。Socket编程在网络编程中不可避免的要用到Socket编程。Socket将TCP/IP协议封装成API接口,方便程序员使用TCP/IP协议栈。Socket是以“打开-读取/写入-关闭”模式实现的。以TCP协议通信的Socket为例,交互过程大致如下。这里从服务器的角度来介绍一下这个过程:createsocket()。socket()根据指定的协议族(IPv4/IPv6)和通信类型(TCP/UDP)在sockfs文件系统中分配一个与socket描述符关联的资源,进程可以像访问一个已经打开的文件一样访问socket资源。绑定bind()。bind()在此套接字上绑定指定的端口号和IP地址。归根结底,网络通信可以看作是不同计算机上的进程间通信。IP地址只能确定进程所在的计算机,结合端口号可以在整个网络中唯一确定一个网络进程。当IP地址为INADDR_ANY(0)时,表示本地计算机的默认IP地址。听听()。服务器socket端口一直处于等待状态,监听网络中所有客户端对端口号的请求,随时准备接收客户端的连接acceptaccept()。服务器socket监听到客户端请求后,将请求放入等待队列,accept()从等待队列中取出连接请求,创建新的socket进行操作,原监听socket不受影响。读/写write()/read()。发送内容就是向套接字写入内容,读取内容就是从套接字中获取内容并关闭close()。close()关闭套接字。在MakeFileC程序的编译过程中,依次经历了预处理、编译、汇编、链接四个阶段。在Linux平台的shell中输入gcc命令完成以上步骤。但是gcc在编译包含很多源文件的工程时,编译每个源文件,然后全部连接起来,效率很低,尤其是当用户只修改其中一个文件时,很多生成的目标文件都不会改变,而且有无需转换每个文件都重新编译一遍。为解决gcc编译效率低下的问题,Windows平台的集成开发环境(IDE)提供了项目管理器。用户只需点击一个“make”按钮即可启动项目管理器自动编译整个程序。在Linux平台上,make工具和makefile提供了一种简单有效的项目管理方法。makefile决定了编译项目的规则。make工具可以通过make命令启动,根据makefile中的编译规则编译链接程序,最终生成可执行文件。从Mongoose官网下载的源程序例程,提供了一个makefile文件,可以直接使用。文件1:PROG=simplest_web_serverMODULE_CFLAGS=-DMG_DISABLE_DAV_AUTH-DMG_ENABLE_FAKE_DAVLOCKinclude../examples.mk文件2:SUBDIRS=$(sort$(dir$(wildcard./*/)))SUBDIRS:=$(filter-out././CC3200/./ESP32_IDF/./ESP8266_RTOS/./mbed/./MSP432/./nRF51/./nRF52/./NXP_K64/./NXP_LPC4088/./PIC32/./STM32F4_CC3100/./TM4C129/./WinCE/,$(SUBDIRS))ifeq($(OS),Windows_NT)SUBDIRS:=$(filter-out./netcat/./raspberry_pi_mjpeg_led/./captive_dns_server/,$(SUBDIRS))endif.PHONY:$(SUBDIRS)all:$(SUBDIRS)$(SUBDIRS):@$(MAKE)-C$@clean:fordin$(SUBDIRS);做$(MAKE)-C$$d清理;这里做的是为了更好的解释makefile文件的功能,只是写了生成可执行文件的编译规则。将simplest_web_server.cmongoose.h编译成中间文件simplest_web_server.o,将mongoose.cmongoose.h编译成中间文件mongoose.o,然后将.o文件和需要的库文件链接成最终的可执行文件.allall:simplest_web_server.omongoose.ogcc-oallsimplest_web_server.omongoose.osimplest_web_server.o:simplest_web_server.cmongoose.hgcc-csimplest_web_server.cmongoose.o:mongoose.cmongoose.hgcc-cmongoose.cLinux平台运行Mongoose开源程序(官网下载6.9版本),提供了多种功能的例程。这里选择最简单的_web_server(基本web服务器程序)的main函数作为主要应用程序。创建Socket、绑定、监听等过程在以下函数中实现:nc=mg_bind(&mgr,s_http_port,ev_handler);循环mg_mgr_poll()遍历所有Socket,接收新连接和收发数据,同时调用Socket对应的事件处理函数。对于(;;){mg_mgr_poll(&mgr,1000);}用户实现的事件处理接口如下,其中可以实现服务器后台的业务逻辑功能:staticvoidev_handler(structmg_connection*nc,intev,void*p){if(ev==MG_EV_HTTP_REQUEST){mg_serve_http(nc,(structhttp_message*)p,s_http_server_opts);}}这里的例子在mg_serve_http()中通过Linux文件系统找到index.html文件返回给浏览器Implementor:if(opts.index_files==NULL){opts.index_files="index.html,index.htm,index.shtml,index.cgi,index.php";将mongoose.h、mongoose.c和simplest_web_server。c复制到自己的程序目录下,index.html也放在这个目录下。在LinuxShell命令行输入make命令,make工具会自动在当前目录下搜索“makefile”文件进行分析,生成最终的可执行文件.all。运行.all可以启动web服务器程序。在服务器机器的浏览器中输入http://localhost:1200/访问web服务器,如下图:通过另一台电脑的浏览器输入http://192.168.20.1:1200/,如下图下图显示:
