1. 程式人生 > >單進程-非阻塞服務器

單進程-非阻塞服務器

imp conn while blog cnblogs 發生 n) client drl

from socket import *
#1.創建socket
serSocket = socket(AF_INET, SOCK_STREAM)

#2. 綁定本地ip以及port
localAddr = (‘‘, 7788)
serSocket.bind(localAddr)

#3. 讓這個socket 變為非堵塞
serSocket.setblocking(False)

#4. 將socket變為監聽(被動)套接字
serSocket.listen(100)

# 用來保存所有已經連接的客戶端的信息
clientAddrList = []

while True:

    #等待一個新的客戶端的到來(即完成3次握手的客戶端)
try: clientSocket,clientAddr = serSocket.accept() except: pass else: print("一個新的客戶端到來:%s"%str(clientAddr)) clientSocket.setblocking(False) clientAddrList.append((clientSocket,clientAddr)) for clientSocket,clientAddr in clientAddrList:
try: recvData = clientSocket.recv(1024) except: pass else: if len(recvData)>0: print("%s:%s"%(str(clientAddr), recvData)) else: clientSocket.close() clientAddrList.remove((clientSocket, clientAddr))
print("%s 已經下線"%str(clientAddr))

select,輪詢最多可監聽1024個人,poll無默認值,但是也是輪詢

import select
import socket
import sys


server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((‘‘, 7788))
server.listen(5)

inputs = [server, sys.stdin]

running = True

while True:

    # 調用 select 函數,阻塞等待
    readable, writeable, exceptional = select.select(inputs, [], [])

    # 數據抵達,循環
    for sock in readable:

        # 監聽到有新的連接
        if sock == server:
            conn, addr = server.accept()
            # select 監聽的socket
            inputs.append(conn)

        # 監聽到鍵盤有輸入
        elif sock == sys.stdin:
            cmd = sys.stdin.readline()
            running = False
            break

        # 有數據到達
        else:
            # 讀取客戶端連接發送的數據
            data = sock.recv(1024)
            if data:
                sock.send(data)
            else:
                # 移除select監聽的socket
                inputs.remove(sock)
                sock.close()

    # 如果檢測到用戶輸入敲擊鍵盤,那麽就退出
    if not running:
        break

server.close()

epoll非輪詢,無限制,事件通知機制

  • EPOLLIN (可讀)
  • EPOLLOUT (可寫)
  • EPOLLET (ET模式)

epoll對文件描述符的操作有兩種模式:LT(level trigger)和ET(edge trigger)。LT模式是默認模式,LT模式與ET模式的區別如下:

LT模式:當epoll檢測到描述符事件發生並將此事件通知應用程序,應用程序可以不立即處理該事件。下次調用epoll時,會再次響應應用程序並通知此事件。

ET模式:當epoll檢測到描述符事件發生並將此事件通知應用程序,應用程序必須立即處理該事件。如果不處理,下次調用epoll時,不會再次響應應用程序並通知此事件。
import socket
import select

# 創建套接字
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 設置可以重復使用綁定的信息
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

# 綁定本機信息
s.bind(("",7788))

# 變為被動
s.listen(10)

# 創建一個epoll對象
epoll=select.epoll()

# 測試,用來打印套接字對應的文件描述符
# print s.fileno()
# print select.EPOLLIN|select.EPOLLET

# 註冊事件到epoll中
# epoll.register(fd[, eventmask])
# 註意,如果fd已經註冊過,則會發生異常
# 將創建的套接字添加到epoll的事件監聽中
epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET)


connections = {}
addresses = {}

# 循環等待客戶端的到來或者對方發送數據
while True:

    # epoll 進行 fd 掃描的地方 -- 未指定超時時間則為阻塞等待
    epoll_list=epoll.poll()

    # 對事件進行判斷
    for fd,events in epoll_list:

        # print fd
        # print events

        # 如果是socket創建的套接字被激活
        if fd == s.fileno():
            conn,addr=s.accept()

            print(有新的客戶端到來%s%str(addr))

            # 將 conn 和 addr 信息分別保存起來
            connections[conn.fileno()] = conn
            addresses[conn.fileno()] = addr

            # 向 epoll 中註冊 連接 socket 的 可讀 事件
            epoll.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)


        elif events == select.EPOLLIN:
            # 從激活 fd 上接收
            recvData = connections[fd].recv(1024)

            if len(recvData)>0:
                print(recv:%s%recvData)
            else:
                # 從 epoll 中移除該 連接 fd
                epoll.unregister(fd)

                # server 側主動關閉該 連接 fd
                connections[fd].close()

                print("%s---offline---"%str(addresses[fd]))

單進程-非阻塞服務器