概述
简述 OSI 七层协议
这里简化为五层:
应用层
与其它计算机进行通讯的一个应用,它是对应应用程序的通信服务的。
例如,一个没有通信功能的字处理程序就不能执行通信的代码, 从事字处理工作的程序员也不关心OSI的第7层。
但是,如果添加了一个传输文件的选项,那么字处理器的程序员就需要实现OSI的第7层。
示例:TELNET,HTTP,FTP,NFS,SMTP等。 表示层 这一层的主要功能是定义数据格式及加密。
例如,FTP允许你选择以二进制或ASCII格式传输。如果选择二进制,那么发送方和接收方不改变文件的内容。如果选择ASCII格式, 发送方将把文本从发送方的字符集转换成标准的ASCII后发送数据。在接收方将标准的ASCII转换成接收方计算机的字符集。示例:加密,ASCII等。
会话层
它定义了如何开始、控制和结束一个会话,包括对多个双向消息的控制和管理,以便在只完成连续消息的一部分时可以通知应用,从而使表示层看到的数据是连续的, 在某些情况下,如果表示层收到了所有的数据,则用数据代表表示层。示例:RPC,SQL等。
传输层
这层的功能包括是否选择差错恢复协议还是无差错恢复协议,及在同一主机上对不同应用的数据流的输入进行复用,还包括对收到的顺序不对的数据包的重新排序功能。示例:TCP,UDP,SPX。
网络层
这层对端到端的包传输进行定义,它定义了能够标识所有结点的逻辑地址,还定义了路由实现的方式和学习的方式。为了适应最大传输单元长度小于包长度的传输介质, 网络层还定义了如何将一个包分解成更小的包的分段方法。示例:IP,IPX等。 数据链路层 它定义了在单个链路上如何传输数据。这些协议与被讨论的各种介质有关。示例:ATM,FDDI等。
物理层
OSI的物理层规范是有关传输介质的特这些规范通常也参考了其他组织制定的标准。连接头、帧、帧的使用、电流、编码及光调制等都属于各种物理层规范中的内容。 物理层常用多个规范完成对所有细节的定义。示例:Rj45,802.3等。
什么是C/S和B/S架构
C/S架构软件(即客户机/服务器模式)分为客户机和服务器两层:第一层是在客户机系统上结合了表示与业务逻辑,第二层是通过网络结合了数据库服务器。 简单的说就是第一层是用户表示层,第二层是数据库层。客户端和服务器直接相连,这两个组成部分都承担着重要的角色。
B/S架构第一层是浏览器(即客户端)只有简单的输入输出功能,处理极少部分的事务逻辑。由于客户不需要安装客户端,只要有浏览器就能上网浏览, 所以它面向的是大范围的用户,所以界面设计得比较简单,通用。第二层是WEB服务器,扮演着信息传送的角色。当用户想要访问数据库时,就会首先向WEB服务器发送请求, WEB服务器统一请求后会向数据库服务器发送访问数据库的请求,这个请求是以SQL语句实现的。第三层是数据库服务器,它存放着大量的数据。当数据库服务器收到了WEB服务器的请求后, 会对SQL语句进行处理,并将返回的结果发送给WEB服务器,接下来,WEB服务器将收到的数据结果转换为HTML文本形式发送给浏览器。
三次握手、四次挥手的流程
所谓三次握手(Three - WayHandshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。 在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:
图2 TCP三次握手
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq = J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。 (2)第二次握手:Server收到数据包后由标志位SYN = 1 知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack = J + 1,随机产生一个值seq = K,并将该数据包发送给Client以确认连接请求 ,Server进入SYN_RCVD状态。 (3)第三次握手:Client收到确认后,检查ack是否为J + 1,ACK是否为1,如果正确则将标志位ACK置为1,ack = K + 1,并将该数据包发送给Server, Server检查ack是否为K + 1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手, 随后Client与Server之间可以开始传输数据了。
所谓四次挥手(Four - Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中, 这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:
图3 TCP四次挥手
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接, 收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。 首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。 (1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 (2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号 + 1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。 (3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。 (4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号 + 1,Server进入CLOSED状态,完成四次挥手。
什么是arp协议
答:ARP(AddressResolutionProtocol) 是个地址解析协议。最直白的说法是:在IP以太网中,当一个上层协议要发包时,有了该节点的IP地址,ARP就能提供该节点的MAC地址。
5.TCP和UDP的区别?
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达; UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
什么是局域网和广域网
答:什么是广域网(WAN、公网、外网),什么是局域网(LAN、私网、内网)? 广域网(WAN),就是我们通常所说的Internet,它是一个遍及全世界的网络。 局域网(LAN),相对于广域网(WAN)而言, 主要是指在小范围内的计算机互联网络。这个“小范围”可以是一个家庭,一所学校,一家公司,或者是一个政府部门。 BT中常常提到的公网、 外网,即广域网(WAN);BT中常常提到私网、内网,即局域网(LAN)。
广域网上的每一台电脑(或其他网络设备)都有一个或多个广域网IP地址(或者说公网、外网IP地址),广域网IP地址一般要到ISP处交费之后才能申请到, 广域网IP地址不能重复;局域网(LAN)上的每一台电脑(或其他网络设备)都有一个或多个局域网IP地址(或者说私网、内网IP地址), 局域网IP地址是局域网内部分配的,不同局域网的IP地址可以重复,不会相互影响。
为何基于tcp协议的通信比基于udp协议的通信更可靠
TCP的可靠保证,是它的三次握手双向机制,这一机制保证校验了数据,保证了他的可靠性。而UDP就没有了,udp信息发出后, 不验证是否到达对方, 所以不可靠。
什么是socket?简述基于tcp协议的套接字通信流程
答:Socket又称”套接字”, 是系统提供的用于网络通信的方法.它的是指并不是一种协议, 没有规定计算机应当怎么传递信息, 只是给程序员提供了一个发送消息的接口, 程序员能使用这个接口同的方法, 发送与接收消息.Socket描述了一个IP, 端口对.它简化了程序员的操作, 知道对方的IP以及port 就可以给对方发送消息, 再由服务器处理发送的这些消息, Socket包含了通信的双发, 即客户端与服务端.
TCP编程的服务器端一般步骤是: 1、创建一个socket,用函数socket(); SOCKET SocketListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 2、设置socket属性,用函数setsockopt(); *可选 3、绑定IP地址、端口等信息到socket上,用函数bind(); SOCKET_ERROR = bind(SocketListen, (const sockaddr *) & addr, sizeof(addr)) 4、开启监听,用函数listen(); SOCKET_ERROR == listen(SocketListen, 2) 5、接收客户端上来的连接,用函数accept(); SOCKET SocketWaiter = accept(SocketListen,
_Out_ struct sockaddr * addr
_Inout_ int * addrlen); 6、收发数据,用函数send() 和recv(),或者read() 和write(); 7、关闭网络连接; closesocket(SocketListen); closesocket(SocketWaiter); 8、关闭监听; SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的。 TCP编程的客户端一般步骤是: 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt(); *可选 3、绑定IP地址、端口等信息到socket上,用函数bind(); *可选 4、设置要连接的对方的IP地址和端口等属性; 5、连接服务器,用函数connect(); 6、收发数据,用函数send() 和recv(),或者read() 和write(); 7、关闭网络连接;
intsend(
_In_SOCKETs, // 向哪个socket发送,accept返回的socket。
_In_const char * buf,
_In_ int len,
_In_ int flags
);
什么是粘包?
socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象? 粘包:多个数据包被连续存储于连续的缓存中,在对数据包进行读取时由于无法确定发生方的发送边界,而采用某一估测值大小来进行数据读出, 若双方的size不一致时就会使指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率, 发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去, 这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区, 用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后, 而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。分包是指在出现粘包的时候我们的接收方要进行分包处理。 (在长连接中都会出现) 数据包的边界发生错位,导致读出错误的数据分包,进而曲解原始数据含义。
当时短连接的情况下,不用考虑粘包的情况 2. 如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包 3. 如果双方建立连接,需要在连接后一段时间内发送不同结构数据 处理方式: 接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开 注:粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包
IO多路复用的作用
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I / O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I / O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I / O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I / O复用。
与多进程和多线程技术相比,I / O多路复用技术的最大优势是系统开销小,系统不必创建进程 / 线程,也不必维护这些进程 / 线程, 从而大大减小了系统的开销。
什么是防火墙以及作用
什么是防火墙 防火墙是指设置在不同网络(如可信任的企业内部网和不可信的公共网)或网络安全域之间的一系列部件的组合。 它可通过监测、限制、 更改跨越防火墙的数据流,尽可能地对外部屏蔽网络内部的信息、结构和运行状况, 以此来实现网络的安全保护。 在逻辑上,防火墙是一个分离器,一个限制器,也是一个分析器,有效地监控了内部网和Internet之间的任何活动, 保证了内部网络的安全。 2. 使用Firewall的益处 保护脆弱的服务 通过过滤不安全的服务,Firewall可以极大地提高网络安全和减少子网中主机的风险。例如, Firewall可以禁止NIS、NFS服务通过, Firewall同时可以拒绝源路由和ICMP重定向封包。 控制对系统的访问 Firewall可以提供对系统的访问控制。如允许从外部访问某些主机,同时禁止访问另外的主机。例如, Firewall允许外部访问特定的Mail Server和Web Server。 集中的安全管理 Firewall对企业内部网实现集中的安全管理,在Firewall定义的安全规则可以运行于整个内部网络系统, 而无须在内部网每台机器上分别设立安全策略。 Firewall可以定义不同的认证方法, 而不需要在每台机器上分别安装特定的认证软件。外部用户也只需要经过一次认证即可访问内部网。 增强的保密性 使用Firewall可以阻止攻击者获取攻击网络系统的有用信息,如Figer和DNS。 记录和统计网络利用数据以及非法使用数据 Firewall可以记录和统计通过Firewall的网络通讯,提供关于网络使用的统计数据,并且,Firewall可以提供统计数据, 来判断可能的攻击和探测。 策略执行 Firewall提供了制定和执行网络安全策略的手段。未设置Firewall时,网络安全取决于每台主机的用户。
select、poll、epoll 模型
备注:FD是File Discriptor ,中文翻译为文件描述符
IO多路复用是什么意思?一个线程,通过记录I/O流的状态来同时管理多个I/O,可以提高服务器的吞吐能力。不好理解?举一个生活的例子。宿管大妈是你家亲戚,来帮你监视下楼的妹子, 这个期间你可以些其他的事情. 例如读书,看报等等. IO复用又包括 select, poll, epoll 模式. 那么它们的区别是什么?
select模式:每一个女生下楼, 大妈一个一个询问, 并且大妈能力还有限, 最多一次帮你监视1024个妹子。
poll模式:不限制盯着女生的数量, 只要是经过宿舍楼门口的女生, 都会帮你去问是不是你女神。
epoll模式:不限制盯着女生的数量, 并且也不需要一个一个去问. 怎么做? 大妈会为每个进宿舍楼的女生脸上贴上一个大字条,上面写上女生自己的名字, 只要女生下楼了, 大妈就知道哪个是你女神了, 然后大妈再通知你。
1.select模型
基本原理:select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即 可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。
select目前几乎在所有的平台上支持其良好跨平台支持。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:select最大的缺陷就是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max查看。32位机默认是1024个。64位机默认是2048.对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。
2.poll模型
基本原理:poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到 设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
poll没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
3.epoll模型
基本原理:epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类 似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下: LT模式-延迟处理:一种缺省的工作方式,当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。 ET模式-立即处理:一种高速的工作方式,当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序立即处理该事件。
epoll的优点:
1》没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。 2》效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数, 3》内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。 这样所带来的缺点是:
1 单个进程可监视的fd数量被限制
2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
3 对socket进行扫描时是线性扫描
三者区别:
1, poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,
如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
2, poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。
在前面说到的复制问题上,epoll使用mmap减少复制开销。
3, 还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd, epoll_wait便可以收到通知
4, select模型单个进程有最大连接数限制,poll没有连接数限制,基于链表存储,epoll虽然有限制,但是很大,1G内存的机器可以打开10w左右的连接。
5,FD剧增后带来的IO效率问题,select和poll都是线性遍历,在FD剧增的情况下,速度变慢。
6,消息传递方式 select和poll都有内核拷贝动作,内核将消息传递给用户空间,epoll是内核和用户空间空享内存
多路IO之select
优点:单进程下支持高并发,可以跨平台
缺点:多次从内核到应用,应用到内核的数组拷贝;
每次内核都会重置填写的数据
最大支持1024客户端,原因在于fd_set定义使用了FD_SETSIZE,大小为1024;
以下是select模型server代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <ctype.h>
int main(){
int lfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in serv;
bzero(&serv,sizeof(serv));
serv.sin_port = htons(8888);
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = htonl(INADDR_ANY);
//reset port
int opt = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bind(lfd,(struct sockaddr*)&serv,sizeof(serv));
listen(lfd,128);
fd_set rdset;//readevent
fd_set allset;//bak readevent
FD_ZERO(&rdset);
FD_SET(lfd,&rdset);
allset = rdset;
struct sockaddr_in client;
socklen_t len = sizeof(client);
int nfds = lfd;
int nready = 0;
while(1){
rdset = allset;//备份传给内核
//阻塞等待事件就绪
nready = select(nfds+1,&rdset,NULL,NULL,NULL);
if(FD_ISSET(lfd,&rdset)){
//有新连接事件,将得到的CFD加入到集合
int cfd = accept(lfd,(struct sockaddr*)&client,&len);
if(cfd > 0){
//rdset是一个传入传出集合,每次都会重置
FD_SET(cfd,&allset);
}
if(nfds < cfd){
nfds = cfd;
}
nready --;
//如果就绪事件就一个,且是新连接就跳出循环
if(nready <= 0)
continue;
}
int i = 0;
for(i = lfd+1;i<nfds +1;i++){
if(FD_ISSET(i,&rdset)){
char buf[256] = {0};
int ret = read(i,buf,sizeof(buf));
if(ret < 0){
perror("read err");
close(i);
FD_CLR(i,&allset);
}
else if (ret == 0){
close(i);//client closed
FD_CLR(i,&allset);
}
else{
int j = 0;
for(;j<ret;j++){
buf[j] = toupper(buf[j]);
}
write(i,buf,ret);
}
if(--nready <= 0)
break;//no event.jump for.
}
}
}
close(lfd);
return 0;
}
多路IO之POLL模型:
POLL的原理与select相同,比select改进的地方:
1,请求和返回分离,避免每次都要重设数组
2,可以突破1024限制,poll是由打开文件的上限决定,可以使用ulimit命令查看上限
3,不能跨平台
poll代码解析:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <poll.h>
#include <arpa/inet.h>
#define _MAXLINE_ 80
#define _SERVER_PORT_ 8888
#define _MAX_OPEN 1024
int main(){
int i,maxi;
char strIP[16];
int lfd = socket(AF_INET,SOCK_STREAM,0);
struct pollfd client[_MAX_OPEN];
struct sockaddr_in clientaddr,servaddr;
int len = sizeof(clientaddr);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(_SERVER_PORT_);
//set reuse port
int opt = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(bind(lfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0){
perror("bind err");
return -1;
}
listen(lfd ,128);
client[0].fd = lfd;//监听第一个文件描述符
client[0].events = POLLIN;//监听读事件
for(i = 1; i <_MAX_OPEN;i++){
client[i].fd = -1;//用-1初始化,因为0也是描述符
}
maxi = 0;//记录client数组有效最大元素下标
while(1){
int nready = poll(client,maxi+1,-1);
//判断是否有新连接
if(client[0].revents & POLLIN){
//此处不会阻塞
int cfd = accept(lfd,(struct sockaddr*)&clientaddr,&len);
printf("recv form %s:%dn",
inet_ntop(AF_INET,&clientaddr.sin_addr,strIP,sizeof(strIP)),
ntohs(clientaddr.sin_port));
for(i = 1;i<_MAX_OPEN;i++){
if(client[i].fd < 0){
client[i].fd = cfd;
break;
}
}
if(i == _MAX_OPEN){
//最大客户连接上限
printf("max connected...n");
continue;
}
client[i].events = POLLIN;
if (i > maxi)
maxi = i;
if(--nready <= 0)
continue;//没有更多就绪事件,继续回到POLL阻塞
}
for(i = 1;i<=maxi;i++){
//前面的IF没有满足,说明没有新连接,而是读事件
int cfd;
//先找到第一个大于0的文件描述符
if((cfd = client[i].fd) < 0)
continue;
if(client[i].revents & POLLIN){
char buf[_MAXLINE_] = {0};
int ret = read(cfd,buf,sizeof(buf));
if(ret < 0){
if(errno == ECONNRESET){
printf("client[%d] aborted connectionn",i);
close(cfd);
client[i].fd =-1;
//POLL中不需要像SELECT一样移除,直接置-1即可
}
else{
perror("read error");
exit(-1);
}
}
else if(ret == 0){
printf("client[%d] closedn",i);
close(cfd);
client[i].fd = -1;
}
else{
write(cfd,buf,ret);
}
if(--nready <= 0)
break;
}
}
}
close(lfd);
return 0;
}
多路IO之EPOLL:
不管是select,还是poll,都需要遍历数组轮询,而且select仅支持1024个客户端,在大量并发,少量活跃的情况下效率较低,也就滋生了epoll模型。
1,可以突破1024限制,不跨平台
2,无须遍历整个文件描述符集,只需遍历被内核IO事件异步唤醒,而加入ready队列的文件描述符。
3,除了select/poll的IO事件水平触发(level triggered)外,还提供边沿触发(edge Triggered),可以缓存IO状态,减少epoll_wait调用,提高效率
代码原型如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
int main(){
int lfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in serv;
bzero(&serv,sizeof(serv));
serv.sin_addr.s_addr = htonl(INADDR_ANY);
serv.sin_port = htons(8888);
serv.sin_family = AF_INET;
int opt = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bind(lfd,(struct sockaddr*)&serv,sizeof(serv));
listen(lfd,128);
struct sockaddr_in client;
socklen_t len = sizeof(client);
//create epoll node
int epfd = epoll_create(1);
struct epoll_event ev,evs[1024];
ev.data.fd = lfd;
ev.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
while(1){
int nready = epoll_wait(epfd,evs,1024,-1);
if(nready > 0){
int i = 0;
for(;i<nready;i++){
if(evs[i].data.fd == lfd){
if(evs[i].events & EPOLLIN){
int cfd = accept(lfd,(struct sockaddr*)&client,&len);
if(cfd > 0){
ev.data.fd = cfd;
//将新的连接上树
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
}
}
}
else{
//处理读事
if(evs[i].events & EPOLLIN){
char buf[256]={0};
int ret = read(evs[i].data.fd,buf,sizeof(buf));
if(ret > 0){
write(evs[i].data.fd,buf,ret);
}
else if(ret == 0){
//client closed
close(evs[i].data.fd);
ev.data.fd = evs[i].data.fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,evs[i].data.fd,&ev);
}
else{
perror("read err");
close(evs[i].data.fd);
ev.data.fd = evs[i].data.fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,evs[i].data.fd,&ev);
}
}
}
}
}
}
close(epfd);
close(lfd);
return 0;
}
总结
select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点:
1,在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
2,select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。
简述 进程、线程、协程的区别 以及应用场景?
1. 线程和进程: 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。 线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息( 如程序计数器、一组寄存器和栈)。 2. 线程、进程与协程: 线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员 协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保持状态,下次继续)。协程,则只使用一个线程, 在一个线程中规定某个代码块执行顺序。 协程的适用场景: 当程序中存在大量不需要CPU的操作时(IO),适用于协程;
GIL锁是什么鬼
GIL即全局解释器锁,全局解释器锁的存在,在同一时间内,python解释器只能运行一个线程的代码,这大大影响了python多线程的性能。而这个 解释器锁由于历史原因,现在几乎无法消除。
python GIL 之所以会影响多线程等性能,是因为在多线程的情况下,只有当线程获得了一个全局锁的时候,那么该线程的代码才能运行,而全局锁只有一个, 所以使用python多线程,在同一时刻也只有一个线程在运行,因此在即使在多核的情况下也只能发挥出单核的性能。
Python中如何使用线程池和进程池
答:Threadpool和ProcessPool模
threading.local的作用
答:Threading.local可以创建一个对象, 每个线程都可以对他读写属性, 但不会互相影响
进程之间如何进行通信
答:进程间通信(IPC,Interprocess communication),方法包括管道(PIPE)、消息排队、旗语、共用内存以及套接字(Socket)。
什么是并发和并行
答:并发是指一个处理器同时处理多个任务。 并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。 并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
进程锁和线程锁的作用
答: 线程锁: 大家都不陌生,主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行该段代码。 当有多个线程访问同一对象的加锁方法 / 代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。 但是,其余线程是可以访问该对象中的非加锁代码块的。
进程锁: 也是为了控制同一操作系统中多个进程访问一个共享资源,只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的, 但是可以使用本地系统的信号量控制(操作系统基本知识)。
解释什么是异步非阻塞
答:Asynchronous I / O, or non - blocking I / O, is a form of input / output processing that permits other processing to continue before the transmission has finished
路由器和交换机的区别
(1)工作层次不同
最初的的交换机是工作在OSI / RM开放体系结构的数据链路层,也就是第二层,而路由器一开始就设计工作在OSI模型的网络层。 由于交换机工作在OSI的第二层(数据链路层),所以它的工作原理比较简单,而路由器工作在OSI的第三层(网络层),可以得到更多的协议信息, 路由器可以做出更加智能的转发决策。
(2)数据转发所依据的对象不同
交换机是利用物理地址或者说MAC地址来确定转发数据的目的地址。而路由器则是利用不同网络的ID号(即IP地址)来确定数据转发的地址。 IP地址是在软件中实现的,描述的是设备所在的网络,有时这些第三层的地址也称为协议地址或者网络地址。MAC地址通常是硬件自带的,由网卡生产商来分配的, 而且已经固化到了网卡中去,一般来说是不可更改的。而IP地址则通常由网络管理员或系统自动分配。
(3)传统的交换机只能分割冲突域,不能分割广播域;而路由器可以分割广播域由交换机连接的网段仍属于同一个广播域,广播数据包会在交换机连接的所有网段上传播, 在某些情况下会导致通信拥挤和安全漏洞。连接到路由器上的网段会被分配成不同的广播域,广播数据不会穿过路由器。虽然第三层以上交换机具有VLAN功能, 也可以分割广播域,但是各子广播域之间是不能通信交流的,它们之间的交流仍然需要路由器。
(4)路由器提供了防火墙的服务
路由器仅仅转发特定地址的数据包,不传送不支持路由协议的数据包传送和未知目标网络数据包的传送,从而可以防止广播风暴。
什么是域名解析
域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为了方便记忆, 采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转换过程。域名的解析工作由DNS服务器完成。
如何修改本地hosts文件
hosts 文件所在位置
c: / windows / system32 / drivers / etc / hosts
左下角 搜索框 搜索 cmd
弹出命令框 输入 ipconfig / displaydns
显示所有 dns内容
ipconfig / flushdns
刷新所有 dns内容
生产者消费者模型应用场景及优势
生产者与消费者模式是通过一个容器来解决生产者与消费者的强耦合关系,生产者与消费者之间不直接进行通讯,而是利用阻塞队列来进行通讯, 生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取,实际应用中,生产者与消费者模式则主要解决生产者与消费者生产与消费的速率不一致的问题, 达到平衡生产者与消费者的处理能力,而阻塞队列则相当于缓冲区。
还有一个比较典型的例子便是日志的记录,多线程产生日志,但写日志由于文件独占,不能多线程来写,于是我们就可以把线程压入队列,由日志线程来读取队列数据,完成写日志的操作。
什么是cdn
CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。 通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、 负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
LVS是什么及作用
LVS是Linux Virtual Server ,Linux虚拟服务器;是一个虚拟的服务器集群【多台机器LBIP】。LVS 集群为三层结构:
负载调度器(loadbalancer):它是整个LVS集群对外的前端机器,负责将client请求发送到一组服务器[多台LBIP]上执行,而client端认为是返回来一个同一个IP【通常把这个IP称为虚拟IP / VIP】
服务器池(serverpool):一组真正执行client请求的服务器,一般是我们的web服务器;除了web,还有FTP,MAIL,DNS
共享存储(sharedstored):它为server pool提供了一个共享的存储区,很容易让服务器池拥有相同的内容,提供相同的服务
Nginx是什么及作用
1、静态HTTP服务器
首先,Nginx是一个HTTP服务器,可以将服务器上的静态文件(如HTML、图片)通过HTTP协议展现给客户端。
2、反向代理服务器
什么是反向代理?
客户端本来可以直接通过HTTP协议访问某网站应用服务器,网站管理员可以在中间加上一个Nginx,客户端请求Nginx,Nginx请求应用服务器, 然后将结果返回给客户端,此时Nginx就是反向代理服务器。
3、负载均衡
当网站访问量非常大,网站站长开心赚钱的同时,也摊上事儿了。因为网站越来越慢,一台服务器已经不够用了。于是将同一个应用部署在多台服务器上, 将大量用户的请求分配给多台机器处理。同时带来的好处是,其中一台服务器万一挂了,只要还有其他服务器正常运行,就不会影响用户使用。
4、虚拟主机
有的网站访问量大,需要负载均衡。然而并不是所有网站都如此出色,有的网站,由于访问量太小,需要节省成本,将多个网站部署在同一台服务器上。
例如将www.aaa.com和www.bbb.com两个网站部署在同一台服务器上,两个域名解析到同一个IP地址,但是用户通过两个域名却可以打开两个完全不同的网站, 互相不影响,就像访问两个服务器一样,所以叫两个虚拟主机。
5、FastCGI
Nginx本身不支持PHP等语言,但是它可以通过FastCGI来将请求扔给某些语言或框架处理(例如PHP、Python、Perl)。
28.keepalived是什么及作用?
eepalived是一个类似于Layer2, 4, 7 交换机制的软件。是Linux集群管理中保证集群高可用的一个服务软件,其功能是用来防止单点故障。
keepalived的工作原理: keepalived是基于VRRP协议实现的保证集群高可用的一个服务软件,主要功能是实现真机的故障隔离和负载均衡器间的失败切换,防止单点故障。在了解keepalived原理之前先了解一下VRRP协议。
VRRP协议: Virtual Route Redundancy Protocol虚拟路由冗余协议。是一种容错协议,保证当主机的下一跳路由出现故障时,由另一台路由器来代替出现故障的路由器进行工作, 从而保持网络通信的连续性和可靠性。
haproxy是什么以及作用
HAProxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。
HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在当前的硬件上 ,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。
HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 、系统调度器限制以及无处不在的锁限制, 很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户空间( User - Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle) 做更多的工作。
什么是负载均衡
负载平衡也称负载共享,是指对系统中的负载情况进行动态调整,以尽量消除或减少系统中各节点负载不均衡的现象。 具体实现方法是将过载节点上的任务转移到其他轻载节点上,尽可能实现系统各节点的负载平衡,从而提高系统的吞吐量。 负载共享有利于统筹管理分布式系统中的各种资源,便于利用共享信息及其服务机制扩大系统的处理能力。 动态负载共享策略是指把系统中各节点上已有的负载作为参考信息,在运行过程中,根据系统中各节点的负载状况, 随时调整负载的分配,使各节点尽可能保持负载的平衡。
什么是rpc及应用场景
调用本地服务一样调用远程服务,而让调用者对网络通信这些细节透明,那么将大大提高生产力,比如服务消费方在执行helloWorldService.sayHello(“test”)时, 实质上调用的是远端的服务。这种方式其实就是RPC(Remote Procedure Call Protocol)RPC调用的流程:
1)服务消费方(client)调用以本地调用方式调用服务;
2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
3)client stub找到服务地址,并将消息发送到服务端;
4)server stub收到消息后进行解码;
5)server stub根据解码结果调用本地的服务;
6)本地服务执行并将结果返回给server stub;
7)server stub将返回结果打包成消息并发送至消费方;
8)client stub接收到消息,并进行解码;
9)服务消费方得到最终结果。
简述 asynio模块的作用和应用场景
asyncio模块提供了一种关于事件循环的框架。事件循环就是等待一些任务发生,然后执行相应的事件。它也会处理例如IO操作或者系统事件。 asyncio实际中有好几种循环实现方式。模块默认使用的方式是其所运行的操作系统上最有效的方式。如果你愿意,你也可以显式地选择其它事件循环方式。 一个事件循环就是当事件A发生时,函数B共同起作用。
设想这样一个场景,服务器等待用户访问并请求一些资源,例如网页。如果这个网站不是非常知名的网站,这个服务器将会在很长的时间内处于空闲状态。 但是,一旦某个时间用户点击了这个网站,服务器就需要作出响应。这个响应就是事件处理。当一个用户下载网页,服务器将会去检查并调用一个或者多个事件句柄。 一旦这些事件句柄完成相应的处理,它们需要将控制交回给事件循环。为了在Python中完成这个任务,asyncio使用协程。
协程是一个特殊的函数,可以将控制交回给它的调用函数,但是并不丢失它的状态。协程是一个消费者函数,并且是生成器的扩展。 协程相比线程最大的优势就是执行协程时不需要占用太多内存。你需要注意的是,当你调用一个协程函数,它并没有真正执行。 相反,它将会返回一个协程对象,你可以将这个协程对象传递给事件循环,然后可以立即或者稍后执行它。
当你在使用asyncio模块时,另一个你可能会执行的是future。future就是一个可以表示还没有结束的任务结果的对象。 你的事件循环可以观察future对象并等待它们结束。当一个future结束时,它被设置为已完成。asyncio模块也支持锁和信号。
简述 gevent模块的作用和应用场景
gevent是第三方库,通过greenlet实现协程,其基本思想是:当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成, 再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成
使用gevent,可以获得极高的并发性能,但gevent只能在Unix / Linux下运行,在Windows下不保证正常安装和运行。
由于gevent是基于IO切换的协程,所以最神奇的是,我们编写的Web App代码,不需要引入gevent的包,也不需要改任何代码,仅仅在部署的时候,用一个支持gevent的WSGI服务器,立刻就获得了数倍的性能提升。
twisted框架的使用和应用
Twisted应用的基本问题,可说是“一个中心,两个基本点”,即:
以“事件”event为中心,以 "建立连接" connect和“定义反馈“callback为基本点。
Twisted 对event 的管理机制,可划分为后台和前台两种形式。
后台的管理,是Twisted 框架的内在机制,自动运行,对程序员透明无须干预,在程序文本中不见其踪迹。
前台的管理,是Twisted 授权程序员,在程序文本中显式写码来实现。程序员的工作,主要是按照既定的方式,实现 event。我们所关心、所用到的,是这部分东西(API)。
转载于:https://www.cnblogs.com/Rglin/p/10786398.html
最后
以上就是坚强帆布鞋为你收集整理的网络编程,并发编程的全部内容,希望文章能够帮你解决网络编程,并发编程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复