编写案例分别使用多进程、多路复用(select、epoll)实现tcp服务

-------------------------------多进程的tcp服务器-------------------------------
通过为每个客户创建一个进程的方式,能够同时为多个客户进行服务器
当客户不是特别多的时候,这种方式还行,如果有几百上千个,就不可取了,因为每次创建进程等过程需要好较大的资源

python代码案例:

 1 #coding=utf-8
 2
 3 #引用对应的包
 4 from socket import *
 5
 6 from multiprocessing import Process
 7
 8 import sys
 9
10 #进程函数:为客户提供tcp服务
11 def tcpClient(newSocket,destAddr):
12     print("客户端(%s)以上线"%str(destAddr))
13     while True:
14         #数据的接受
15         recvData=newSocket.recv(1024)
16         #模拟echo将数据回发服务器
17         newSocket.send(recvData)
18
19         #如果接收的数据长度为0,进行客户端的关闭操作
20         if len(recvData) <= 0:
21             print("------客户端(%s)已经下线-------"%str(destAddr))
22
23             newSocket.close()
24             break
25         print ("客户端(%s)传递过的数据为:%s"%(str(destAddr),recvData))
26
27
28 #函数:main
29 def main():
30     #创建Tcp套接字
31     socTcpSer=socket(AF_INET,SOCK_STREAM)
32
33     socTcpSer.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
34
35     #创建ip和端口进行绑定
36     local=("",int(sys.argv[1]))
37     socTcpSer.bind(local)
38
39     #开启监听程序
40     socTcpSer.listen(5)
41
42     print(‘---------等待客户端上线---------‘)
43
44     #进行服务的开启,并循环为客户进行服务
45     while True:
46
47         #接受客户端响应信息
48         newSocket,destAddr=socTcpSer.accept()
49
50         #创建子进程
51         pClient=Process(target=tcpClient,args=(newSocket,destAddr,))
52
53         #子进程开始
54         pClient.start()
55
56     socTcpSer.close()
57
58
59 #程序入口
60 if __name__==‘__main__‘:
61     main()

多进程实现tcp服务

-------------------------------多路复用的服务器(select)-------------------------------

网络通信被Unix系统抽象为文件的读写,通常是一个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回改文件描述符。当遍历结束之后,如果仍然没有一个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。

缺点:
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.对socket进行扫描时是依次扫描的,即采用轮询的方法,效率较低。当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。

应用:
#import select:对应import包的引用操作

#readable,writeable,exceptinal=select.select(inputs,[],[]):使用select对套接字组成的列表进行遍历,过滤出对应的未堵塞的套接字

python代码示例:

 1 #coding=utf-8
 2
 3 #引用对应的包
 4 from socket import *
 5
 6 import select
 7
 8 import sys
 9
10 #函数:main
11 def main():
12     #创建套接字
13     serTcpSocket=socket(AF_INET,SOCK_STREAM)
14
15     #绑定端口和ip,sys.argv[1] 运行时传递的参数
16     serTcpSocket.bind(("",int(sys.argv[1])))
17
18     #打开被动监听
19     serTcpSocket.listen(5)
20
21     print("-----服务器开启-----")
22
23     #创建列表,存储对应的套接字
24     inputs=[serTcpSocket]
25
26     #循环
27     while True:
28         #使用select进行对应套接字的处理过滤
29         readList,writeList,exceptList=select.select(inputs,[],[])
30
31         #遍历readList列表进行操作
32         for sockItem in readList:
33             #如果为服务器套接字,进行accept()数据的接收
34             if sockItem==serTcpSocket:
35                 #监听接受客户端传递过来的数据信息
36                 newSocket,destAddr=sockItem.accept()
37
38                 print("客户端(%s)以上线"%str(destAddr))
39
40                 #将用于与客户端通信的套接字进行存储
41                 inputs.append(newSocket)
42
43             else:
44                 #进行客户端发送过来的数据的接收
45                 recvData=sockItem.recv(1024)
46
47                 #进行判断,如果传递过来的数据不为空
48                 if len(recvData)>0:
49                     #进行客户端发送过来的数据的打印操作
50                     print("客户端(%s):%s"%(str(destAddr),recvData))
51                 else:
52                     #表示客户端下线
53                     print("客户端(%s)以下线!"%str(destAddr))
54
55                     #进行对应该客户端套接字的关闭操作
56                     sockItem.close()
57
58                     #将该套接字从对应的列表中移除
59                     inputs.remove(sockItem)
60
61 #程序入口
62 if __name__=="__main__":
63     main()

select多路复用实现tcp服务

-------------------------------多路复用的服务器(epoll)-------------------------------
优点:
1、没有最大并发连接的限制,能打开的FD的上限远大于1024
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select。

应用:
#import select:引用select包
#epoll=select.epoll():创建epoll对象
#soc.fileno():获取套接字对应的文件描述符
#epoll.register(soc.fileno(),select.EPOLLIN|select.EPOLLET):将创建的套接字添加到epoll的事件监听
EPOLLIN (可读)
EPOLLOUT (可写)
EPOLLET (ET模式)
epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
LT模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll时,会再次响应应用程序并通知此事件。
ET模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll时,不会再次响应应用程序并通知此事件。

#epollList=epoll.poll():进行已注册套接字的扫描操作
#epoll.unregister(soc.fileno()):从epoll对象中注销该套接字

 1 #coding=utf-8
 2
 3 #引用对应的包
 4 from socket import *
 5
 6 import select
 7
 8 import sys
 9
