网络编程的基本概念

时间:2022-12-15 08:07:25

网络编程是编写服务器程序不可或缺的部分。 高性能的服务器程序一定都会有好的网络处理程序,下面就来谈一谈网络编程的基本概念。

凡是网络编程,几乎都离不开socket。socket分为阻塞与非阻塞两种。阻塞的socket在调用读写接口是,会一直阻塞到socket上有可读数据,或者socket可以写入数据。而非阻塞socket,则会立即返回,不管socket是否可以读写。举个简单的例子,你去咖啡店买咖啡,发现前面排了很多人,对于阻塞模型来说,就是你会排队一直等待。非阻塞模型,就是你看到要排队,你就走了去干别的事了。很明显,对于服务器来说,阻塞模型是严重影响服务器性能的。大部分情况下,都会使用非阻塞的调用

对于,阻塞与非阻塞的socket,又可以引申出两个概念,同步与异步。很多同学会混淆这两种说法,其实还是有一些区别的。在客户端编程时,当要访问网络资源,都会调用一些已经封装好的接口。同步接口就是说,调用接口时会一直等到有结果才返回;异步接口则是立即返回,等待有数据时,通过设置的回调函数通知调用者结果。同步并不等于阻塞的socket,异步也并不等于非阻塞的socket。不管是同步接口还是异步接口,都可以通过阻塞或是非阻塞的socket来实现。同样以买咖啡的例子来说明同步与异步的区别:同步就是你一直排队等你买到咖啡为止,异步就是你发现排队人很多先去干别的事了,等到前面没人了服务员会叫你过去。就是这样

对于linux来说,socket有这样几种使用方式:阻塞,非阻塞(I/O复用),信号触发,异步

服务器程序,一般不会使用阻塞方式,信号触发也用得不多。较常见的就是I/O复用(select, poll, epoll),异步i/o这两种。我们继续来聊聊i/o复用。

对于服务器来说,每个连接的客户端都对应着一个socket,但是不代表一个客户端占用一个端口,这点千万要注意。服务器能处理多少客户端,是由文件描述符的数量来说的,其实就是一个int类型的值。显然这并不是瓶颈,起决定因素的往往是 内存,多线程/进程模型,以及事件处理方式。这里重点说说事件处理方式。服务器处理成千上万的客户端连接,说到底就是处理这些socket上的读写事件。那么如何高效的处理这么多的连接呢? 还是以咖啡店的例子来说,刚开始是一个服务员处理一个顾客的需求,但是顾客越来越多,不可能有那么多服务员。于是一个服务员开始处理多个顾客。顾客付完钱,就找桌子坐下来玩手机了,没必要在那排队干等着。等你的咖啡好了,服务员通知你去取就行了。如果把顾客看作客户端,那么顾客使用的是异步接口。服务员当作服务器,使用的是I/O复用,即一个线程/进程处理好多个顾客的请求。于是又诞生了两种高效的事件处理模式:reactor与proactor。这两种模式的区别是什么,在书本看到的说法是,reactor关注的是I/O就绪事件,proactor关注的是I/O完成事件。到底是什么意思?其实大多数人接触到的可能都是I/O复用,这就是典型的reactor模式,即需要应用程序自己调用socket的读写接口来处理数据。而proactor则是直接告诉应用程序,哪个socket上来了什么数据,你直接处理就行了。怎么样,这么说明白了吗?还不明白,那我们再用买咖啡的例子来打比方。在买完咖啡后,你在那玩手机。把咖啡比作是数据,取咖啡比作是一个读写过程,喝咖啡当作是处理数据过程。reactor模型是,服务员大声喊,xxx咖啡好了,这时你就要自己去取咖啡(读过程),然后再回来喝(处理过程),喝完了你得自己再送回去(写过程)。而proactor则是,服务员直接把你的咖啡端到桌子上,你负责喝就行了。这样就很明显可以看出,proactor省略了应用程序的读写过程,一般来说proactor都要依赖于异步的I/O.