上一篇文章《TCP/IP协议分析》讲述了自己是如何和网络领域的开发扯上关系的。正如从招聘网站上抽出的几个关键词“TCP/IP, Socket, 多线程”可见,协议分析并不是网络开发的主流俗话我们所说的网络编程是socket编程今天就以我的经历来讲一下socket编程的相关知识。
socket编程的基础知识我就不在这里科普了大家可以通过相关书籍和资料去了解。
在招聘工程师时面试问到某些新人这个问题。他们会以为会编写客户端/服务端建立连接正常收发数据的流程就认为自己了解socket开发。真是这样简单调用几个API理解下三次握手会使用bind、listen、......就可以了吗
当然不是当随着客户端数量的增加少量并发——>大量并发——>海量并发的过程中服务端的处理难度也会随之增加。在服务端编程中非常有名的C10K问题网络服务在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪就向我们做了很详细的说明。但是客户端socket编程虽然没有这种并发要求就一定简单吗我认为不是的请参看我的这篇文章《客户端网络库实现真的很简单吗》。另外互联网中各种复杂的网络环境也会给我们进行socket编程带来很多困难和挑战。
所以总体来说想要做好socket编程还是有一定难度的大家很容易从各种招聘渠道了解到精通这个领域的人在市场上很热销报酬也很可观而且多为一些互联网巨头和新兴互联网公司所需要。
要想处理我们上面所说的C10K甚至C100K问题网络模型的选择是非常重要的。先要清楚几个概念阻塞I/O非阻塞I/OI/O复用异步I/O 。网络上关于这个话题的讨论太多太多我的理解就是
阻塞非阻塞进程/线程要访问的数据是否就绪进程/线程是否需要睡眠等待
同步异步访问数据的方式同步需要主动读写数据在读写数据的过程中还是会阻塞异步只需要I/O操作完成的通知并不主动读写数据由操作系统内核完成数据的读写。
所以可以看出阻塞I/O非阻塞I/OI/O复用都属于同步的范畴。
常见的几种服务端网络模型
all connections one thread+blocking I/O服务端对所有客户端连接只用一个线程去处理而且网络I/O还是阻塞的。这在多并发情况下就是找死吞吐率会很低、延迟会非常大。
per connection per thread+blocking I/O服务端线程资源是有限的上下文切换是耗资源的随着客户端并发连接的增多你是承载不起的。但是有些语言内置的协程用户级线程却可以解决比如Go routine Erlang actor。前几天刚发现了一个基于C语言的协程库state thread感觉也很好用。
thread pool+blocking I/O线程池的方式受制于服务器CPU个数也不会高效的。
non-blocking I/O + I/O multiplexing 有点眉目了正是解决C10K问题最基础的方案。在这个模型下我们还可以更进一步优化吗
non-blocking I/O + I/O multiplexing + asyc I/O非常理想、高效的网络模型。windows下是完成端口来帮我们实现linux下要依赖异步I/O机制但是究竟是否好用我没尝试过。
从上面又引出了两种高性能的服务端网络编程设计模式基于I/O事件驱动的Reactor和基于异步I/O驱动的Proactor其中non-blocking I/O + I/O multiplexing属于Reactor模式non-blocking I/O + I/O multiplexing + asyc I/O属于O驱动的Proactor模式。
今天我们所讲的只基于linux平台毕竟linux平台的服务器占据了市场份额的90%以上。linux下目前最成熟的模型是epoll。说到这给大家提个问题epoll和它的老前辈select有什么不同呢
刚才讲到non-blocking I/O + I/O multiplexing时提到过在此模型的基础上我们还可以优化吗是的可以。我们可以采用一个线程对应一个事件循环的机制启用多个线程毕竟现在的服务器硬件资源是很强大的我们不要浪费了cpu。这就引出了我们要讲的另一个概念master——worker模型字面上的理解就是有一个master负责协调工作由好几个worker去实际完成工作。在我们络编程中也就是有一个master负责将客户端连接分发给不同的worker线程或者通知连接来了由worker去抢占实际上由worker去完成客户端连接的读写或逻辑处理工作这样不但有效利用了服务器的CPU资源也增加了服务端的吞吐量和降低了客户端的延迟。更详细的讲解请参见我的文章《从master-worker模型看团队管理》 。
站在巨人的肩膀上可以让我们看得更为高远如果选用了合适的第三方网络库也会使我们的工作事半功倍。
ACE学之者生用之者死。这是陈硕老师对ACE库的评价我觉得很形象对于学院派理想化的东西去学习收益很大但是使用就不见得适合自己。
boost asio:has a “near STL” statusstackoverflow;
Poco: 很全面的库不仅仅有网络库。
libev :速度更快,bug更少,特性更多,体积更小;
libevent:简单,强大;
网络编程中还有很重要的一点是客户端/服务端交互数据协议的选择。我们选择的依据有哪些呢
网络数据大小——占用带宽,传输效率;
网络数据安全性——敏感数据的网络安全;
编码复杂度——序列化和反序列化复杂度,效率,数据结构的可扩展性,可 维护性;
协议通用性——大众规范;
我们可以以自定义的角度采用TLV结构的二进制协议
可以以提供序列化和反序列化库的的角度采用第三方协议:protocol buffers, json, thrift
可以以选择文本化协议的角度选择xml,json协议。
我的理解是什么呢请参见文章《网络传输数据格式的选择》。
关键词MTU、SO_LINGER、TCPNODELAY、TIMEWAIT、keepalive、串话...这些关键词都代表了某种你需要考虑和处理的网络情况。
辅助工具python、netcat、tcpdump、wireshark...它们都会成为使你事半功倍的巨人。
《UNIX网络编程卷1》
《Linux多线程服务端编程》
《构建高性能Web站点》
《UNIX网络编程卷1》
《Linux多线程服务端编程》
http://stackoverflow.com/questions/992069/ace-vs-boost-vs-pocohttp://stackoverflow.com/questions/9433864/whats-the-difference-between-libev-and-libevent
高性能tcp网络服务器
基于TCP协议的远程过程调用框架客户端实现