本文是基于嵌入式物联网研发工程师的视觉对网络编程和web编程进行阐述。对于专注J2EE后端服务开发的童鞋们来说,这篇文章可能稍显简单。但是网络编程和web编程对于绝大部分嵌入式物联网工程师来说是一块真空领域。
的确,物联网研发应该以团队协作分工的方式进行,所以有嵌入式设备端、网关、web前端、APP、后端开发等专属岗位。作为系统架构师,自然需要掌握各种岗位的关键技术。作为嵌入式工程师,掌握网络编程、web编程,能够极大地拓展自己的视野和架构思维,能够主动地对系统的各种协议和应用场景提出优化的见解,而不仅仅是接受任务摊派。至少,能够在不需要依赖后端工程师的情况,能够快速搭建一个物联网demo系统。因此,掌握一些基本的网络编程、web编程技能,对于提升物联网研发工程师的开发能力是非常重要的。
本文可以视为嵌入式企鹅圈发布微信Wifi接入解决方案的首篇原创技术分享。微信Wifi接入方案系列技术分享将于2016年春节后陆续公开,敬请关注。本篇文章对物联网涉及的知识进行概述,之后的文章再进行详细的指导开发。
一、 OSI七层模型和TCP/IP四层模型
OSI七层模型是网络协议的理论研究模型,或者可以称为理想的模型,而TCP/IP四层模型才是事实标准,是已经被广泛使用的模型。两者之间的关联图示如下:
对于两种模型的解读,我想说的是作为开发人员不必强行去理解各层的含义,例如会话层负责什么,表示层负责什么。当你在开发过程中没有涉及到这些层次所解决的问题的时候,你想理解并记住是比较困难的。但是,当你遇到问题并需要去解决的时候,这时你一定会对这些层次的职责非常清晰。
衡量一个物联网平台或者协议是否实用的非常关键的因素是它提供的消息触达能力,其直接影响物联网应用开发。所以,我们从消息触达能力去分析TCP/IP这个事实标准模型。我们设想以下场景,并进行分析。
1.网络接口层。路由器1和wifi音箱、空调、热水器组成一个家庭局域网,其使用wifi(802.11)协议进行通信。该协议定义了物理信号、数据帧格式、丢包重发机制、流量控制等等。这些都是网络接口层的任务。还有,多个设备共享信道,同时发数据会产生冲突,它是怎么解决的,这也是网络接口层的内容。其实,物联网工程师不必在意这些内容。因为wifi物理信号方面的内容是由wifi芯片厂商负责,而wifi单芯片(wifi+SOC)则会提供SDK包并提供SOCKET编程接口了。所以,我们职责的重点是关注网络层以上的编程开发知识。
2.网络层,即IP协议,最基础的认识是每个IP对应一个物联设备、手机或者一个后方服务器。原则上一个网卡对应一个IP,如图中wifi音箱、wifi热水器均有一个独立的IP。网络之间的通信都是基于IP进行的,网络包会通过路由器最终送到目标IP所对应的设备上。
Wifi音箱等家庭设备加入家庭局域网,其实是各获得一个局域网IP,192.168.*.*,包括路由器1也有一个局域网地址,但是路由器1还有一个互联网IP。只有路由器的互联网IP才能被外界所获知,外界是不能主动获知局域网IP具体对应哪个设备的,只有路由器1才知道,因此所有对外发送的数据包的源IP都是路由器1的互联网IP,外界发送给设备的数据包的目标IP也是路由器的互联网IP。
我们都知道,设备上线时需要向物联平台服务器告知自己的状态,以便于用户控制。因此物联平台服务器的IP必须是一个互联网IP,或者是一个域名(DNS协议可以将域名解析为IP)。如果它是一个局域网IP,那设备是不可能访问到的。
这里,我们还必须要记住一点,设备和物联平台的第一次通信永远都应该设备主动发出,因为就算物联平台知道路由器1的公网IP,它也无法将消息传送到内部的设备的。另外,服务器必须要持续设备到达物联平台最后一跳的路由信息,因为路由信息原路返回的过程中具有路由器1记录是哪个设备发出的信息。如果不记录这个信息,物联平台单靠路由器1的互联网IP是无法触达到具体设备的。
如果你不能理解上面这一段话,就记住,物联平台通过路由器1的互联网IP主动发一条消息到设备是不能成功的,但是,当设备发一条消息给物联平台后,物联平台直接响应该数据包(IP源地址和目标地址调换位置)是可以触达设备的。如果是满足一问一答的方式,那么服务器不需要记录这个路由信息,如果需要满足服务器主动发消息给设备的场景,那么服务器是需要记录这个信息的。
另外,我们知道,网络设备在物理上表现为一个真实的网卡,网卡的MAC地址是6个字节,48位。在一个局域网内通信,网络编程时都是基于IP地址的,路由器或者交换机如何通过IP地址找到对应的MAC地址,即为ARP地址解析协议,这个也是网络层的职责,但是作为开发人员来说,我们了解即可。
3. 传输层,即TCP/UDP协议。对于传输层,我们需要理解的是,一台PC或者手机上运行的网络运用可能很多,但是他们都对应同样一个IP。操作系统如何将一个数据包分发给对应的网络运用呢?这就依靠传输层所定义的端口来区分。常见的网络应用层协议都会默认传输层端口号,如FTP对应21,HTTP对应80,SMTP对应25等等。传输层除了定义端口号之外,还有两个非常重要的协议,即TCP面向连接的协议和UDP数据包协议。前者可以理解为一个虚拟的电话连接协议,一旦双方打电话建立起连接后双方通话的过程中,所有的数据包均是走同样的路由路径。因为面向连接的协议会在带宽中预留资源来保障,所以面向连接协议能够保证质量,因此适用于一些对数据质量要求严格的网络运用中,如电子邮件应用,如果不保证质量,邮件内容都不保证正确,谁会使用这个邮件系统呢?但是,对于一些音频视频类运用,丢一两帧完全不影响用户体验,则会使用UDP协议,其不是面向连接,发完后之前的路由信息可以不在保存,其是使用最大努力交付(即trymy best)。
4. 应用层。常见的网络应用协议包括HTTP、FTP、SMTP、POP等等。嵌入式物联应用是建立在这些网络应用协议的基础之上的。这些协议会规范基本的请求连接、响应和数据传输等方面的格式。作为嵌入式物联网应用来说,其应该自行定义应用协议的格式,这些数据格式可以简单自定义,也可以使用成熟的标准格式,如HTML、XML、JSON等等。由于防火墙一般只放开端口为80的HTTP数据包,所以物联网应用一般都会构建在HTTP的基础上。
所以,我们要区分网络应用层协议HTTP和应用自定义协议。后者使用前者进行传输通信。不管应用自定义协议使用哪一种格式,都需要通信双方同时使用。物联设备和物联平台后台通信时,可以使用简单的XML格式或者JSON格式,而物联平台还要被PC浏览器访问,那么,由于浏览器只支持HTML格式,则要求物联平台后台提供HTML格式的内容服务,同理,物联网平台和手机APP之间的通信可以用XML或者JSON。甚至,我们可以自定义简单的命令来实现功能,但是使用XML或者JSON这些格式有助于数据有良好的可读性,而且也有成熟的类库来解释。
这些都是建立在HTTP网络应用协议的基础上的。
二、 socket编程
socket编程分为TCP和UDP两种方式。分别如下:
可见,TCP/UDP的socket套接字在通信之前需要绑定(bind)IP和端口地址。对于TCP来说,服务器需要先侦听listen,而客户端发起connect请求后,服务器才能accept,之后即建立面向连接的通信环境,通过send/recv函数进行通信。
而UDP编程则很简单,绑定之后可以立即开始数据传输。
除了掌握基本的socket编程之外,还需要清楚以下知识:
1)阻塞和非阻塞。网络套接字有阻塞和非阻塞之分。如假设创建的socket是阻塞的,那么其recv函数就一定要等到对方传送数据过来,才会返回,否则会一直处于阻塞状态。而非阻塞状态则是立即看看缓冲有没有数据,如果有就返回数据,没有会返回错误,而不是一直死等。阻塞模式可以简单地理解为同步工作模式,而非阻塞模式可以理解为异步工作模式。
2)多路复用。作为服务器,可能会存在多个客户端连接,如果轮询每个客户端socket有没有数据,那效率多累啊。Socket编程的select和poll接口用来解决这类多路IO复用的问题,它能够同时侦测多个连接的数据通信。
三、 B/S和C/S
1.B/S是浏览器和客户端模式,使用HTML语法格式。其使用一问一答,即服务器是无状态的,它不知道客户端之前是否已经访问过。无状态有助于服务器高效率而且稳定地服务。但是这种模式对于物联网应用的影响是致命的。因为服务器无法主动地发送消息给物联设备。那么,如何改进呢?
1)ajax技术。Ajax技术最新是为了解决页面局部刷新频繁的效率问题的。即一个HTML页面的局部数据发送变化了,在ajax之前需要重新发送一次请求,来刷新整个页面。而ajax则是仅仅向服务器请求发送变化的数据。前者在请求时会看到页面有闪烁,而后者则没有。我们正好可以利用ajax来定时向服务器发起请求,询问服务器是否有更新的数据。如果询问频率高,那么实时效果就好,但是会加重服务器负担。本质上,还是一问一答的形式,而不是双向通信。Ajax需要浏览器技术支持。
2)websocket技术。Websocket是为了解决HTML的双向通信问题而提出的,其在第一条HTTP请求之后会让服务器将后续的协议更新到Websocket进行通信。Websocket需要tomcat7.0以上的运行容器技术支持。
物联网应用开发在设备端可以通过socket编程来模拟HTTP协议,同样可以模拟HTTP之上的HTML、XML或者Websocket。
2. C/S
C/S客户端和服务器编程在智能机出现之前在PC桌面领域一度被认为会在逐渐被B/S所取代,但是在智能机设备端它又焕发新生。尽管HTML5发展迅速,但是个人觉得浏览器在手机等智能设备端的体验还是比不上原生APP。而HTML5更大的优势是其移植性很强。
C/S编程可以直接使用socket通信进行通信,那自然不存在双方通信的问题。如果C/S编程使用http类库进行编程通信,它同样也会存在双向通信的问题。
目前看来,很多人都希望沿用J2EE那套业务架构来支持物联网,但是物联设备毕竟是资源有限,有些终端可能是简单的单片机,其跑完整的TCP/UDP协议都比较困难,因此有人提出了精简版的TCP/IP协议,如CoAP(受限制的应用协议(ConstrainedApplicationProtocol)的代名词)、ucIP、LWIP等等。从业务应用协议来看,IBM研发的MQTT可能会成为物联网应用协议的标准。
四、 Web编程
Web编程最先指的是浏览器展示内容的语法编程。Web静态语言即是HTML。
1.HTML不支持数据的动态变化。因此产生了基于解释引擎的动态语言,如ASP、PHP、JSP等等。这类语言会使用HTML/CSS来描述展现样式,而使用动态语言来控制数据的展现,例如访问数据库获取新数据等等。
需要知道,ASP,PHP,JSP这些语言是服务器编程语言,当用户通过浏览器访问服务器对应网页时,该网页的ASP/PHP/JSP等内容会经过服务器的解释引擎转化为具体的数据,最终和其他的HTML、CSS数据一起返回给浏览器进行展现。因此,浏览器得到的永远都是确定的HTML、CSS和数据,不存在ASP/PHP/JSP的语句。
2.JavaScript脚本。脚本是浏览器技术支持的语法,而不是服务器技术支持的。例如一个登录界面,要确保用户输入的正确性,如不规则字符,长度太长等等,一般会使用javascript脚本进行检测,而不需要发送请求给服务器。上述讲到的ajax技术也是浏览器支持的脚本技术。
3.jQuery,它是对javascript技术的封装,能够更好地进行前端编程控制。
4.HTML/CSS/JS脚本,称为web前端编程开发,而ASP/JSP/PHP等为后端开发。
5.后端开发自然会涉及到数据库访问、业务逻辑,并且其需要和前端进行交互。因此,web应用编程架构普遍使用MVC编程模型,即M为数据模型,负责数据库访问;V为视图,负责展现;C为控制。MVC模型能够对展现和数据库进行良好的分离,有助于应用开发。
6.作为整体运行架构来理解,服务器需要包括数据库(如MySQL)、web应用和解释引擎和web服务(如apache和tomcat)。Apache提供http服务。
7.JSP的基础是Java语法,J2EE架构提供servlet技术,用于支持网络运用。JSP其实是对servlet的高级封装并作为独立的技术出现的,JSP侧重支持B/S方面的网络运用,而servlet则通过映射类的方式支持C/S方式的网络运用,如微信蓝牙接入和wifi接入的后端技术即使用servlet进行支持, 顶层使用XML/JSON格式。
预告:嵌入式企鹅圈将于春节后发布微信wifi接入解决方案。嵌入式企鹅圈微信蓝牙开发板(同时支持微信Airsync协议和一般APP串口透传,可进行二次开发,无需外加MCU)已经发布。