刘生建 李俊琴
摘要:现代生活离不开互联网,计算机的网络通信技术最先发源于UNIX系统,而Windows平台虽然起步稍晚,但是目前对互联网技术的支持也有长足的进步。现在很多的网络游戏客户端都是基于Windows平台的。使用的底层通信技术经过多年的发展,也出现了各种技术解决方案,该文研究概括总结了在Windows平台上计算机网络通信技术的主要技术方法。
关键词:实时应用;套接字;Node.js
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2014)27-6298-03
Abstract: Modern life cannot do without the Internet, network communication technology and computer originated in the first UNIX system and Windows platform, although it started late, but the support of Internet technology has made great progress. Now a lot of network game client is based on Windows platform. Using the underlying communication technology after years of development, also appeared all kinds of technical solutions, this paper summed up the Windows platform in the computer network communication technology of the main technical method.
Key words: Real - Time Application ,Socket, Node.js
现代生活离不开互联网,计算机的网络通信技术最先发源于UNIX系统,而Windows平台虽然起步稍晚,但是目前对互联网技术的支持也有长足的进步。现在很多的网络游戏客户端都是基于Windows平台,而TCP/IP协议在计算机的网络通信技术发挥着巨大的作用。在金融系统、社交应用、网络游戏等软件中使用较多的是TCP,它能保证数据包的有序传送,在通信链路建立后,所有的数据包都从该通道链路进行传送。
应用层的网络通信一般都通过Berkeley Socket编程接口实现,在Windows平台上对应为Winsock技术。大部分Winsock API在WS2_32.dll中实现,在WINSOCK2.H文件中申明[1]。目前应用广泛的应用如:QQ、微信、微博,还有一些流行的网页游戏、手机游戏,也直接或者间接的依赖Socket来传送数据。Socket简称套接字,用于实现网络上客户和服务器之间的连接,套接字是在比较低的层次上通信的,不同的操作系统对Socket有不同的支持方式。使用Socket进行网络通讯屏蔽了复杂的网络底层协议差异性。目前所有主流的操作系统对原生的Socket都有全面的支持。
1 传统Socket通信过程
在TCP/IP网络应用中,通信的两个进程间相互主要采用C/S(客户端/服务器)通信模式,即客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。使用此模型的通常情况是:网络的中各节点设备的软硬件资源、运算能力不均等,需要共享,拥有众多资源的服务主机提供服务,资源较少的客户请求服务;网间进程通信完全是异步的,相互通信的进程间通常不共享内存缓冲区,服务端和客户端的执行过程如图1所示:
服务器方需要首先启动,并根据请求提供相应服务。主要步骤:
1) 打开一通信通道并告知本地主机,它愿意在互联网地址的特定端口(如WWW为80,FTP为21等)接收客户请求。
2) 等待客户请求到达该端口。
3) 接收到客户服务请求,处理该请求并发送应答信号。接收到并发服务请求,启动一个新进程来处理这个客户请求,并交由该新进程来处理此客户后续请求。服务完成后,关闭此新进程与客户的通信链路并结束。
4) 返回第2步继续等待新的客户请求。
客户方的主要执行步骤:
1) 打开一通信通道,连接到服务器所在主机的特定端口。
2) 向服务器发服务请求,等待并接收应答。
3) 接收服务器方返回的处理结果。
4) 再次发出服务请求直到结束。
5) 请求结束后关闭通信通道并终止。
2 Node.js中的网络编程
传统的网站服务器采用为每个连接分配一个线程的做法来提供网络服务。虽然可以使用线程池来减少线程新建的时间,但是在处理并发请求上一直是一个棘手的问题。普遍的做法是通过增加服务器内存和CPU数量硬件手段加以解决。
Node.js在设计之初就采用了全新的思想,采用一个单一的主服务进程来处理所有的连接请求,但是所有的API调用都是非阻塞的,要求程序员同样不能在处理函数进行复杂计算。对文件或者数据库这种比较耗时的操作,在读写完成后通过回调函数把结果数据通知到请求者。Node.js运行平台基于谷歌Chrome浏览器的JavaScript运行环境,可以在所有主流操作系统上顺畅运行。它是一个容易快速构建,可扩展好的网络应用程序平台。Node.js使用一个事件驱动的、非阻塞I/O运行控制模型,使得它轻巧、高效,十分适合运行数据密集型分布式实时应用程序的运行[1]。
使用Node.js来开发网络应用的主要步骤:
1) 从官方网站www.nodejs.org下载对应自己操作系统的Node.js安装包;
2) 安装Node.js;
3) 使用JavaScript语言编写后台应用程序;
4) 使用node命令运行编写的应用,注意监听端口不能被其他程序占用;
5) 利用控制台等工具调试程序,确保程序运行符合预期结果;
在Node.js中有三种socket:TCP、UDP、Unix域套接字。使用TCP需要引用net模块,该模块是Node.js中网络编程的封装。利用JavaScript的闭包特性,可以省去不少的参数传递,网络应用的编写显得简单明了。如果要使用http协议,则可以直接使用http模块;如果要做一个大数据、计算不太密集型的社交型或者企业门户网站,还可以使用express模块,使经典的MVC模式提升开发质量并缩短开发时间。
3 使用Socket.IO简化网络开发
很多的社交应用和网络游戏是基于网页或者移动设备的本地应用程序的。在客户端安装一个Node.js也许有些大材小用。这时我们可以选择使用Socket.IO客户端来解决这个问题。
目前主流浏览器都能支持WebSocket,这样就可以直接使用标准的Scoket编程步骤加上事件回调处理方式来进行客户端与服务器的通讯。Socket.IO的诞生则统一了网络分布应用的前后端通讯方式,即便再老式的浏览器,比如IE8,也能运行基于“Socket”的网络交互。
Socket.IO的第一个版本在Node.JS出现的不久就开发出来。目前1.0版本也已经发布,还提供了对二进制数据的传输支持,方便了图片、声音的文件的传送,降低了网络应用的编写复杂度。Socket.IO其实也是Web上的事件发生器(EventEmitter)。Socket.IO的1.0版本代码已经不再处理传输与浏览器兼容的事情了。那些工作已经并入到新模块Engine.IO里面了,Engine.IO是一套类WebSocket风格的API实现。Socket.IO的服务端只有一千两百多行代码;客户端代码只有代码不到一千行。
在分布式应用中,客户端可以使用Socket.IO连接后后端服务器来获取资料。使用Socket.IO时,不用关心包、帧、TCP等底层概念,而只需要关注什么事件被发送和接收。在Node.js上使用Socket.IO开发一个简单聊天应用[3]只需要很少的几行代码。Socket.IO还提供了namespace和room等概念,方便消息频道及私有组内部的通讯。
4 Node.js应用的负载均衡设计
大多数轻量级 Web 服务器,比如 nginx 和 lighttpd,都能够针对多台 HTTP 服务器进行负载平衡,但如果您想要在非 HTTP 服务器之间实现平衡,nginx 可能无法满足要求[4]。而使用Node.js平台后,由于由于Node.js是单线程非阻塞方式运行的,没有多进程竞争也没有死锁,一台标准配置的服务器也可以同时为上万个客户端同时提供服务。而现在的标准服务器一般都配置有多核CPU或者多个CPU。在这种情况下,可以让一些CPU核去执行计算型任务,避免Node.js天生不适合复杂运算的缺点。
从Node.js的0.8版本开始内置了cluster的特性。对于小型的网站应用,可以单独使用Nodejs作为开发方案。在使用cluster时,最重要的两个概念是master和worker。其中是master总控主进程,作为服务管理者,worker是具体服务进程。可以根据CPU的数量,启动相应数量的worker。值得注意的是由于具体服务不是固定某个具体的worker上,所以同一客户端的两次http请求应该没有任何联系,即没有共享状态才能有效的利用负载均衡,对于确实需要共享状态的会话,可以把共享状态存放在数据库中。如果使用Socket长连接,则不存在会话状态的问题,但是会大大降低并发处理能力。
4.1 简单服务代理的编写
前面是单机上的集群处理办法,如果是多服务器,我们可以选择一台服务器来兼做服务代理。为了说明分布式处理思想,这里假设所有的后端服务器一直都是可用的,程序内也不进行任何错误处理。它接收一个来自客户端的套接字连接,随机挑选一个实际目标服务器进行连接,然后将来自客户端的所有数据转发给该服务器,并将来自该服务器的所有数据都发回到客户端。
假设每个服务请求的处理时间为100毫秒,服务配有4核CPU,代理程序和3个实际服务进程分别占用1个CPU核充分利用多核来进行并行计算。集群运行后能同时处理3个请求,如果只有不到4个请求同时到达,每个请求都会在接近100毫秒时间内得到处理结果。如果同时收到30个服务请求,那么是最后发出请求的客户端要大约1秒后(100毫秒*30/3)才能得到处理结果。为了提升效应速度,可以多增加几台计算服务器。
上面的方案设计简单,容易编写,实际编程时可以通过一个配置文件确定工作服务器的地址和端口。这个方案的缺点也比较明显,后端服务器的配置信息暴露给了客户端,在云计算环境下同步更新困难,该方案没有监控所有后端服务进程的状态并自动更新可用服务器的信息,极有可能出现将客户端请求连接到失效后端服务器的情况。
4.2 借助Redis作为作业消息队列
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。只要选择一台普通配置的服务器,配置能够适当的内存。客户端和服务工作进程都直接连接到该服务器,这台服务器其实充当了一个网络上的消息队列。实现的思想如图2所示:
具体实现分为以下几个主要步骤:
1) 所有服务进程向Redis订阅自己可处理请求服务名称的通知消息;
2) 客户进程发送请求到Redis内存消息队列并产生作业编号,并通过Redis发布广播通知。
3) 多个服务进程收到通之后,按照先到先得的处理原则从从消息队列中提取一个任务并开始处理;
4) 处理完毕后将结果放到一个单独结果队列中,该队列实际上是一个以作业号作为key的哈希表。
这个方案的优点是不需要配置服务进行的地址和端口信息,可以按照请求量在线动态增加服务进程的数量。但是缺点是要多安装Redis服务,而Redis很可能成为通讯的瓶颈,也容易造成单点故障。当然可以采用双机热备或者Redis提供的集群方法来避免单点故障。
5 结论
Windows系统是最常用的办公和游戏的平台,现在的很多应用都需要互联网实时通信技术,该文总结了在Windows平台上开发这种应用常用的技术,通过本文的总结可以看出WinSocket通信是最基本的技术,现代基于Node.js的各种实时通信技术其实是对原有技术封装利用,但是利用这些现代技术将使我们更快地开发出高质量的实时应用。
参考文献:
[1] [美]Anthony Jones,Jim Ohlund.Windows网络编程[M]. 杨合庆,译.2版.北京:清华大学出版社,2002,33.
[2] Node.js官方网站.http://www.nodejs.org/.
[3] Socket.IO官方网站.http://socket.io/get-started/chat/.
[4] Noah Gift ,Jeremy Jones.使用Node.js作为完整的云环境开发堆栈. http://www.ibm.com/developerworks/cn/cloud/library/cl-nodejscloud/