10
11 #函数:main
12 def main():
13     #创建服务器的套接字
14     serTcpSocket=socket(AF_INET,SOCK_STREAM)
15
16     #进行端口和ip的绑定操作
17     serTcpSocket.bind(("",int(sys.argv[1])))
18
19     #开启被动,进行监听
20     serTcpSocket.listen(10)
21
22     #创建一个epoll对象
23     epoll=select.epoll()
24
25     #使用epoll对套接字在操作系统中进行注册
26     epoll.register(serTcpSocket.fileno(),select.EPOLLIN|select.EPOLLET)
27
28     #创建两个字典
29     #根据套接字的文件标识符对应套接字
30     connection={}
31
32     #根据套接字的文件标识符对应ip和端口元组信息
33     address={}
34
35     #提示服务开启
36     print("------服务开启-----")
37
38     #进行循环遍历
39     while True:
40         #接收对象中,未阻塞的套接字
41         epollList = epoll.poll()
42
43         for fd,event  in epollList:
44             #判断是否为服务器的套接字
45             if fd==serTcpSocket.fileno():
46                 #接收客户端传递过来的数据信息
47                 newSocket,destAddr=serTcpSocket.accept()
48
49                 print("客户端(%s)以上线"%str(destAddr))
50
51                 #将对应的数据向字典中进行存储
52                 connection[newSocket.fileno()]=newSocket
53                 address[newSocket.fileno()]=destAddr
54
55                 #将套接字在对应的epoll对象中进行注册
56                 epoll.register(newSocket.fileno(),select.EPOLLIN|select.EPOLLET)
57
58             elif event==select.EPOLLIN:
59                 #在字典中取出对应的套接字对象和地址(ip和端口)
60                 soc=connection[fd]
61                 addr=address[fd]
62
63                 #接收客户端发送过来的数据信息
64                 recvData=soc.recv(1024)
65
66                 #判断客户端是否下线
67                 if len(recvData)>0:
68                     #进行打印
69                     print("客户端(%s):%s"%(str(addr),recvData))
70
71                     #echo服务,将信息回发到客户端
72                     soc.send(recvData)
73                 else:
74                     print("客户端(%s)以下线"%str(addr))
75
76                     #进行该套接字在epoll中的注销操作
77                     epoll.unregister(fd)
78
79                     #关闭该套接字
80                     soc.close()
81
82
83
84
85 #程序入口
86 if __name__=="__main__":
87     main()

多路复用epoll实现tcp服务

时间: 2024-11-16 18:32:51

编写案例分别使用多进程、多路复用(select、epoll)实现tcp服务的相关文章

转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的.下面记录下分别基于Select/Poll/Epoll的echo server实现.Python Select Server,可监控事件数量有限制: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <arpa/inet.h> #include &q

IO多路复用(二) -- select、poll、epoll实现TCP反射程序

接着上文IO多路复用(一)-- Select.Poll.Epoll,接下来将演示一个TCP回射程序,源代码来自于该博文https://www.cnblogs.com/Anker/p/3258674.html 博主的几篇相关的文章,在这里将其进行了整合,突出select.poll和epoll不同方法之间的比较,但是代码的结构相同,为了突出方法之间的差别,可能有的代码改动的并不合理,实际中使用并非这么写. 程序逻辑 该程序的主要逻辑如下: 服务器: 1. 开启服务器套接字 2. 将服务器套接字加入要

五种网络IO模型以及多路复用IO中select/epoll对比

下面都是以网络读数据为例 [2阶段网络IO] 第一阶段:等待数据 wait for data 第二阶段:从内核复制数据到用户 copy data from kernel to user 下面是5种网络IO模型 [阻塞blocking IO] 两阶段全程阻塞 recvfrom -> [syscall -> wait -> copy ->] return OK [非阻塞nonblocking IO] 第一阶段是非阻塞的不断检查是否数据准备好,第二阶段阻塞读取数据 recvfrom -&

I/O多路复用select/poll/epoll

前言 早期操作系统通常将进程中可创建的线程数限制在一个较低的阈值,大约几百个.因此, 操作系统会提供一些高效的方法来实现多路IO,例如Unix的select和poll.现代操作系统中,线程数已经得到了极大的提升,如NPTL线程软件包可支持数十万的线程. I/O多路复用 select select 允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或指定时间后返回它. select函数原型 #include <sys/select.h> #include <sys/t

IO多路复用select、poll、epoll的区别

(1)select==>时间复杂度O(n) 它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作.所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长. (2)poll==>时间复杂度O(n) poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的

I/O多路复用之epoll

1.select.poll的些许缺点 先回忆下select和poll的接口 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int poll(struct pollfd *fds, nfds_t nfds, int timeout); 这两个多路复用实现的特点是: 每次调用select和poll都要把用户关心的事件集合(select为readf

I/O多路复用 SELECT POLL -- 内核实现

等待队列 先补充个基础知识――等待队列 认识 定义 wait_queue_head_t wait_queue; 初始化 init_waitqueue_head(&wait_queue); 等待 wait_event(queue, condition)   等待某个条件而进入睡眠 wait_event_interruptible(queue, condition)  等待某个条件而进入睡眠并允许信号中断睡眠 wait_event_timeout(queue, condition,timeout)

用C写一个web服务器(二) I/O多路复用之epoll

.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .container::before,.container::after { content: " "; display: table } .container::after { clear: both } .container::before,.container::after { content: