套接字(socket)

Socket是應用層與TCP/IP協議簇通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。

套接字家族 基于文件類型 套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信

基于網絡類型 套接字家族的名字:AF_INET

(還有AF_INET6被用于ipv6,還有一些其他的地址家族,不過,他們要么是只用于某個平臺,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由于我們只關心網絡編程,所以大部分時候我么只使用AF_INET)

tcp協議、udp協議

TCP(Transmission Control Protocol)可靠的、面向連接的協議(eg:打電話)、傳輸效率低全雙工通信(發送緩存&接收緩存)、面向字節流。使用TCP的應用:Web瀏覽器;電子郵件、文件傳輸程序。

UDP(User Datagram Protocol)不可靠的、無連接的服務,傳輸效率高(發送前時延小),一對一、一對多、多對一、多對多、面向報文,盡最大努力服務,無擁塞控制。使用UDP的應用:域名系統 (DNS);視頻流;IP語音(VoIP)

套接字(socket)初始使用 基于TCP協議的socket server端

import socket sk = socket.socket() sk.bind((\\\’127.0.0.1\\\’, 8088)) sk.listen() conn, addr = sk.accept() while True: res = conn.recv(1024).decode(\\\’utf-8\\\’) if res == \\\’bye\\\’: break else: print(res) info = input(\\\’請輸入:n>>>\\\’).encode(\\\’utf-8\\\’) conn.send(info) conn.close() sk.close() client端

import socket sk= socket.socket() sk.connect((\\\’127.0.0.1\\\’, 8088)) while True: info = input(\\\’請輸入:n>>>\\\’).encode(\\\’utf-8\\\’) sk.send(info) res = sk.recv(1024).decode(\\\’utf-8\\\’) if res == \\\’bye\\\’: break else: print(res) sk.close() 對于Address already in use的情況,可以加入socket配置重用ip和端口

import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 sk.bind((\\\’127.0.0.1\\\’,8898)) #把地址綁定到套接字 sk.listen() #監聽鏈接 conn,addr = sk.accept() #接受客戶端鏈接 ret = conn.recv(1024) #接收客戶端信息 print(ret) #打印客戶端信息 conn.send(b\\\’hi\\\’) #向客戶端發送信息 conn.close() #關閉客戶端套接字 sk.close() #關閉服務器套接字(可選) 一個服務端與多個客戶端交互示例

服務端

import socket sk = socket.socket() sk.bind((\\\’127.0.0.1\\\’, 8088)) # listen([backlog])中的[backlog]參數代表服務端允許多少客戶端連接到服務端,即阻塞隊列長度,所以一共能與服務器連接的客戶端共有backlog 1個 sk.listen(3) while True: conn, addr = sk.accept() # sk.accept()放在循環內部是為了每次與同一個客戶端交互結束后會重新建立conn連接,以便下一個客戶端連入 res = conn.recv(1024).decode(\\\’utf-8\\\’) if res == \\\’bye\\\’: break else: print(\\\’收到\\\’,res) info = input(\\\’請輸入>>>\\\’).encode(\\\’utf-8\\\’) conn.send(info) conn.close() # 同上面sk.accept()的作用 sk.close()

客戶端(可以多個客戶端逐個與服務端進行通訊)

import socket sk = socket.socket() sk.connect((\\\’127.0.0.1\\\’, 8088)) while True: info = input(\\\’請輸入>>>\\\’).encode(\\\’utf-8\\\’) if info == \\\’bye\\\’: break else: sk.send(info) res = sk.recv(1024).decode(\\\’utf-8\\\’) print(res) sk.close() 基于UDP協議的socket:不可靠 無連接,效率高 server端

import socket ip_port=(\\\’127.0.0.1\\\’,9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_client.bind(ip_port) while True: msg,addr=udp_server_client.recvfrom(BUFSIZE) print(msg,addr) udp_server_client.sendto(msg.upper(),addr) client端

import socket ip_port=(\\\’127.0.0.1\\\’,9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: msg=input(\\\’>>: \\\’).strip() if not msg:continue udp_server_client.sendto(msg.encode(\\\’utf-8\\\’),ip_port) back_msg,addr=udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode(\\\’utf-8\\\’),addr) 黏包

產生原因:粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。

特點:只有TCP有粘包現象,UDP永遠不會粘包

產生黏包的兩種情況 發送數據時間間隔很短,數據了很小,會合到一起,產生粘包

服務端

from socket import * ip_port=(\\\’127.0.0.1\\\’,8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(10) data2=conn.recv(10) print(\\\’—–>\\\’,data1.decode(\\\’utf-8\\\’)) print(\\\’—–>\\\’,data2.decode(\\\’utf-8\\\’)) conn.close()

客戶端

import socket BUFSIZE=1024 ip_port=(\\\’127.0.0.1\\\’,8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send(\\\’hello\\\’.encode(\\\’utf-8\\\’)) s.send(\\\’feng\\\’.encode(\\\’utf-8\\\’)) 接收方不及時接收緩沖區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生粘包)

服務端

from socket import * ip_port=(\\\’127.0.0.1\\\’,8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(2) #一次沒有收完整 data2=conn.recv(10)#下次收的時候,會先取舊的數據,然后取新的 print(\\\’—–>\\\’,data1.decode(\\\’utf-8\\\’)) print(\\\’—–>\\\’,data2.decode(\\\’utf-8\\\’))

客戶端

import socket BUFSIZE=1024 ip_port=(\\\’127.0.0.1\\\’,8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send(\\\’hello feng\\\’.encode(\\\’utf-8\\\’)) 黏包的解決方案 原理:問題的根源在于,接收端不知道發送端將要傳送的字節流的長度,所以解決粘包的方法就是圍繞,如何讓發送端在發送數據前,把自己將要發送的字節流總大小讓接收端知曉,然后接收端來一個死循環接收完所有數據。

直接告知客戶端發送數據的長度(Server端)

import socket, subprocess sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sk.bind((\\\’localhost\\\’, 8070)) sk.listen() while True: conn, addr = sk.accept() print(\\\’客戶端:\\\’, addr) while True: msg = conn.recv(1024) if not msg: break res_temp = subprocess.Popen(msg.decode(\\\’utf-8\\\’), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) error = res_temp.stderr.read() if error: res = error else: res = res_temp.stdout.read() date_len = len(res) conn.send(str(date_len).encode(\\\’utf-8\\\’)) data = conn.recv(1024).decode(\\\’utf-8\\\’) if data == \\\’recv_ready\\\’: conn.sendall() conn.close()

直接告知客戶端發送數據的長度(Client端)

import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) res = s.connect_ex((\\\’127.0.0.1\\\’, 8080)) while True: msg = input(\\\’>>: \\\’).strip() if len(msg) == 0: continue if msg == \\\’quit\\\’: break s.send(msg.encode(\\\’utf-8\\\’)) length = int(s.recv(1024).decode(\\\’utf-8\\\’)) s.send(\\\’recv_ready\\\’.encode(\\\’utf-8\\\’)) send_size = 0 recv_size = 0 data = b\\\’\\\’ while recv_size < length: data = s.recv(1024) recv_size = len(data) print(data.decode(\\\’utf-8\\\’))

借助struct模塊打包定制報頭(Server端)

import socket, struct, json import subprocess sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加 sk.bind((\\\’127.0.0.1\\\’, 8080)) sk.listen(5) while True: conn, addr = sk.accept() while True: cmd = conn.recv(1024) if not cmd: break print(\\\’cmd: %s\\\’ % cmd) res = subprocess.Popen(cmd.decode(\\\’utf-8\\\’), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() print(err) if err: back_msg = err else: back_msg = res.stdout.read() conn.send(struct.pack(\\\’i\\\’, len(back_msg))) # 先發back_msg的長度 conn.sendall(back_msg) # 在發真實的內容 conn.close()

借助struct模塊打包定制報頭(Server端)

import socket, time, struct s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) res = s.connect_ex((\\\’127.0.0.1\\\’, 8080)) while True: msg = input(\\\’>>: \\\’).strip() if len(msg) == 0: continue if msg == \\\’quit\\\’: break s.send(msg.encode(\\\’utf-8\\\’)) l = s.recv(4) x = struct.unpack(\\\’i\\\’, l)[0] print(type(x), x) r_s = 0 data = b\\\’\\\’ while r_s < x: r_d = s.recv(1024) data = r_d r_s = len(r_d) print(data.decode(\\\’gbk\\\’)) # windows默認gbk編碼 網絡傳輸大文件的解決方案 讀取固定字節內容,避免全部讀進內存使得內存占用過大

Server端

import json import socket import struct sk = socket.socket() sk.bind((\\\’127.0.0.1\\\’,8090)) sk.listen() buffer = 1024 conn,addr = sk.accept() # 接收 head_len = conn.recv(4) head_len = struct.unpack(\\\’i\\\’,head_len)[0] json_head = conn.recv(head_len).decode(\\\’utf-8\\\’) head = json.loads(json_head) filesize = head[\\\’filesize\\\’] with open(head[\\\’filename\\\’],\\\’wb\\\’) as f: while filesize: print(filesize) if filesize >= buffer: content = conn.recv(buffer) f.write(content) filesize -= len(content) else: content = conn.recv(filesize) f.write(content) break conn.close() sk.close()

Client端

import os import json import struct import socket sk = socket.socket() sk.connect((\\\’127.0.0.1\\\’,8090)) buffer = 2048 # 發送文件 head = {\\\’filepath\\\’:r\\\’C:UsersygDesktop\\\’, \\\’filename\\\’:r\\\’02 python fullstack s9day32 基于udp的socket服務.mp4\\\’, \\\’filesize\\\’:None} file_path = os.path.join(head[\\\’filepath\\\’],head[\\\’filename\\\’]) filesize = os.path.getsize(file_path) head[\\\’filesize\\\’] = filesize json_head = json.dumps(head) # 字典轉成了字符串 bytes_head = json_head.encode(\\\’utf-8\\\’) # 字符串轉bytes # 計算head的長度 head_len = len(bytes_head) # 報頭的長度 pack_len = struct.pack(\\\’i\\\’,head_len) sk.send(pack_len) # 先發報頭的長度 sk.send(bytes_head) # 再發送bytes類型的報頭 with open(file_path,\\\’rb\\\’) as f: while filesize: print(filesize) if filesize >= buffer: content = f.read(buffer) # 每次讀出來的內容 sk.send(content) filesize -= len(content) else: content = f.read(filesize) sk.send(content) break sk.close() socket方法

服務端套接字函數

s.bind() 綁定(主機,端口號)到套接字 s.listen() 開始TCP監聽 s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來

客戶端套接字函數

s.connect() 主動初始化TCP服務器連接 s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的套接字函數

s.recv() 接收TCP數據 s.send() 發送TCP數據 s.sendall() 發送TCP數據 s.recvfrom() 接收UDP數據 s.sendto() 發送UDP數據 s.getpeername() 連接到當前套接字的遠端的地址 s.getsockname() 當前套接字的地址 s.getsockopt() 返回指定套接字的參數 s.setsockopt() 設置指定套接字的參數 s.close() 關閉套接字

面向鎖的套接字方法

s.setblocking() 設置套接字的阻塞與非阻塞模式 s.settimeout() 設置阻塞套接字操作的超時時間 s.gettimeout() 得到阻塞套接字操作的超時時間

面向文件的套接字的函數

s.fileno() 套接字的文件描述符 s.makefile() 創建一個與該套接字相關的文件 hmac模塊—客戶端鏈接合法性驗證 hmac模塊—客戶端鏈接合法性驗證

鏈接合法性驗證—舉例

服務端

# Server端 import os, socket, hmac secret_key = b\\\’secretkey\\\’ sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sk.bind((\\\’localhost\\\’, 8889)) sk.listen() def check_client(conn): send_msg = os.urandom(32) conn.send(send_msg) h = hmac.new(secret_key, send_msg) digest = h.digest() client_digest = conn.recv(1024) return hmac.compare_digest(digest, client_digest) conn, addr = sk.accept() is_legal = check_client(conn) if is_legal: print(\\\’合法客戶端!\\\’) conn.send(b\\\’legal\\\’) while True: recv_msg = conn.recv(1024).decode(\\\’utf-8\\\’) if recv_msg.lower() == \\\’q\\\’: conn.send(b\\\’q\\\’) conn.close() break else: print(recv_msg) info = input(\\\’服務端>>>\\\’).strip() if info: conn.send(info.encode(\\\’utf-8\\\’)) else: print(\\\’非法客戶端!\\\’) conn.close() sk.close()

客戶端

#Client端 import socket, hmac secret_key = b\\\’secretkey\\\’ sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sk.connect((\\\’localhost\\\’, 8889)) msg = sk.recv(1024) h = hmac.new(secret_key, msg) digest = h.digest() sk.send(digest) is_legal = sk.recv(1024) if is_legal == b\\\’legal\\\’: while True: info = input(\\\’客戶端>>>\\\’).strip() if info: sk.send(info.encode(\\\’utf-8\\\’)) recv_msg = sk.recv(1024).decode(\\\’utf-8\\\’) if recv_msg == \\\’q\\\’: break else: print(recv_msg) sk.close() 利用socketserver模塊處理服務端與多客戶端交互問題

服務端

#Server端 import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): while True: recv_msg = self.request.recv(1024).decode(\\\’utf-8\\\’) if recv_msg == \\\’q\\\’: self.request.send(b\\\’q\\\’) break else: print(recv_msg) info = input(\\\’服務端>>>\\\’).strip() if info: self.request.send(info.encode(\\\’utf-8\\\’)) if __name__ == \\\’__main__\\\’: server = socketserver.ThreadingTCPServer((\\\’localhost\\\’, 8889), MyServer) server.allow_reuse_address = True server.serve_forever()

客戶端

#Client端 import socket sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sk.connect((\\\’localhost\\\’, 8889)) while True: info = input(\\\’客戶端>>>\\\’).strip() if info == \\\’\\\’: continue elif info == \\\’q\\\’: sk.send(info.encode(\\\’utf-8\\\’)) break else: sk.send(info.encode(\\\’utf-8\\\’)) recv_msg = sk.recv(1024).decode(\\\’utf-8\\\’) print(recv_msg) sk.close()

更多關于云服務器域名注冊,虛擬主機的問題,請訪問三五互聯官網:m.shinetop.cn

贊(0)
聲明:本網站發布的內容(圖片、視頻和文字)以原創、轉載和分享網絡內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。郵箱:3140448839@qq.com。本站原創內容未經允許不得轉載,或轉載時需注明出處:三五互聯知識庫 » Python網絡編程

登錄

找回密碼

注冊

主站蜘蛛池模板: 又大又粗又爽18禁免费看| 18禁极品一区二区三区| 日韩精品毛片一区到三区| 中文字幕日韩视频欧美一区| 日韩有码中文在线观看| 老师破女学生处特级毛ooo片| 日韩一区二区黄色一级片| 永久免费AV无码网站YY| 亚洲岛国成人免费av| 国产播放91色在线观看| 国产二区三区不卡免费| 亚洲精品美女久久久久9999| 无套内射视频囯产| 深夜在线观看免费av| 色综合久久中文综合网| 一区二区三区放荡人妻| 国产亚洲精品日韩av在| 老鸭窝在钱视频| 免费看黄片一区二区三区| 91国产自拍一区二区三区| 亚洲日韩精品无码一区二区三区| 国产亚洲精久久久久久无码77777 久久66热人妻偷产精品 | 国产成人精品久久性色av| 欧美日韩精品一区二区三区高清视频| 亚洲综合精品第一页| 久久精品国产久精国产| 国产中文三级全黄| 无限看片在线版免费视频大全 | 色又黄又爽18禁免费视频| 人妻少妇精品系列一区二区| 日本免费一区二区三区久久| 欧美激情综合色综合啪啪五月| 国产二区三区不卡免费| 夜夜爽77777妓女免费看| 色狠狠色婷婷丁香五月| 97精品伊人久久久大香线蕉| 风流老熟女一区二区三区| 久久综合干| 欧美精品在线观看视频| 午夜福利国产一区二区三区| 牲欲强的熟妇农村老妇女视频|