簡單的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、舉個例子 # 小李喝了想喝水,因而去煮開水。 # 一、小李把水壺放到爐子上,等待水燒開。(同步阻塞) # 小李感受這樣太費時間。 # 二、小李把水壺放到爐子上,去客廳看電視,時不時去廚房看看水開沒有。(同步非阻塞) # 小李仍是以爲本身這樣太累,因而買了把會響笛的那種水壺。水開以後,能發出聲音。 # 三、小李把響水壺放到爐子上,等待水壺發出聲音。(異步阻塞) # 以爲這樣傻等意義不大 # 五、小李把響水壺放到爐子上,去客廳看電視,水壺響以前再也不去看它了,響了再去拿壺。(異步非阻塞) # 這樣真好。