Python學習_socket學習 & socketserver學習 & IO多路複用

簡單的socket項目:python

client端:多線程

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

obj = socket.socket()

obj.connect(("127.0.0.1", 9999,))

# recv 也是阻塞的
ret_bytes = obj.recv(1024)
ret_str = str(ret_bytes, encoding="utf-8")
print(ret_str)
while True:
    inp = input("請輸入要發送的內容:")
    if inp == "q":
        obj.sendall(bytes(inp, encoding="utf-8"))
        break
    else:
        obj.sendall(bytes(inp, encoding="utf-8"))
        ret = str(obj.recv(1024), encoding="utf-8")
        print(ret)
obj.close()

server端:併發

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

sk = socket.socket()
# 綁定ip和端口
sk.bind(("127.0.0.1", 9999,))
# 開啓監聽
sk.listen(5)
# 接收客戶端的請求
while True:
    conn, address = sk.accept()  # accept阻塞
    conn.sendall(bytes("歡迎郭屌毛訪問!", encoding="utf-8"))
    while True:
        # 接收客戶端發過來的消息,限制1024個字節
        ret_bytes = conn.recv(1024)
        ret_str = str(ret_bytes, encoding="utf-8")
        if ret_str == "q":
            break
        # 給客戶端發送消息
        conn.sendall(bytes(ret_str + "你好", encoding="utf-8"))

爲解決socket的併發問題,使用socketserver:app

#!/usr/bin/env python
# -*- coding:utf-8 -*-

# 解決socket併發問題,使用socketserver
import socketserver


# 定義一個class,必須繼承 socketserver.BaseRequestHandler 類
class MyServer(socketserver.BaseRequestHandler):
    # 重寫handle方法
    def handle(self):
        print(self.client_address)
        print(self.server)
        print(self.request)
        conn = self.request
        conn.sendall(bytes("歡迎訪問xxx系統!", encoding="utf-8"))
        while True:
            ret_bytes = conn.recv(1024)
            ret_str = str(ret_bytes, encoding="utf-8")
            if ret_str == "q":
                break
            conn.sendall(bytes(ret_str + " 你好!", encoding="utf-8"))


if __name__ == '__main__':
    # 使用剛纔的類建立server
    server = socketserver.ThreadingTCPServer(("127.0.0.1", 9999), MyServer)
    # serve_forever 等價於 while True,使server一直阻塞,等待鏈接
    server.serve_forever()

socketserver源碼分析:異步

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socketserver


class MyServer(socketserver.BaseRequestHandler):

    def handle(self):
        self.request


if __name__ == '__main__':
    # socket + select + 多線程
    # ip和端口,類名
    # MyServer == RequestHandlerClass
    # ThreadingTCPServer.init() => TCPServer.init() => BaseServer.init()
    # server 對象:
        # self.server_address == ("127.0.0.1", 9999)
        # self.RequestHandlerClass == MyServer
        # self.socket = 建立的socket對象
    server = socketserver.ThreadingTCPServer(("127.0.0.1", 9999), MyServer)
    # server對象的serve_forever(), 在 BaseServer 中找到serve_forever()
    # --------執行流程以下---------
    # BaseServer.serve_forever() => BaseServer._handle_request_noblock() => ThreadingMixIn.process_request()
    # => ThreadingMixIn.process_request_thread() => BaseServer.finish_request()
    # => self.RequestHandlerClass(request, client_address, self) 等價於 MyServer() 執行 BaseRequestHandler.init()方法
    server.serve_forever()

IO多路複用:socket

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import select

# IO多路複用: 能夠監聽多個 文件描述符(socket對象)(文件句柄),一旦文件句柄出現變化,便可感知

sk1 = socket.socket()
sk1.bind(("127.0.0.1", 8001))
sk1.listen(5)

# sk2 = socket.socket()
# sk2.bind(("127.0.0.1", 8002))
# sk2.listen(5)
#
# sk3 = socket.socket()
# sk3.bind(("127.0.0.1", 8003))
# sk3.listen(5)

