python单进程tcp服务器-select版-epoll版-gevent版

作者: 鲁智深 分类: Python 发布时间: 2017-06-26 01:30

tcp原理

tcp原理


select版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#第一题、单进程tcp服务器-select版(共计30分)
#为什么要用select,因为我自己遍历的话也可以实现并发效果,但是如果有1000个客户端链接,那用遍历的效果就非常差

from socket import *
from select import select
import sys

def main():
    #创建套字接
    tcp_socket = socket(AF_INET,SOCK_STREAM)
    tcp_socket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#setsockopt获得端口重用
    #绑定端口
    tcp_socket.bind(("",8888))

    #监听,等待客户端的链接
    tcp_socket.listen(5)
    #这里定义了一个需要select监听的列表,列表里面是需要监听的对象(等于系统监听的文件描述符)
    #这里监听socket套接字和用户的输入
    socket_list = [tcp_socket,sys.stdin]
    wirte_list = []#可以写的列表(可发数据)

    print("---sys.stdin---",sys.stdin)

    is_run = False  # true就退出程序,False继续执行

    try:
        while True:
            # 开始检查,返回三个列表
            # 判断----》可以读的列表(可以收数据)
            # 判断----》可以写的列表(可发数据)
            # 判断----》列表中的socket出错
            # 如果没有curl服务器,此时没有建立tcp客户端连接,因此改列表内的对象都是数据资源不可用。因此select阻塞不返回。
            readable,wirteable,excep = select(socket_list,wirte_list,[])

            for wirte in wirteable:
                wirte.send("xxx".encode("gb2312"))

            for sock in readable:#循环遍历[tcp_socket,sys.stdin,new_socket],sock代表这3条数据
                print("---sys.stdin---", sys.stdin)

                if sock == tcp_socket:
                    new_socket, new_addrss = tcp_socket.accept()
                    print("有新链接链接",new_addrss)
                    socket_list.append(new_socket)
                    print(socket_list)#打印后发现有3条数据[tcp_socket,sys.stdin,new_socket],sock代表这3条数据

                elif sock == sys.stdin:#当在屏幕输入的时候,会被循环检查到
                    print("---sock---", sock)
                    print("---sys.stdin---", sys.stdin)
                    is_run = input("")#给 is_run 赋值为 True
                    break

                else:#反之就是 new_socket
                    recv_data = sock.recv(1024)#阻塞

                    wirte_list.append(sock)#当客户端发送信息时候,将sock添加到发送列表中,服务器就将内容放回客户端

                    if len(recv_data) > 0:
                        print(recv_data.decode("gb2312"))#打印
                        sock.send("dd".encode("gb2312"))#发送到客户端
                    else:
                        print("有客户端已经下线")
                        sock.close()#关闭套接字
                        socket_list.remove(sock)#删除返回的套接字

            if is_run == "q":#为q就退出循环
                break
    finally:#执行,关闭套接字
        tcp_socket.close()

main()

#学习地址:http://python.jobbole.com/84058/

epoll版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from socket import *
import select

def main():
    tcp_socket = socket(AF_INET, SOCK_STREAM)#套接字
    tcp_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)#端口重用
    # 绑定端口
    tcp_socket.bind(("",5555))
    # 监听,等待客户端的链接
    tcp_socket.listen(5)
    #创建epoll
    epoll = select.epoll()
    #注册监听sockect
    #第一个参数是:要监听的socket的文件描述符
    #第二参数:要监听接收数据和立刻通知
    #ET模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。下次将不会在通知。
    #注册概念,我要监听谁就将谁注册进去,还可以设置通知机制
    print(tcp_socket.fileno())#tcp_socket的文件描述符 3
    epoll.register(tcp_socket.fileno(),select.EPOLLIN|select.EPOLLET)#写2个参数的时候,实现方式
    print(tcp_socket.fileno())#tcp_socket的文件描述符 3

    socket_list = {}#保存的new_socket
    address_lists = {}#保存的new_address

    try:
        while True:
            epoll_list = epoll.poll()#如果有新的客户端和可以收数据的socket和断开的socket就解除阻塞,并且返回列表
            print(epoll_list)#[(3, 1)] 3是文件描述符,1是有数据进来了

            for fd,event in epoll_list:

                # 当前的文件描述符,是3,就是 tcp_socket
                if fd == tcp_socket.fileno():#当有客户端链接的时候
                    print(fd)
                    print(tcp_socket.fileno())
                    # 有新的客户端链接了
                    new_socket,new_address = tcp_socket.accept()
                    # 当有新的链接就会创建新的sockect,但是也要注册到epoll中
                    epoll.register(new_socket.fileno(), select.EPOLLIN | select.EPOLLET)

                    socket_list[new_socket.fileno()] = new_socket
                    address_lists[new_socket.fileno()] = new_address
                    print("有客户端[%s]链接进来了" % str(new_address))

                # 当有客户端的数据发送过来的时候
                elif event == select.EPOLLIN:#如果event == 1
                    #取数据
                    new_socket = socket_list[fd]
                    new_address = address_lists[fd]
                    recv_data = new_socket.recv(1024)#收数据

                    if len(recv_data) > 0:#打印数据,并将数据返回
                        print(recv_data)
                        new_socket.send(recv_data)
                    else:
                        print("客户端已经关闭",(str(new_address)))
                        new_socket.close()#关闭套接字

    finally:
        tcp_socket.close()
main()

gevent版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import gevent
from gevent import monkey,socket  #第三方的gevent为Python提供了比较完善的协程支持
monkey.patch_all()#不是用系统的api

#接收
def recv(new_socket,new_address):
    while True:
        recv_data = new_socket.recv(1024)
        if len(recv_data) > 0:
            print(recv_data,new_address)
        else:
            print("链接关闭")
            new_socket.close()
            break

def main():
    s = socket.socket()#这些都是gevent里面的socket
    s.bind(("",3333))
    s.listen(5)

    while True:
        new_socket,new_address = s.accept()
        gevent.spawn(recv,new_socket,new_address)#gevent方法,参数recv函数引用,新的套接字,id

main()

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

一条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注