一、c/s架構(硬件):打印機服務等html
二、c/s架構(軟件):web服務(瀏覽器爲客戶端),有客戶端的遊戲等python
三、c/s架構與socket的關係:socket就是爲了完成C/S架構的開發linux
一、網絡模型簡介:web
http://www.cnblogs.com/wangshuyang/articles/7053085.html算法
二、socket邏輯架構圖:shell
Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。因此,咱們無需深刻理解tcp/udp協議,socket已經爲咱們封裝好了,咱們只須要遵循socket的規定去編程,寫出的程序天然就是遵循tcp/udp標準的。編程
也有人將socket說成ip+port,ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啓的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序而程序的pid是同一臺機器上不一樣進程或者線程的標識json
一、套接字的發展史:windows
套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 所以,有時人們也把套接字稱爲「伯克利套接字」或「BSD 套接字」。一開始,套接字被設計用在同 一臺主機上多個應用程序之間的通信。這也被稱進程間通信,或 IPC。設計模式
二、套接字分類:
套接字有兩種(或者稱爲有兩個種族),分別是基於文件型的和基於網絡型的。
a.基於文件類型的套接字家族:套接字家族的名字:AF_UNIX
unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,能夠經過訪問同一個文件系統間接完成通訊基於網絡類型的套接字家族
b.套接字家族的名字:AF_INET
(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)
三、套接字的工做流程:
a.工做流程(服務端--〉客戶端):
服務器端先初始化Socket,而後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端鏈接。在這時若是有個客戶端初始化一個Socket,而後鏈接服務器(connect),若是鏈接成功,這時客戶端與服務器端的鏈接就創建了。客戶端發送數據請求,服務器端接收請求並處理請求,而後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉鏈接,一次交互結束。
1 import socket 2 socket.socket(socket_family,socket_type,protocal=0) 3 socket_family 能夠是 AF_UNIX 或 AF_INET。socket_type 能夠是 SOCK_STREAM 或 SOCK_DGRAM。protocol 通常不填,默認值爲 0。 4 5 獲取tcp/ip套接字 6 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 8 獲取udp/ip套接字 9 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 10 11 因爲 socket 模塊中有太多的屬性。咱們在這裏破例使用了'from module import *'語句。使用 'from socket import *',咱們就把 socket 模塊裏的全部屬性都帶到咱們的命名空間裏了,這樣能 大幅減短咱們的代碼。 12 例如tcpSock = socket(AF_INET, SOCK_STREAM)
b.python服務端套接字函數
s.bind() 綁定(主機,端口號)到套接字
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的鏈接,(阻塞式)等待鏈接的到來
c.python客戶端套接字函數
s.connect() 主動初始化TCP服務器鏈接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
d.公共用途的套接字函數
s.recv() 接收TCP數據
s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完)
s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完)
s.recvfrom() 接收UDP數據
s.sendto() 發送UDP數據
s.getpeername() 鏈接到當前套接字的遠端的地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回指定套接字的參數
s.setsockopt() 設置指定套接字的參數
s.close() 關閉套接字
c.面向鎖的套接字方法
s.setblocking() 設置套接字的阻塞與非阻塞模式
s.settimeout() 設置阻塞套接字操做的超時時間
s.gettimeout() 獲得阻塞套接字操做的超時時間
d.面向文件的套接字的函數
s.fileno() 套接字的文件描述符
s.makefile() 建立一個與該套接字相關的文件
e.python套接字流程代碼推到
1 1:用打電話的流程快速描述socket通訊 2 2:服務端和客戶端加上基於一次連接的循環通訊 3 3:客戶端發送空,卡主,證實是從哪一個位置卡的 4 服務端: 5 from socket import * 6 phone=socket(AF_INET,SOCK_STREAM) 7 phone.bind(('127.0.0.1',8081)) 8 phone.listen(5) 9 10 conn,addr=phone.accept() 11 while True: 12 data=conn.recv(1024) 13 print('server===>') 14 print(data) 15 conn.send(data.upper()) 16 conn.close() 17 phone.close() 18 客戶端: 19 from socket import * 20 21 phone=socket(AF_INET,SOCK_STREAM) 22 phone.connect(('127.0.0.1',8081)) 23 24 while True: 25 msg=input('>>: ').strip() 26 phone.send(msg.encode('utf-8')) 27 print('client====>') 28 data=phone.recv(1024) 29 print(data) 30 31 說明卡的緣由:緩衝區爲空recv就卡住,引出原理圖 32 33 34 35 4.演示客戶端斷開連接,服務端的狀況,提供解決方法 36 37 5.演示服務端不能重複接受連接,而服務器都是正常運行不斷來接受客戶連接的 38 39 6:簡單演示udp 40 服務端 41 from socket import * 42 phone=socket(AF_INET,SOCK_DGRAM) 43 phone.bind(('127.0.0.1',8082)) 44 while True: 45 msg,addr=phone.recvfrom(1024) 46 phone.sendto(msg.upper(),addr) 47 客戶端 48 from socket import * 49 phone=socket(AF_INET,SOCK_DGRAM) 50 while True: 51 msg=input('>>: ') 52 phone.sendto(msg.encode('utf-8'),('127.0.0.1',8082)) 53 msg,addr=phone.recvfrom(1024) 54 print(msg) 55 56 udp客戶端能夠併發演示 57 udp客戶端能夠輸入爲空演示,說出recvfrom與recv的區別,暫且不提tcp流和udp報的概念,留到粘包去說
一、tcp & socket & python
a.tcp服務端(邏輯)
1 ss = socket() #建立服務器套接字 2 ss.bind() #把地址綁定到套接字 3 ss.listen() #監聽連接 4 inf_loop: #服務器無限循環 5 cs = ss.accept() #接受客戶端連接 6 comm_loop: #通信循環 7 cs.recv()/cs.send() #對話(接收與發送) 8 cs.close() #關閉客戶端套接字 9 ss.close() #關閉服務器套接字(可選)
b.tcp客戶端(邏輯)
1 cs = socket() # 建立客戶套接字 2 cs.connect() # 嘗試鏈接服務器 3 comm_loop: # 通信循環 4 cs.send()/cs.recv() # 對話(發送/接收) 5 cs.close() # 關閉客戶套接字
c.python & tcp 服務端
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 import socket 4 ip_port=('127.0.0.1',8081)#電話卡 5 BUFSIZE=1024 6 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 7 s.bind(ip_port) #手機插卡 8 s.listen(5) #手機待機 9 10 11 while True: #新增接收連接循環,能夠不停的接電話 12 conn,addr=s.accept() #手機接電話 13 # print(conn) 14 # print(addr) 15 print('接到來自%s的電話' %addr[0]) 16 while True: #新增通訊循環,能夠不斷的通訊,收發消息 17 msg=conn.recv(BUFSIZE) #聽消息,聽話 18 19 # if len(msg) == 0:break #若是不加,那麼正在連接的客戶端忽然斷開,recv便再也不阻塞,死循環發生 20 21 print(msg,type(msg)) 22 23 conn.send(msg.upper()) #發消息,說話 24 25 conn.close() #掛電話 26 27 s.close() #手機關機 28 29 服務端改進版
d.python & tcp 客戶端
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 import socket 4 ip_port=('127.0.0.1',8081) 5 BUFSIZE=1024 6 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 7 8 s.connect_ex(ip_port) #撥電話 9 10 while True: #新增通訊循環,客戶端能夠不斷髮收消息 11 msg=input('>>: ').strip() 12 if len(msg) == 0:continue 13 s.send(msg.encode('utf-8')) #發消息,說話(只能發送字節類型) 14 15 feedback=s.recv(BUFSIZE) #收消息,聽話 16 print(feedback.decode('utf-8')) 17 18 s.close() #掛電話 19 20 客戶端改進版
ps.可能遇到的問題:
在重啓服務端時可能會遇到端口占用,這個是因爲你的服務端仍然存在四次揮手的time_wait狀態在佔用地址(若是不懂,請深刻研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高併發狀況下會有大量的time_wait狀態的優化方法)
1 #加入一條socket配置,重用ip和端口 2 3 phone=socket(AF_INET,SOCK_STREAM) 4 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 5 phone.bind(('127.0.0.1',8080))
二、udp & socket & python
a.udp服務端(邏輯)
1 ss = socket() #建立一個服務器的套接字 2 ss.bind() #綁定服務器套接字 3 inf_loop: #服務器無限循環 4 cs = ss.recvfrom()/ss.sendto() # 對話(接收與發送) 5 ss.close() # 關閉服務器套接字
b.udp客戶端(邏輯)
1 cs = socket() # 建立客戶套接字 2 comm_loop: # 通信循環 3 cs.sendto()/cs.recvfrom() # 對話(發送/接收) 4 cs.close() # 關閉客戶套接字
c.udp簡單示例:
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 import socket 4 ip_port=('127.0.0.1',9000) 5 BUFSIZE=1024 6 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 7 8 udp_server_client.bind(ip_port) 9 10 while True: 11 msg,addr=udp_server_client.recvfrom(BUFSIZE) 12 print(msg,addr) 13 14 udp_server_client.sendto(msg.upper(),addr)
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 import socket 4 ip_port=('127.0.0.1',9000) 5 BUFSIZE=1024 6 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 7 8 while True: 9 msg=input('>>: ').strip() 10 if not msg:continue 11 12 udp_server_client.sendto(msg.encode('utf-8'),ip_port) 13 14 back_msg,addr=udp_server_client.recvfrom(BUFSIZE) 15 print(back_msg.decode('utf-8'),addr)
d.qq聊天示例(因爲udp無鏈接,因此能夠同時多個客戶端去跟服務端通訊)
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 import socket 4 ip_port=('127.0.0.1',8081) 5 udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #買手機 6 udp_server_sock.bind(ip_port) 7 8 while True: 9 qq_msg,addr=udp_server_sock.recvfrom(1024) 10 print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8'))) 11 back_msg=input('回覆消息: ').strip() 12 13 udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
1 import socket 2 BUFSIZE=1024 3 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 4 5 qq_name_dic={ 6 '狗哥':('127.0.0.1',8081), 7 'B哥':('127.0.0.1',8081), 8 '一棵樹':('127.0.0.1',8081), 9 '武大郎':('127.0.0.1',8081), 10 } 11 12 13 while True: 14 qq_name=input('請選擇聊天對象: ').strip() 15 while True: 16 msg=input('請輸入消息,回車發送: ').strip() 17 if msg == 'quit':break 18 if not msg or not qq_name or qq_name not in qq_name_dic:continue 19 udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name]) 20 21 back_msg,addr=udp_client_socket.recvfrom(BUFSIZE) 22 print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8'))) 23 24 udp_client_socket.close()
1 import socket 2 BUFSIZE=1024 3 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 4 5 qq_name_dic={ 6 '狗哥':('127.0.0.1',8081), 7 'B哥':('127.0.0.1',8081), 8 '一棵樹':('127.0.0.1',8081), 9 '武大郎':('127.0.0.1',8081), 10 } 11 12 13 while True: 14 qq_name=input('請選擇聊天對象: ').strip() 15 while True: 16 msg=input('請輸入消息,回車發送: ').strip() 17 if msg == 'quit':break 18 if not msg or not qq_name or qq_name not in qq_name_dic:continue 19 udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name]) 20 21 back_msg,addr=udp_client_socket.recvfrom(BUFSIZE) 22 print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8'))) 23 24 udp_client_socket.close()
e.時間服務器:
1 from socket import * 2 from time import strftime 3 4 ip_port=('127.0.0.1',9000) 5 bufsize=1024 6 7 tcp_server=socket(AF_INET,SOCK_DGRAM) 8 tcp_server.bind(ip_port) 9 10 while True: 11 msg,addr=tcp_server.recvfrom(bufsize) 12 print('===>',msg) 13 14 if not msg: 15 time_fmt='%Y-%m-%d %X' 16 else: 17 time_fmt=msg.decode('utf-8') 18 back_msg=strftime(time_fmt) 19 20 tcp_server.sendto(back_msg.encode('utf-8'),addr) 21 22 tcp_server.close()
1 from socket import * 2 ip_port=('127.0.0.1',9000) 3 bufsize=1024 4 5 tcp_client=socket(AF_INET,SOCK_DGRAM) 6 7 8 9 while True: 10 msg=input('請輸入時間格式(例%Y %m %d)>>: ').strip() 11 tcp_client.sendto(msg.encode('utf-8'),ip_port) 12 13 data=tcp_client.recv(bufsize) 14 15 print(data.decode('utf-8')) 16 17 tcp_client.close()
一、收發消息的原理:發消息,都是將數據發送到己端的發送緩衝中,收消息都是從己端的緩衝區中收。
a.send發消息,recv收消息
b.sendto發消息,recvfrom收消息
二、send與sendinto
tcp是基於數據流的,而udp是基於數據報的:
a.send(bytes_data):發送數據流,數據流bytes_data若爲空,本身這段的緩衝區也爲空,操做系統不會控制tcp協議發空包
b.sendinto(bytes_data,ip_port):發送數據報,bytes_data爲空,還有ip_port,全部即使是發送空的bytes_data,數據報其實也不是空的,本身這端的緩衝區收到內容,操做系統就會控制udp協議發包。
三、recv與recvfrom
a.tcp協議:
1)若是收消息緩衝區裏的數據爲空,那麼recv就會阻塞(阻塞很簡單,就是一直在等着收)
2)只不過tcp協議的客戶端send一個空數據就是真的空數據,客戶端即便有無窮個send空,也跟沒有一個樣。
3)tcp基於連接通訊
客戶端發送爲空,測試結果--->驗證:(1)
客戶端直接終止程序,測試結果--->驗證:(2)
1 mport subprocess 2 from socket import * 3 4 phone=socket(AF_INET,SOCK_STREAM) 5 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) 6 phone.bind(('127.0.0.1',8080)) 7 phone.listen(5) 8 9 conn,addr=phone.accept() 10 11 while True: 12 data=conn.recv(1024) 13 print('from client msg is ',data) 14 conn.send(data.upper())
1 import subprocess 2 from socket import * 3 4 phone=socket(AF_INET,SOCK_STREAM) 5 phone.connect(('127.0.0.1',8080)) 6 7 8 while True: 9 msg=input('>>: ') 10 phone.send(msg.encode('utf-8')) 11 print('Client message has been sent') 12 13 data=phone.recv(1024) 14 print('from server msg is ',data.decode('utf-8')) 15 phone.close()
b.udp協議:
1)若是若是收消息緩衝區裏的數據爲「空」,recvfrom也會阻塞
2)只不過udp協議的客戶端sendinto一個空數據並非真的空數據(包含:空數據+地址信息,獲得的報仍然不會爲空),因此客戶端只要有一個sendinto(無論是否發送空數據,都不是真的空數據),服務端就能夠recvfrom到數據。
3)udp無連接
客戶端發送空,看服務端結果--->驗證(1)
1 __author__ = 'Linhaifeng' 2 from socket import * 3 4 ip_port=('127.0.0.1',9003) 5 bufsize=1024 6 7 udp_server=socket(AF_INET,SOCK_DGRAM) 8 udp_server.bind(ip_port) 9 10 while True: 11 data1,addr=udp_server.recvfrom(bufsize) 12 print(data1)
1 from socket import * 2 ip_port=('127.0.0.1',9003) 3 bufsize=1024 4 5 udp_client=socket(AF_INET,SOCK_DGRAM) 6 7 while True: 8 msg=input('>>: ') 9 udp_client.sendto(msg.encode('utf-8'),ip_port) #發送空,發現服務端能夠接收空
分別運行服務端,客戶端--->驗證(2)
1 from socket import * 2 3 ip_port=('127.0.0.1',9003) 4 bufsize=1024 5 6 udp_server=socket(AF_INET,SOCK_DGRAM) 7 udp_server.bind(ip_port) 8 9 data1,addr=udp_server.recvfrom(1) 10 print('第一次收了 ',data1) 11 data2,addr=udp_server.recvfrom(1) 12 print('第二次收了 ',data2) 13 data3,addr=udp_server.recvfrom(1) 14 print('第三次收了 ',data3) 15 print('--------結束----------')
1 from socket import * 2 ip_port=('127.0.0.1',9003) 3 bufsize=1024 4 5 udp_client=socket(AF_INET,SOCK_DGRAM) 6 7 udp_client.sendto(b'hello',ip_port) 8 udp_client.sendto(b'world',ip_port) 9 udp_client.sendto(b'egon',ip_port)
不運行服務端,單獨運行客戶端,一點問題沒有,可是消息丟了--->驗證(3)
1 from socket import * 2 3 ip_port=('127.0.0.1',9003) 4 bufsize=1024 5 6 udp_server=socket(AF_INET,SOCK_DGRAM) 7 udp_server.bind(ip_port) 8 9 data1,addr=udp_server.recvfrom(bufsize) 10 print('第一次收了 ',data1) 11 data2,addr=udp_server.recvfrom(bufsize) 12 print('第二次收了 ',data2) 13 data3,addr=udp_server.recvfrom(bufsize) 14 print('第三次收了 ',data3) 15 print('--------結束----------')
1 from socket import * 2 import time 3 ip_port=('127.0.0.1',9003) 4 bufsize=1024 5 6 udp_client=socket(AF_INET,SOCK_DGRAM) 7 8 udp_client.sendto(b'hello',ip_port) 9 udp_client.sendto(b'world',ip_port) 10 udp_client.sendto(b'egon',ip_port) 11 12 print('客戶端發完消息啦') 13 time.sleep(100)
ps:
你單獨運行上面的udp的客戶端,你發現並不會報錯,相反tcp卻會報錯,由於udp協議只負責把包發出去,對方收不收,我根本無論,而tcp是基於連接的,必須有一個服務端先運行着,客戶端去跟服務端創建連接而後依託於連接才能傳遞消息,任何一方試圖把連接摧毀都會致使對方程序的崩潰。
上面的udp程序,你註釋任何一條客戶端的sendinto,服務端都會卡住,爲何?由於服務端有幾個recvfrom就要對應幾個sendinto,哪怕是sendinto(b'')那也要有。
一、粘包現象:能夠分爲服務端粘包,和客戶端粘包,緣由爲雙方都不知道發送多少字節的數據,接收應接收多少字節的數據。upd沒有粘包的現象,由於他會丟數據。
發送端能夠是一K一K地發送數據,而接收端的應用程序能夠兩K兩K地提走數據,固然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據,也就是說,應用程序所看到的數據是一個總體,或說是一個流(stream),一條消息有多少字節對應用程序是不可見的,所以TCP協議是面向流的協議,這也是容易出現粘包問題的緣由。而UDP是面向消息的協議,每一個UDP段都是一條消息,應用程序必須以消息爲單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不一樣的。怎樣定義消息呢?能夠認爲對方一次性write/send的數據爲一個消息,須要明白的是當對方send一條信息的時候,不管底層怎樣分段分片,TCP協議層會把構成整條消息的數據段排序完成後才呈如今內核緩衝區。
例如基於tcp的套接字客戶端往服務端上傳文件,發送時文件內容是按照一段一段的字節流發送的,在接收方看了,根本不知道該文件的字節流從何處開始,在何處結束。所謂粘包問題主要仍是由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的。
此外,發送方引發的粘包是由TCP協議自己形成的,TCP爲提升傳輸效率,發送方每每要收集到足夠多的數據後才發送一個TCP段。若連續幾回須要send的數據都不多,一般TCP會根據優化算法把這些數據合成一個TCP段後一次發送出去,這樣接收方就收到了粘包數據。
tcp的協議數據不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端老是在收到ack時纔會清除緩衝區內容。數據是可靠的,可是會粘包。
兩種狀況下會發生粘包:
a.發送端須要等緩衝區滿才發送出去,形成粘包(發送數據時間間隔很短,數據了很小,會合到一塊兒,產生粘包)
1 from socket import * 2 ip_port=('127.0.0.1',8080) 3 4 tcp_socket_server=socket(AF_INET,SOCK_STREAM) 5 tcp_socket_server.bind(ip_port) 6 tcp_socket_server.listen(5) 7 8 9 conn,addr=tcp_socket_server.accept() 10 11 12 data1=conn.recv(10) 13 data2=conn.recv(10) 14 15 print('----->',data1.decode('utf-8')) 16 print('----->',data2.decode('utf-8')) 17 18 conn.close()
1 import socket 2 BUFSIZE=1024 3 ip_port=('127.0.0.1',8080) 4 5 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 6 res=s.connect_ex(ip_port) 7 8 9 s.send('hello'.encode('utf-8')) 10 s.send('feng'.encode('utf-8'))
b.接收方不及時接收緩衝區的包,形成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候仍是從緩衝區拿上次遺留的數據,產生粘包)
1 from socket import * 2 ip_port=('127.0.0.1',8080) 3 4 tcp_socket_server=socket(AF_INET,SOCK_STREAM) 5 tcp_socket_server.bind(ip_port) 6 tcp_socket_server.listen(5) 7 8 9 conn,addr=tcp_socket_server.accept() 10 11 12 data1=conn.recv(2) #一次沒有收完整 13 data2=conn.recv(10)#下次收的時候,會先取舊的數據,而後取新的 14 15 print('----->',data1.decode('utf-8')) 16 print('----->',data2.decode('utf-8')) 17 18 conn.close()
1 import socket 2 BUFSIZE=1024 3 ip_port=('127.0.0.1',8080) 4 5 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 6 res=s.connect_ex(ip_port) 7 8 9 s.send('hello feng'.encode('utf-8'))
二、拆包狀況(粘包詳解):
發送端緩衝區的長度大於網卡的MTU時,tcp會將此次發送的數據拆成幾個數據包發送出去。
a.爲什麼tcp是可靠傳輸,udp是不可靠傳輸
tcp在數據傳輸時,發送端先把數據發送到本身的緩存中,而後協議控制將緩存中的數據發往對端,對端返回一個ack=1,發送端則清理緩存中的數據,對端返回ack=0,則從新發送數據,因此tcp是可靠的
而udp發送數據,對端是不會返回確認信息的,所以不可靠
b.send(字節流)和recv(1024)及sendall
recv裏指定的1024意思是從緩存裏一次拿出1024個字節的數據。send的字節流是先放入己端緩存,而後由協議控制將緩存內容發往對端,若是待發送的字節流大小大於緩存剩餘空間,那麼數據丟失,用sendall就會循環調用send,數據不會丟失
三、粘包實例:
讓咱們基於tcp先製做一個遠程執行命令的程序(1:執行錯誤命令 2:執行ls 3:執行ifconfig)。
res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
的結果的編碼是以當前所在的系統爲準的,若是是windows,那麼res.stdout.read()讀出的就是GBK編碼的,在接收端需要用GBK解碼且只能從管道里讀一次結果。
命令ls -l ; lllllll ; pwd 的結果是既有正確stdout結果,又有錯誤stderr結果
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 from socket import * 4 import subprocess 5 6 ip_port=('127.0.0.1',8080) 7 BUFSIZE=1024 8 9 tcp_socket_server=socket(AF_INET,SOCK_STREAM) 10 tcp_socket_server.bind(ip_port) 11 tcp_socket_server.listen(5) 12 13 while True: 14 conn,addr=tcp_socket_server.accept() 15 print('客戶端',addr) 16 17 while True: 18 cmd=conn.recv(BUFSIZE) 19 if len(cmd) == 0:break 20 21 res=subprocess.Popen(cmd.decode('utf-8'),shell=True, 22 stdout=subprocess.PIPE, 23 stdin=subprocess.PIPE, 24 stderr=subprocess.PIPE) 25 26 stderr=act_res.stderr.read() 27 stdout=act_res.stdout.read() 28 conn.send(stderr) 29 conn.send(stdout)
1 import socket 2 BUFSIZE=1024 3 ip_port=('127.0.0.1',8080) 4 5 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 6 res=s.connect_ex(ip_port) 7 8 while True: 9 msg=input('>>: ').strip() 10 if len(msg) == 0:continue 11 if msg == 'quit':break 12 13 s.send(msg.encode('utf-8')) 14 act_res=s.recv(BUFSIZE) 15 16 print(act_res.decode('utf-8'),end='')
上述程序是基於tcp的socket,在運行時會發生粘包
ps.udp協議:
1 from socket import * 2 import subprocess 3 4 ip_port=('127.0.0.1',9003) 5 bufsize=1024 6 7 udp_server=socket(AF_INET,SOCK_DGRAM) 8 udp_server.bind(ip_port) 9 10 while True: 11 #收消息 12 cmd,addr=udp_server.recvfrom(bufsize) 13 print('用戶命令----->',cmd) 14 15 #邏輯處理 16 res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdin=subprocess.PIPE,stdout=subprocess.PIPE) 17 stderr=res.stderr.read() 18 stdout=res.stdout.read() 19 20 #發消息 21 udp_server.sendto(stderr,addr) 22 udp_server.sendto(stdout,addr) 23 udp_server.close()
1 from socket import * 2 ip_port=('127.0.0.1',9003) 3 bufsize=1024 4 5 udp_client=socket(AF_INET,SOCK_DGRAM) 6 7 8 while True: 9 msg=input('>>: ').strip() 10 udp_client.sendto(msg.encode('utf-8'),ip_port) 11 12 data,addr=udp_client.recvfrom(bufsize) 13 print(data.decode('utf-8'),end='')
四、解決粘包
爲字節流加上自定義固定長度報頭,報頭中包含字節流長度,而後一次send到對端,對端在接收時,先從緩存中取出定長的報頭,而後再取真實數據。
struct模塊
該模塊能夠把一個類型,如數字,轉成固定長度的bytes
a.struct詳細用法:
1 import struct 2 import binascii 3 import ctypes 4 5 values1 = (1, 'abc'.encode('utf-8'), 2.7) 6 values2 = ('defg'.encode('utf-8'),101) 7 s1 = struct.Struct('I3sf') 8 s2 = struct.Struct('4sI') 9 10 print(s1.size,s2.size) 11 prebuffer=ctypes.create_string_buffer(s1.size+s2.size) 12 print('Before : ',binascii.hexlify(prebuffer)) 13 # t=binascii.hexlify('asdfaf'.encode('utf-8')) 14 # print(t) 15 16 17 s1.pack_into(prebuffer,0,*values1) 18 s2.pack_into(prebuffer,s1.size,*values2) 19 20 print('After pack',binascii.hexlify(prebuffer)) 21 print(s1.unpack_from(prebuffer,0)) 22 print(s2.unpack_from(prebuffer,s1.size)) 23 24 s3=struct.Struct('ii') 25 s3.pack_into(prebuffer,0,123,123) 26 print('After pack',binascii.hexlify(prebuffer)) 27 print(s3.unpack_from(prebuffer,0))
b.使用原理
1 import json,struct 2 #假設經過客戶端上傳1T:1073741824000的文件a.txt 3 4 #爲避免粘包,必須自定製報頭 5 header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T數據,文件路徑和md5值 6 7 #爲了該報頭能傳送,須要序列化而且轉爲bytes 8 head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化並轉成bytes,用於傳輸 9 10 #爲了讓客戶端知道報頭的長度,用struck將報頭長度這個數字轉成固定長度:4個字節 11 head_len_bytes=struct.pack('i',len(head_bytes)) #這4個字節裏只包含了一個數字,該數字是報頭的長度 12 13 #客戶端開始發送 14 conn.send(head_len_bytes) #先發報頭的長度,4個bytes 15 conn.send(head_bytes) #再發報頭的字節格式 16 conn.sendall(文件內容) #而後發真實內容的字節格式 17 18 #服務端開始接收 19 head_len_bytes=s.recv(4) #先收報頭4個bytes,獲得報頭長度的字節格式 20 x=struct.unpack('i',head_len_bytes)[0] #提取報頭的長度 21 22 head_bytes=s.recv(x) #按照報頭長度x,收取報頭的bytes格式 23 header=json.loads(json.dumps(header)) #提取報頭 24 25 #最後根據報頭的內容提取真實的數據,好比 26 real_data_len=s.recv(header['file_size']) 27 s.recv(real_data_len)
c.自定義報頭
1 import socket,struct,json 2 import subprocess 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 5 6 phone.bind(('127.0.0.1',8080)) 7 8 phone.listen(5) 9 10 while True: 11 conn,addr=phone.accept() 12 while True: 13 cmd=conn.recv(1024) 14 if not cmd:break 15 print('cmd: %s' %cmd) 16 17 res=subprocess.Popen(cmd.decode('utf-8'), 18 shell=True, 19 stdout=subprocess.PIPE, 20 stderr=subprocess.PIPE) 21 err=res.stderr.read() 22 print(err) 23 if err: 24 back_msg=err 25 else: 26 back_msg=res.stdout.read() 27 28 29 conn.send(struct.pack('i',len(back_msg))) #先發back_msg的長度 30 conn.sendall(back_msg) #在發真實的內容 31 32 conn.close()
1 import socket,time,struct 2 3 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 res=s.connect_ex(('127.0.0.1',8080)) 5 6 while True: 7 msg=input('>>: ').strip() 8 if len(msg) == 0:continue 9 if msg == 'quit':break 10 11 s.send(msg.encode('utf-8')) 12 13 14 15 l=s.recv(4) 16 x=struct.unpack('i',l)[0] 17 print(type(x),x) 18 # print(struct.unpack('I',l)) 19 r_s=0 20 data=b'' 21 while r_s < x: 22 r_d=s.recv(1024) 23 data+=r_d 24 r_s+=len(r_d) 25 26 # print(data.decode('utf-8')) 27 print(data.decode('gbk')) #windows默認gbk編碼
d.使用實例:
1 import socket,struct,json 2 import subprocess 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 5 6 phone.bind(('127.0.0.1',8080)) 7 8 phone.listen(5) 9 10 while True: 11 conn,addr=phone.accept() 12 while True: 13 cmd=conn.recv(1024) 14 if not cmd:break 15 print('cmd: %s' %cmd) 16 17 res=subprocess.Popen(cmd.decode('utf-8'), 18 shell=True, 19 stdout=subprocess.PIPE, 20 stderr=subprocess.PIPE) 21 err=res.stderr.read() 22 print(err) 23 if err: 24 back_msg=err 25 else: 26 back_msg=res.stdout.read() 27 28 headers={'data_size':len(back_msg)} 29 head_json=json.dumps(headers) 30 head_json_bytes=bytes(head_json,encoding='utf-8') 31 32 conn.send(struct.pack('i',len(head_json_bytes))) #先發報頭的長度 33 conn.send(head_json_bytes) #再發報頭 34 conn.sendall(back_msg) #在發真實的內容 35 36 conn.close()
1 from socket import * 2 import struct,json 3 4 ip_port=('127.0.0.1',8080) 5 client=socket(AF_INET,SOCK_STREAM) 6 client.connect(ip_port) 7 8 while True: 9 cmd=input('>>: ') 10 if not cmd:continue 11 client.send(bytes(cmd,encoding='utf-8')) 12 13 head=client.recv(4) 14 head_json_len=struct.unpack('i',head)[0] 15 head_json=json.loads(client.recv(head_json_len).decode('utf-8')) 16 data_len=head_json['data_size'] 17 18 recv_size=0 19 recv_data=b'' 20 while recv_size < data_len: 21 recv_data+=client.recv(1024) 22 recv_size+=len(recv_data) 23 24 print(recv_data.decode('utf-8')) 25 #print(recv_data.decode('gbk')) #windows默認gbk編碼
e.ftp上傳下載
1 import socket 2 import struct 3 import json 4 import subprocess 5 import os 6 7 class MYTCPServer: 8 address_family = socket.AF_INET 9 10 socket_type = socket.SOCK_STREAM 11 12 allow_reuse_address = False 13 14 max_packet_size = 8192 15 16 coding='utf-8' 17 18 request_queue_size = 5 19 20 server_dir='file_upload' 21 22 def __init__(self, server_address, bind_and_activate=True): 23 """Constructor. May be extended, do not override.""" 24 self.server_address=server_address 25 self.socket = socket.socket(self.address_family, 26 self.socket_type) 27 if bind_and_activate: 28 try: 29 self.server_bind() 30 self.server_activate() 31 except: 32 self.server_close() 33 raise 34 35 def server_bind(self): 36 """Called by constructor to bind the socket. 37 """ 38 if self.allow_reuse_address: 39 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 40 self.socket.bind(self.server_address) 41 self.server_address = self.socket.getsockname() 42 43 def server_activate(self): 44 """Called by constructor to activate the server. 45 """ 46 self.socket.listen(self.request_queue_size) 47 48 def server_close(self): 49 """Called to clean-up the server. 50 """ 51 self.socket.close() 52 53 def get_request(self): 54 """Get the request and client address from the socket. 55 """ 56 return self.socket.accept() 57 58 def close_request(self, request): 59 """Called to clean up an individual request.""" 60 request.close() 61 62 def run(self): 63 while True: 64 self.conn,self.client_addr=self.get_request() 65 print('from client ',self.client_addr) 66 while True: 67 try: 68 head_struct = self.conn.recv(4) 69 if not head_struct:break 70 71 head_len = struct.unpack('i', head_struct)[0] 72 head_json = self.conn.recv(head_len).decode(self.coding) 73 head_dic = json.loads(head_json) 74 75 print(head_dic) 76 #head_dic={'cmd':'put','filename':'a.txt','filesize':123123} 77 cmd=head_dic['cmd'] 78 if hasattr(self,cmd): 79 func=getattr(self,cmd) 80 func(head_dic) 81 except Exception: 82 break 83 84 def put(self,args): 85 file_path=os.path.normpath(os.path.join( 86 self.server_dir, 87 args['filename'] 88 )) 89 90 filesize=args['filesize'] 91 recv_size=0 92 print('----->',file_path) 93 with open(file_path,'wb') as f: 94 while recv_size < filesize: 95 recv_data=self.conn.recv(self.max_packet_size) 96 f.write(recv_data) 97 recv_size+=len(recv_data) 98 print('recvsize:%s filesize:%s' %(recv_size,filesize)) 99 100 101 tcpserver1=MYTCPServer(('127.0.0.1',8080)) 102 103 tcpserver1.run()
1 import socket 2 import struct 3 import json 4 import os 5 6 7 8 class MYTCPClient: 9 address_family = socket.AF_INET 10 11 socket_type = socket.SOCK_STREAM 12 13 allow_reuse_address = False 14 15 max_packet_size = 8192 16 17 coding='utf-8' 18 19 request_queue_size = 5 20 21 def __init__(self, server_address, connect=True): 22 self.server_address=server_address 23 self.socket = socket.socket(self.address_family, 24 self.socket_type) 25 if connect: 26 try: 27 self.client_connect() 28 except: 29 self.client_close() 30 raise 31 32 def client_connect(self): 33 self.socket.connect(self.server_address) 34 35 def client_close(self): 36 self.socket.close() 37 38 def run(self): 39 while True: 40 inp=input(">>: ").strip() 41 if not inp:continue 42 l=inp.split() 43 cmd=l[0] 44 if hasattr(self,cmd): 45 func=getattr(self,cmd) 46 func(l) 47 48 49 def put(self,args): 50 cmd=args[0] 51 filename=args[1] 52 if not os.path.isfile(filename): 53 print('file:%s is not exists' %filename) 54 return 55 else: 56 filesize=os.path.getsize(filename) 57 58 head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize} 59 print(head_dic) 60 head_json=json.dumps(head_dic) 61 head_json_bytes=bytes(head_json,encoding=self.coding) 62 63 head_struct=struct.pack('i',len(head_json_bytes)) 64 self.socket.send(head_struct) 65 self.socket.send(head_json_bytes) 66 send_size=0 67 with open(filename,'rb') as f: 68 for line in f: 69 self.socket.send(line) 70 send_size+=len(line) 71 print(send_size) 72 else: 73 print('upload successful') 74 75 76 77 78 client=MYTCPClient(('127.0.0.1',8080)) 79 80 client.run()
若是你想在分佈式系統中實現一個簡單的客戶端連接認證功能,又不像SSL那麼複雜,那麼利用hmac+加鹽的方式來實現。
1 from socket import * 2 import hmac,os 3 4 secret_key=b'linhaifeng bang bang bang' 5 def conn_auth(conn): 6 ''' 7 認證客戶端連接 8 :param conn: 9 :return: 10 ''' 11 print('開始驗證新連接的合法性') 12 msg=os.urandom(32) 13 conn.sendall(msg) 14 h=hmac.new(secret_key,msg) 15 digest=h.digest() 16 respone=conn.recv(len(digest)) 17 return hmac.compare_digest(respone,digest) 18 19 def data_handler(conn,bufsize=1024): 20 if not conn_auth(conn): 21 print('該連接不合法,關閉') 22 conn.close() 23 return 24 print('連接合法,開始通訊') 25 while True: 26 data=conn.recv(bufsize) 27 if not data:break 28 conn.sendall(data.upper()) 29 30 def server_handler(ip_port,bufsize,backlog=5): 31 ''' 32 只處理連接 33 :param ip_port: 34 :return: 35 ''' 36 tcp_socket_server=socket(AF_INET,SOCK_STREAM) 37 tcp_socket_server.bind(ip_port) 38 tcp_socket_server.listen(backlog) 39 while True: 40 conn,addr=tcp_socket_server.accept() 41 print('新鏈接[%s:%s]' %(addr[0],addr[1])) 42 data_handler(conn,bufsize) 43 44 if __name__ == '__main__': 45 ip_port=('127.0.0.1',9999) 46 bufsize=1024 47 server_handler(ip_port,bufsize)
1 from socket import * 2 import hmac,os 3 4 secret_key=b'linhaifeng bang bang bang' 5 def conn_auth(conn): 6 ''' 7 驗證客戶端到服務器的連接 8 :param conn: 9 :return: 10 ''' 11 msg=conn.recv(32) 12 h=hmac.new(secret_key,msg) 13 digest=h.digest() 14 conn.sendall(digest) 15 16 def client_handler(ip_port,bufsize=1024): 17 tcp_socket_client=socket(AF_INET,SOCK_STREAM) 18 tcp_socket_client.connect(ip_port) 19 20 conn_auth(tcp_socket_client) 21 22 while True: 23 data=input('>>: ').strip() 24 if not data:continue 25 if data == 'quit':break 26 27 tcp_socket_client.sendall(data.encode('utf-8')) 28 respone=tcp_socket_client.recv(bufsize) 29 print(respone.decode('utf-8')) 30 tcp_socket_client.close() 31 32 if __name__ == '__main__': 33 ip_port=('127.0.0.1',9999) 34 bufsize=1024 35 client_handler(ip_port,bufsize)
1 from socket import * 2 3 def client_handler(ip_port,bufsize=1024): 4 tcp_socket_client=socket(AF_INET,SOCK_STREAM) 5 tcp_socket_client.connect(ip_port) 6 7 while True: 8 data=input('>>: ').strip() 9 if not data:continue 10 if data == 'quit':break 11 12 tcp_socket_client.sendall(data.encode('utf-8')) 13 respone=tcp_socket_client.recv(bufsize) 14 print(respone.decode('utf-8')) 15 tcp_socket_client.close() 16 17 if __name__ == '__main__': 18 ip_port=('127.0.0.1',9999) 19 bufsize=1024 20 client_handler(ip_port,bufsize)
1 from socket import * 2 import hmac,os 3 4 secret_key=b'linhaifeng bang bang bang1111' 5 def conn_auth(conn): 6 ''' 7 驗證客戶端到服務器的連接 8 :param conn: 9 :return: 10 ''' 11 msg=conn.recv(32) 12 h=hmac.new(secret_key,msg) 13 digest=h.digest() 14 conn.sendall(digest) 15 16 def client_handler(ip_port,bufsize=1024): 17 tcp_socket_client=socket(AF_INET,SOCK_STREAM) 18 tcp_socket_client.connect(ip_port) 19 20 conn_auth(tcp_socket_client) 21 22 while True: 23 data=input('>>: ').strip() 24 if not data:continue 25 if data == 'quit':break 26 27 tcp_socket_client.sendall(data.encode('utf-8')) 28 respone=tcp_socket_client.recv(bufsize) 29 print(respone.decode('utf-8')) 30 tcp_socket_client.close() 31 32 if __name__ == '__main__': 33 ip_port=('127.0.0.1',9999) 34 bufsize=1024 35 client_handler(ip_port,bufsize)
基於tcp的套接字,關鍵就是兩個循環,一個連接循環,一個通訊循環
socketserver模塊中分兩大類:server類(解決連接問題)和request類(解決通訊問題)
server類:
request類:
繼承關係:
如下述代碼爲例,分析socketserver源碼:
ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
ftpserver.serve_forever()
查找屬性的順序:ThreadingTCPServer->ThreadingMixIn->TCPServer->BaseServer
源碼分析總結:
基於tcp的socketserver咱們本身定義的類中的
a.self.server即套接字對象
b.self.request即一個連接
c.self.client_address即客戶端地址
基於udp的socketserver咱們本身定義的類中的
a.self.request是一個元組(第一個元素是客戶端發來的數據,第二部分是服務端的udp套接字對象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
b.self.client_address即客戶端地址
ftp示例代碼:
1 import socketserver 2 import struct 3 import json 4 import os 5 class FtpServer(socketserver.BaseRequestHandler): 6 coding='utf-8' 7 server_dir='file_upload' 8 max_packet_size=1024 9 BASE_DIR=os.path.dirname(os.path.abspath(__file__)) 10 def handle(self): 11 print(self.request) 12 while True: 13 data=self.request.recv(4) 14 data_len=struct.unpack('i',data)[0] 15 head_json=self.request.recv(data_len).decode(self.coding) 16 head_dic=json.loads(head_json) 17 # print(head_dic) 18 cmd=head_dic['cmd'] 19 if hasattr(self,cmd): 20 func=getattr(self,cmd) 21 func(head_dic) 22 def put(self,args): 23 file_path = os.path.normpath(os.path.join( 24 self.BASE_DIR, 25 self.server_dir, 26 args['filename'] 27 )) 28 29 filesize = args['filesize'] 30 recv_size = 0 31 print('----->', file_path) 32 with open(file_path, 'wb') as f: 33 while recv_size < filesize: 34 recv_data = self.request.recv(self.max_packet_size) 35 f.write(recv_data) 36 recv_size += len(recv_data) 37 print('recvsize:%s filesize:%s' % (recv_size, filesize)) 38 39 40 ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer) 41 ftpserver.serve_forever()
1 import socket 2 import struct 3 import json 4 import os 5 6 7 8 class MYTCPClient: 9 address_family = socket.AF_INET 10 11 socket_type = socket.SOCK_STREAM 12 13 allow_reuse_address = False 14 15 max_packet_size = 8192 16 17 coding='utf-8' 18 19 request_queue_size = 5 20 21 def __init__(self, server_address, connect=True): 22 self.server_address=server_address 23 self.socket = socket.socket(self.address_family, 24 self.socket_type) 25 if connect: 26 try: 27 self.client_connect() 28 except: 29 self.client_close() 30 raise 31 32 def client_connect(self): 33 self.socket.connect(self.server_address) 34 35 def client_close(self): 36 self.socket.close() 37 38 def run(self): 39 while True: 40 inp=input(">>: ").strip() 41 if not inp:continue 42 l=inp.split() 43 cmd=l[0] 44 if hasattr(self,cmd): 45 func=getattr(self,cmd) 46 func(l) 47 48 49 def put(self,args): 50 cmd=args[0] 51 filename=args[1] 52 if not os.path.isfile(filename): 53 print('file:%s is not exists' %filename) 54 return 55 else: 56 filesize=os.path.getsize(filename) 57 58 head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize} 59 print(head_dic) 60 head_json=json.dumps(head_dic) 61 head_json_bytes=bytes(head_json,encoding=self.coding) 62 63 head_struct=struct.pack('i',len(head_json_bytes)) 64 self.socket.send(head_struct) 65 self.socket.send(head_json_bytes) 66 send_size=0 67 with open(filename,'rb') as f: 68 for line in f: 69 self.socket.send(line) 70 send_size+=len(line) 71 print(send_size) 72 else: 73 print('upload successful') 74 75 76 77 78 client=MYTCPClient(('127.0.0.1',8080)) 79 80 client.run()