print("sk1 ", sk1)
inputs = [sk1, ]
outputs = []
message_dict = {}

while True:
    # select 內部自動監聽sk1,sk2,sk3三個對象,一旦某個句柄發生變化
    # 解釋:
    # select內部自動監聽sk1,sk2,sk3三個對象,監聽三個句柄是否發生變化,把發生變化的元素放入r_list中。
    # 若是有人鏈接sk1,則r_list = [sk1]
    # 若是有人鏈接sk1和sk2,則r_list = [sk1,sk2]
    # select中第1個參數表示inputs中發生變化的句柄放入r_list。
    # select中第2個參數表示[]中的值原封不動的傳遞給w_list。
    # select中第3個參數表示inputs中發生錯誤的句柄放入e_list。
    # 參數1表示1秒監聽一次

    # 若是有人第一次來鏈接,sk1發送變化
    # r_list = [sk1]
    r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1)
    print("正在監聽的socket對象:%d" % len(inputs))
    print(r_list)
    for sk_or_conn in r_list:
        # 每個鏈接對象
        if sk_or_conn == sk1:
            # 表示有新用戶來鏈接了
            conn, address = sk_or_conn.accept()
            # inputs = [sk1, conn, ....]
            inputs.append(conn)
            # message_dict = {"conn1":[], "conn2":[], ...}
            message_dict[conn] = []
        else:
            # 有老用戶發消息了
            try:
                # 當用戶鏈接中斷的時候,data_bytes 爲空 == 此狀況適用於2.7版本
                data_bytes = sk_or_conn.recv(1024)
            except Exception as e:
                # 若是用戶中斷鏈接
                print(e)
                inputs.remove(sk_or_conn)
            else:
                # 用戶正常發送消息
                data_str = str(data_bytes, encoding="utf-8")
                # sk_or_conn.sendall(bytes(res + " 你好!", encoding="utf-8"))
                message_dict[sk_or_conn].append(data_str)
                outputs.append(sk_or_conn)
    # w_list僅僅保存了誰給我發過消息
    for conn in w_list:
        # 這裏能夠優化,使用queue優化,後面再說
        recv_str = message_dict[conn][0]
        del message_dict[conn][0]
        conn.sendall(bytes(recv_str + " 你好!", encoding="utf-8"))
        # 發完消息後刪除socket對象
        outputs.remove(conn)

    for sk_or_conn in e_list:
        inputs.remove(sk_or_conn)

# while True:
#     conn, address = sk.accept()
#     while True:
#         content_bytes = conn.recv(1024)
#         content_str = bytes(content_bytes, encoding="utf-8")
#         conn.sendall(bytes(content_str + "好", encoding="utf-8"))
#     conn.close()


# 1、概念
# 異步:某個事情須要10s完成。而我只須要調用某個函數告訴xxx來幫我作(而後我再幹其餘的事情)
# 同步:某個事情須要10s完成,我須要一直等它完成(等10s),再能繼續後面的工做。
# 阻塞:作某件事情,直到完成,除非超時
# 非阻塞:嘗試作,若是不能作,就不作(直接返回),若是能作,就作。
# 前二者和後二者不容易區分,不過前二者更多的有涉及到多線程交互(消息)的場景。
# 2、舉個例子
# 小李喝了想喝水,因而去煮開水。
# 一、小李把水壺放到爐子上,等待水燒開。(同步阻塞)
# 小李感受這樣太費時間。
# 二、小李把水壺放到爐子上,去客廳看電視,時不時去廚房看看水開沒有。(同步非阻塞)
# 小李仍是以爲本身這樣太累,因而買了把會響笛的那種水壺。水開以後,能發出聲音。
# 三、小李把響水壺放到爐子上,等待水壺發出聲音。(異步阻塞)
# 以爲這樣傻等意義不大
# 五、小李把響水壺放到爐子上,去客廳看電視,水壺響以前再也不去看它了,響了再去拿壺。(異步非阻塞)
# 這樣真好。
相關文章
相關標籤/搜索