select簡單示例,有註釋

所有都在代碼中:python

import select
import socket
import queue

"""
簡單的select 實現echo server
我的理解:
select 編程思想,讓select去去判斷能不能讀或能不能寫。
若是能讀或能寫,select就會告知readable或writeable,這時就得讓文件句柄去讀或寫,
在這裏是scoket,recv()或scoket.send()去執行實際的操做
"""
server = socket.socket()   # 獲取一個套接字
server.setblocking(False)  # 設置爲非阻塞模式
server.bind(('127.0.0.1',9001))  # 綁定ip和端口
server.listen(5)           # 啓動監聽,到這步能夠在netstat 看到監聽狀態,若是沒有server.accept()的話,客戶端的鏈接會被拒絕

inputs = [ server ]        # 設置輸入隊列,當server的監聽收到客戶端鏈接時,select會認爲這個server爲可讀,就得讓server.accpet()去接收客戶端鏈接
                           # 或者當socket有新的數據發送過來時,select會認爲這個socket是可讀的,就得去讓socket的recv()去接收數據
outputs = []               # 當socket能夠發送數據給對端時,select會認定這個socket是
message_queue = {}         # 消息隊列,key值爲socket套接字
n = 0                      # 體現while循環了多少次
while inputs:              # inputs中是否存在值
    print("waiting for connect..., %s"%n)
    readable , writeable ,exceptions = select.select(inputs,outputs,inputs)             # select就像一個很是強大的監視器,監視各個文件句柄的是否可讀可寫。

    for s in readable:                  # 循環可讀的。
        if s is server:                 # 若是是server套接字
            print("server can read...")
            conn, addr = s.accept()          # 就接收客戶端鏈接
            print("welcome (%s,%s) to connect..."%addr)
            inputs.append(conn)              # 將客戶端套鏈接接字放入inputs,而不是立刻接收或發生數據。
            # s.setblocking(False)

        else:                             # 若是不是server套接字
            print("a scoket can read:" ,s.getpeername())    # 只有當有數據發送過來時纔會把那個套接字放在readable裏
            try:
                data = s.recv(1024)       # 就代表客戶端鏈接套接字收到了客戶端發送來的數據
            except Exception as e:   # 客戶端進程被強制關閉,致使客戶端發送rst包。 ConnectionResetError
                print(e)
                inputs.remove(s)          # 若是客戶端退出,就講這個套接字從inputs中移除
                if s in outputs:          # 若是這個套接字存在於outputs中,也將這個套接字從outputs中移除,由於客戶端都關閉了
                                          # 還將這個套接字放在outputs中沒有了,發不了數據了啊。
                    outputs.remove(s)
                s.close()                 # 記得關閉這個套接字
                break                     # 這裏要break,由於s.recv(1024)異常了,data根本就沒有被定義,下面的if data就會報錯。

            if data:                      # 若是有數據
                print("recv from %s:%s"%(s.getpeername(),data.decode()))
                message_queue[s] = queue.Queue()      # 將收到的數據放入消息隊列中
                message_queue[s].put(data)
                outputs.append(s)                     # 由於是echo server,全部有道數據後就得原有返回數據,可是這裏不當即返回,而是交由select.
            else:
                print("the client (%s,%s) is closed "%s.getpeername())             # 若是data爲空,表示客戶端正常關閉鏈接,客戶端發送了fin.
                if s in outputs:                      # 客戶端關閉了,這個套接字也就不必放在outputs和inputs中了
                    outputs.remove(s)
                inputs.remove(s)
                del message_queue[s]                  # 消息隊列也能夠清除
                s.close()                             # 關閉套接字

    for s in writeable:                               # 若是可寫,「好像ouputs有鏈接的套接字 存在就是可寫的。」
        try:
            data = message_queue[s].get_nowait()      # 獲取要發送的消息
        except queue.Empty:                           # 消息隊列爲空也是可寫的。
            print("outpt queue for (%s,%s) in empty"%s.getpeername())
            outputs.remove(s)                         # 由於是echo server,因此消息隊列爲空時,說明已經迴應了數據給對端,就不用一直去判斷這個套接字是否可寫了。
            # s.close()     # 沒有數據可發送並不表明要關閉socket
        else:
            print("send data to client (%s,%s)"%s.getpeername())
            s.send(data)
            # outputs.remove(s)           # 發送完一條數據後,其實不能將他移除,由於有可能對端很是迅速的發了多條消息過來,而服務器一條消息都還沒來得及回覆,
                                          # 服務器的消息將會都放在隊列中等待發送,因此這裏的讓隊列爲空時在把套接字從outputs中移除。

    for s in exceptions:                # 若是發生錯誤
        print("expect happend on (%s,%s)"%s.getpeername())
        inputs.remove(s)                # 將這個套接字從inouts,outputs中移除
        if s in outputs:
            outputs.remove(s)
        s.close()                       # 鏈接要關閉
        del message_queue[s]            # 消息隊列也要關閉
    n += 1                              # 看看while循環可多少次
