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()

如果覺得我的文章對您有用,請隨意打賞。您的支持將鼓勵我繼續創作!

一條評論

發表評論

電子郵件地址不會被公開。 必填項已用*標注

中了亿元大奖