waiting for connect..., 0
server can read...
welcome (127.0.0.1,3137) to connect...
waiting for connect..., 1
server can read...
welcome (127.0.0.1,3138) to connect...
waiting for connect..., 2
a scoket can read: ('127.0.0.1', 3137)   # 這裏說明第一個客戶端套接字在readable裏,下面也沒看到第二個客戶端在readable裏直到第二個客戶端發送數據過來,也就是說select只是把可讀的返回給了readable裏。
recv from ('127.0.0.1', 3137):this is the 1st client
waiting for connect..., 3
send data to client (127.0.0.1,3137)
waiting for connect..., 4 outpt queue for (127.0.0.1,3137) in empty
waiting for connect..., 5
a scoket can read: ('127.0.0.1', 3138)
recv from ('127.0.0.1', 3138):this is the 2nd client
waiting for connect..., 6
send data to client (127.0.0.1,3138)
waiting for connect..., 7
outpt queue for (127.0.0.1,3138) in empty
waiting for connect..., 8
a scoket can read: ('127.0.0.1', 3138)
recv from ('127.0.0.1', 3138):2nd haha
waiting for connect..., 9
send data to client (127.0.0.1,3138)
waiting for connect..., 10
outpt queue for (127.0.0.1,3138) in empty
waiting for connect..., 11
a scoket can read: ('127.0.0.1', 3137)
recv from ('127.0.0.1', 3137):1st hello
waiting for connect..., 12
send data to client (127.0.0.1,3137)
waiting for connect..., 13
outpt queue for (127.0.0.1,3137) in empty
waiting for connect..., 14
a scoket can read: ('127.0.0.1', 3138)
recv from ('127.0.0.1', 3138):2nd wiil killed by os
waiting for connect..., 15
send data to client (127.0.0.1,3138)
waiting for connect..., 16
outpt queue for (127.0.0.1,3138) in empty
waiting for connect..., 17
a scoket can read: ('127.0.0.1', 3138)
[WinError 10054] 遠程主機強迫關閉了一個現有的鏈接。
waiting for connect..., 18
a scoket can read: ('127.0.0.1', 3137)
recv from ('127.0.0.1', 3137):1st wiil closed by socket.close()
waiting for connect..., 19
send data to client (127.0.0.1,3137)
waiting for connect..., 20
outpt queue for (127.0.0.1,3137) in empty
waiting for connect..., 21
a scoket can read: ('127.0.0.1', 3137)
the client (127.0.0.1,3137) is closed 
waiting for connect..., 22

 

 

客戶端:編程

import socket

s = socket.socket()
s.connect(('127.0.0.1',9001))

while True:
    data = input(">> ").strip()
    if data == "":
        continue
    if data == "q":
        s.close()
        break
    s.send(bytes(data,encoding="utf-8"))
    data = s.recv(1024)
    print("recv:",data.decode())
相關文章
相關標籤/搜索