在學習網絡編程之前還有許多的知識需要普及。socket就是很重要的一環。今天來看一看套接字。
1.服務器端與客戶端
BS架構 (騰訊通軟件:server+client)
CS架構 (web網站)
C/S架構與socket的關系:
我們學習socket就是為了完成C/S架構的開發
2.OSI七層模型
互聯網協議按照功能不同分為osi七層或tcp/ip五層或tcp/ip四層
每層運行常見物理設備
詳細參考:
http://www.cnblogs.com/linhaifeng/articles/5937962.html#_label4
學習socket一定要先學習互聯網協議:
1.首先:本節課程的目標就是教會你如何基於socket編程,來開發一款自己的C/S架構軟件
2.其次:C/S架構的軟件(軟件屬於應用層)是基於網絡進行通信的
3.然后:網絡的核心即一堆協議,協議即標准,你想開發一款基於網絡通信的軟件,就必須遵循這些標准。
4.最后:就讓我們從這些標准開始研究,開啟我們的socket編程之旅
TCP/IP協議族包括運輸層、網絡層、鏈路層。
3.socket層
Socket是介於應用層和傳輸層之間。
4.socket是什么
Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標准的。
掃盲篇:
1 將socket說成ip+port,ip是用來標識互聯網中的一台主機的位置,而port是用來標識這台機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啟的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序 2 3 而程序的pid是同一台機器上不同進程或者線程的標識(Google Chrome會有多個PID)
5.套接字的發展歷程
套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 因此,有時人們也把套接字稱為“伯克利套接字”或“BSD 套接字”。一開始,套接字被設計用在同 一台主機上多個應用程序之間的通訊。這也被稱進程間通訊,或 IPC。套接字有兩種(或者稱為有兩個種族),分別是基於文件型的和基於網絡型的。
1、基於文件類型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信
2、基於網絡類型的套接字家族
套接字家族的名字:AF_INET
(還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要么是只用於某個平台,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由於我們只關心網絡編程,所以大部分時候我么只使用AF_INET)
6.套接字的工作流程
生活中的場景,你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲后提起電話,這時你和你的朋友就建立起了連接,就可以講話了。等交流結束,掛斷電話結束此次交談。
生活中的場景就解釋了這工作原理,也許TCP/IP協議族就是誕生於生活中,這也不一定。
先從服務器端說起。服務器端先初始化Socket,然后與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然后把回應數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束。
一、socket模塊發送和接收消息
示例:模擬發送消息和接收消息的過程
tcp服務端(server)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 import socket 5 6 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 7 phone.bind(('127.0.0.1',8000)) #綁定手機卡 #改成服務端網卡IP地址和端口 8 phone.listen(5) #開機 5的作用是最大掛起連接數 #backlog連接池(也叫半鏈接) 9 print('------------->') 10 conn,addr=phone.accept() #等電話 11 12 msg=conn.recv(1024) #收消息 13 print('客戶端發來的消息是:',msg) 14 conn.send(msg.upper()) #發消息 15 16 conn.close() 17 phone.close()
執行結果:
1 ------------->
tcp客戶端(client)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 import socket 5 6 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 7 8 phone.connect(('127.0.0.1',8000)) #拔通電話 #改成服務端網卡IP地址和端口 9 10 phone.send('hello'.encode('utf-8')) #發消息 11 data=phone.recv(1024) 12 print('收到服務端的發來的消息: ',data) 13 14 phone.close()
執行結果:
1 收到服務端的發來的消息: b'HELLO'
二、功能介紹
① server = socket.socket()

1 參數一:地址簇 2 3 socket.AF_INET IPv4(默認) 4 socket.AF_INET6 IPv6 5 6 socket.AF_UNIX 只能夠用於單一的Unix系統進程間通信 7 8 參數二:類型 9 10 socket.SOCK_STREAM 流式socket , for TCP (默認) 11 socket.SOCK_DGRAM 數據報式socket , for UDP 12 13 socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。 14 socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用。 15 socket.SOCK_SEQPACKET 可靠的連續數據包服務 16 17 參數三:協議 18 (默認)與特定的地址家族相關的協議,如果是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議 19 20 詳情

1 # 服務端 2 import socket 3 ip_port = ('127.0.0.1',9999) 4 sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) 5 sk.bind(ip_port) 6 7 while True: 8 data,(host,port) = sk.recvfrom(1024) 9 print(data,host,port) 10 sk.sendto(bytes('ok', encoding='utf-8'), (host,port)) 11 12 13 #客戶端 14 import socket 15 ip_port = ('127.0.0.1',9999) 16 17 sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) 18 while True: 19 inp = input('數據:').strip() 20 if inp == 'exit': 21 break 22 sk.sendto(bytes(inp, encoding='utf-8'),ip_port) 23 data = sk.recvfrom(1024) 24 print(data) 25 26 sk.close() 27 28 UDP Demo
② server.bind(address)
server.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址
③ server.listen(backlog)
開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量。backlog等於5,表示內核已經接到了連接請求,但服務器還沒有調用accept進行處理的連接個數最大為5,這個值不能無限大,因為要在內核中維護連接隊列
④ server.setblocking(bool)
是否阻塞(默認True),如果設置False,那么accept和recv時一旦無數據,則報錯
⑤ conn,addr = server.accept()
接受連接並返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。接收TCP 客戶的連接(阻塞式)等待連接的到來
⑥ client.connect(address)
連接到address處的套接字。一般,address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。
⑦ client.connect_ex(address)
同上,只不過會有返回值,連接成功時返回 0 ,連接失敗時候返回編碼,例如:10061
⑧ client.close()
關閉套接字
⑨ client.recv(bufsize[,flag])
接受套接字的數據。數據以字符串形式返回,bufsize指定最多可以接收的數量。flag提供有關消息的其他信息,通常可以忽略
⑩ client.recvfrom(bufsize[.flag])
與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址
⑪ server.send(string[,flag])
將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容全部發送
⑫ server.sendall(string[,flag])
將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常;
內部通過遞歸調用send,將所有內容發送出去
⑬ server.sendto(string[,flag],address)
將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議
⑭ sk.settimeout(timeout)
設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因為它們可能用於連接的操作(如 client 連接最多等待5s )
⑮ sk.getpeername()
返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)
⑯ sk.getsockname()
返回套接字自己的地址。通常是一個元組(ipaddr,port)
⑰ sk.fileno()
套接字的文件描述符
三、ssh程序
整合下上面的代碼,做個ssh連接的客戶端,實現基本xshell功能

1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #-Author-Lian 4 5 #ssh server 6 7 import socket,os 8 9 ip_port = ("127.0.0.1",9999) 10 server = socket.socket() 11 server.bind(ip_port) 12 server.listen(5) 13 14 while True: 15 conn,add = server.accept() 16 while True: 17 client_data = conn.recv(1024) 18 recv_data = client_data.decode("utf-8") 19 if recv_data == "exit": 20 break 21 send_data = os.popen(recv_data).read() 22 if not send_data: 23 conn.sendall(client_data+"命令不存在".encode("utf-8")) 24 else: 25 conn.sendall(send_data.encode("utf-8")) 26 conn.close() 27 28 ssh 服務端

1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #-Author-Lian 4 5 #ssh client 6 7 import socket 8 9 ip_port = ("127.0.0.1",9999) 10 client = socket.socket() 11 client.connect(ip_port) 12 13 while True: 14 info = input("->>").strip() 15 if not info: 16 continue 17 client.sendall(info.encode("utf-8")) 18 if info == "exit": 19 break 20 server_data = client.recv(1024) 21 print(server_data.decode("utf-8")) 22 23 client.close() 24 25 ssh 客戶端
7.粘包
須知:只有TCP有粘包現象,UDP永遠不會粘包。(原因詳見第3點)
1、socket收發消息的原理
socket發送原理圖
2、為什么會出現所謂的粘包
原因:接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。
此外,發送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化算法把這些數據合成一個TCP段后一次發送出去,這樣接收方就收到了粘包數據。
- TCP(transport control protocol,傳輸控制協議)是面向連接的,面向流的,提供高可靠性服務。收發兩端(客戶端和服務器端)都要有一一成對的socket,因此,發送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將多次間隔較小且數據量小的數據,合並成一個大的數據塊,然后進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通信是無消息保護邊界的。
- UDP(user datagram protocol,用戶數據報協議)是無連接的,面向消息的,提供高效率服務。不會使用塊的合並優化算法,, 由於UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩沖區)采用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對於接收端來說,就容易進行區分處理了。 即面向消息的通信是有消息保護邊界的。
- tcp是基於數據流的,於是收發的消息不能為空,這就需要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,而udp是基於數據報的,即便是你輸入的是空內容(直接回車),那也不是空消息,udp協議會幫你封裝上消息頭。
3、tcp會發生粘包的兩種情況如下:
1、發送端多次send間隔較短,並且數據量較小,tcp會通過Nagls算法,封裝成一個包,發送到接收端,接收端不知道這個包由幾部分組成,所以就會產生粘包。
2、數據量發送的大,接收端接收的小,再接一次,還會出現上次沒有接收完成的數據。就會出現粘包。
示例1: 發送端多次send間隔較短,並且數據量較小,tcp會通過Nagls算法,封裝成一個包,發送到接收端,接收端不知道這個包由幾部分組成,所以就會產生粘包。
server服務端:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 from socket import * 6 ip_port=('127.0.0.1',8082) 7 back_log=5 8 buffer_size=1024 9 10 tcp_server=socket(AF_INET,SOCK_STREAM) 11 tcp_server.bind(ip_port) 12 tcp_server.listen(back_log) 13 14 conn,addr=tcp_server.accept() 15 16 data1=conn.recv(buffer_size) #指定buffer_size ,得到的結果就是通過Nagle算法,隨機接收次數。 17 print('第1次數據',data1) 18 19 data2=conn.recv(buffer_size) 20 print('第2次數據',data2) 21 22 data3=conn.recv(buffer_size) 23 print('第3次數據',data3)
client客戶端
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 from socket import * 6 import time 7 8 ip_port=('127.0.0.1',8082) 9 back_log=5 10 buffer_size=1024 11 12 tcp_client=socket(AF_INET,SOCK_STREAM) 13 tcp_client.connect(ip_port) 14 15 tcp_client.send('hello'.encode('utf-8')) 16 tcp_client.send('world'.encode('utf-8')) 17 tcp_client.send('egon'.encode('utf-8')) 18 19 20 time.sleep(1000)
執行結果:
1 第1次數據 b'helloworldegon' #不確定接收次數。
示例2:指定接收字節數,相當於服務端知道接收長度,就不會出現粘包現象
粘包服務端
1 from socket import * 2 ip_port=('127.0.0.1',8080) 3 back_log=5 4 buffer_size=1024 5 6 tcp_server=socket(AF_INET,SOCK_STREAM) 7 tcp_server.bind(ip_port) 8 tcp_server.listen(back_log) 9 10 conn,addr=tcp_server.accept() 11 12 data1=conn.recv(5) #指定每次接收字節數,就不會出現粘包現象 13 print('第一次數據',data1) 14 15 data2=conn.recv(5) 16 print('第2次數據',data2) 17 18 data3=conn.recv(5) 19 print('第3次數據',data3)
粘包客戶端
1 from socket import * 2 import time 3 ip_port=('127.0.0.1',8080) 4 back_log=5 5 buffer_size=1024 6 7 tcp_client=socket(AF_INET,SOCK_STREAM) 8 tcp_client.connect(ip_port) 9 10 tcp_client.send('hello'.encode('utf-8')) 11 tcp_client.send('world'.encode('utf-8')) 12 tcp_client.send('egon'.encode('utf-8')) 13 14 15 time.sleep(1000)
執行結果:
1 第1次數據 b'hello' #不會出現粘包現象,發送三次,就接收三次 2 第2次數據 b'world' 3 第3次數據 b'egon'
示例3:數據量發送的大,接收端接收的小,再接一次,還會出現上次沒有接收完成的數據。就會出現粘包。
粘包服務端
1 from socket import * 2 ip_port=('127.0.0.1',8080) 3 back_log=5 4 buffer_size=1024 5 6 tcp_server=socket(AF_INET,SOCK_STREAM) 7 tcp_server.bind(ip_port) 8 tcp_server.listen(back_log) 9 10 conn,addr=tcp_server.accept() 11 12 data1=conn.recv(1) 13 print('第1次數據',data1) 14 15 # data2=conn.recv(5) 16 # print('第2次數據',data2) 17 # 18 # data3=conn.recv(1) 19 # print('第3次數據',data3)
粘包客戶端
1 from socket import * 2 import time 3 ip_port=('127.0.0.1',8080) 4 back_log=5 5 buffer_size=1024 #接收的數據只有1024 6 7 tcp_client=socket(AF_INET,SOCK_STREAM) 8 tcp_client.connect(ip_port) 9 10 tcp_client.send('helloworldegon'.encode('utf-8')) 11 12 time.sleep(1000)
執行結果:
1 第1次數據 b'h' 2 第2次數據 b'ellow' #發送的數據過大,接收的數據設置的較小,就會出現導致粘包 3 第3次數據 b'o'
補充知識:
1、tcp是可靠傳輸
tcp在數據傳輸時,發送端先把數據發送到自己的緩存中,然后協議控制將緩存中的數據發往對端,對端返回一個ack=1,發送端則清理緩存中的數據,對端返回ack=0,則重新發送數據,所以tcp是可靠的。
2、udp是不可靠傳輸
udp發送數據,對端是不會返回確認信息的,因此不可靠。
3.解決粘包
法一:比較(LOW)版本
示例:
low_socket_server服務端
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 #low版解決粘包版本服務端 6 from socket import * 7 import subprocess 8 ip_port=('127.0.0.1',8080) 9 back_log=5 10 buffer_size=1024 11 12 tcp_server=socket(AF_INET,SOCK_STREAM) 13 tcp_server.bind(ip_port) 14 tcp_server.listen(back_log) 15 16 while True: 17 conn,addr=tcp_server.accept() 18 print('新的客戶端鏈接',addr) 19 while True: 20 #收消息 21 try: 22 cmd=conn.recv(buffer_size) 23 if not cmd:break 24 print('收到客戶端的命令',cmd) 25 26 #執行命令,得到命令的運行結果cmd_res 27 res=subprocess.Popen(cmd.decode('utf-8'),shell=True, 28 stderr=subprocess.PIPE, 29 stdout=subprocess.PIPE, 30 stdin=subprocess.PIPE) 31 err=res.stderr.read() 32 if err: 33 cmd_res=err 34 else: 35 cmd_res=res.stdout.read() 36 37 #發送消息 38 if not cmd_res: 39 cmd_res='執行成功'.encode('gbk') 40 41 length=len(cmd_res) #計算長度 42 conn.send(str(length).encode('utf-8')) #把長度發給客戶端 43 client_ready=conn.recv(buffer_size) #卡着一個recv 44 if client_ready == b'ready': #如果收到客戶端的ready消息,就說明准備好了。 45 conn.send(cmd_res) #就可以send給客戶端發送消息啦! 46 except Exception as e: 47 print(e) 48 break
low_socket_client客戶端
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 #low版解決粘包版客戶端 6 from socket import * 7 ip_port=('127.0.0.1',8080) 8 back_log=5 9 buffer_size=1024 10 11 tcp_client=socket(AF_INET,SOCK_STREAM) 12 tcp_client.connect(ip_port) 13 14 while True: 15 cmd=input('>>: ').strip() 16 if not cmd:continue 17 if cmd == 'quit':break 18 19 tcp_client.send(cmd.encode('utf-8')) 20 21 22 #解決粘包 23 length=tcp_client.recv(buffer_size) #接收發送過來的長度(1024*8=8192,2**8192=可以接收的長度) 24 tcp_client.send(b'ready') #客戶端再send給服務端,告訴服務端我准備好啦! 25 26 length=int(length.decode('utf-8')) #先解碼,轉成字符串的長度 27 #解決思路:就是提前發一個頭過去,告訴客戶端需要接收的長度(分兩步:1、發送發度 2、再次發送數據) 28 recv_size=0 #接收的尺寸 29 recv_msg=b'' #最后要拼接起來 30 while recv_size < length: #要收多大?,要先判斷接收的尺寸<length 31 recv_msg += tcp_client.recv(buffer_size) #接收到的數據,拼接buffer_size, 32 recv_size=len(recv_msg) #1024 #衡量自己接收了多少數據,有沒有收完(統計recv_msg的長度) 33 34 35 print('命令的執行結果是 ',recv_msg.decode('gbk')) 36 tcp_client.close()
執行結果:

#客戶端執行命令: >>: cat /etc/passwd 命令的執行結果是 'cat' is not recognized as an internal or external command, operable program or batch file. >>: dir 命令的執行結果是 Volume in drive D is SSD Volume Serial Number is 687D-EF64 Directory of D:\python\day30 2017/01/04 00:36 <DIR> . 2017/01/04 00:36 <DIR> .. 2017/01/03 11:39 613 client.py 2017/01/03 11:40 597 client_01.py 2017/01/03 11:40 597 client_02.py 2017/01/04 00:35 770 low_socket_client.py 2017/01/04 00:36 1,408 low_socket_server.py 2017/01/03 15:54 438 ntp_clinet.py 2017/01/03 16:01 591 ntp_server.py 2017/01/03 19:36 206 s1_server.py 2017/01/03 11:26 588 server.py 2017/01/03 11:55 717 server01.py 2017/01/03 23:57 603 socket_clinet_tcp.py 2017/01/04 00:12 531 socket_clinet_udp.py 2017/01/03 17:48 1,301 socket_server_tcp.py 2017/01/03 18:27 897 socket_server_udp.py 2017/01/03 15:38 440 udp_clinet.py 2017/01/03 15:23 355 udp_server.py 16 File(s) 10,652 bytes 2 Dir(s) 638,994,411,520 bytes free >>: cd .. 命令的執行結果是 執行成功 #服務端返回結果: 新的客戶端鏈接 ('127.0.0.1', 54395) 收到客戶端的命令 b'cat /etc/passwd' 收到客戶端的命令 b'dir' 收到客戶端的命令 b'cd ..'
總結:
(為何low): 程序的運行速度遠快於網絡傳輸速度,所以在發送一段字節前,先用send去發送該字節流長度,這種方式會放大網絡延遲帶來的性能損耗。
法二:節省網絡傳輸版本(牛逼版本)
為字節流加上自定義固定長度報頭,報頭中包含字節流長度,然后一次send到對端,對端在接收時,先從緩存中取出定長的報頭,然后再取真實數據。
示例:(沒實現多客戶端並發)
tcp_socket_server服務端:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 from socket import * 6 import subprocess 7 import struct 8 ip_port=('127.0.0.1',8080) 9 back_log=5 10 buffer_size=1024 11 12 tcp_server=socket(AF_INET,SOCK_STREAM) 13 tcp_server.bind(ip_port) 14 tcp_server.listen(back_log) 15 16 while True: 17 conn,addr=tcp_server.accept() 18 print('新的客戶端鏈接',addr) 19 while True: 20 #收 21 try: 22 cmd=conn.recv(buffer_size) 23 if not cmd:break 24 print('收到客戶端的命令',cmd) 25 26 #執行命令,得到命令的運行結果cmd_res 27 res=subprocess.Popen(cmd.decode('utf-8'),shell=True, 28 stderr=subprocess.PIPE, 29 stdout=subprocess.PIPE, 30 stdin=subprocess.PIPE) 31 err=res.stderr.read() 32 if err: 33 cmd_res=err 34 else: 35 cmd_res=res.stdout.read() 36 37 #發 38 if not cmd_res: 39 cmd_res='執行成功'.encode('gbk') 40 41 length=len(cmd_res) 42 43 data_length=struct.pack('i',length) 44 conn.send(data_length) 45 conn.send(cmd_res) 46 except Exception as e: 47 print(e) 48 break
tcp_socket_client客戶端
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 from socket import * 6 import struct 7 from functools import partial 8 ip_port=('127.0.0.1',8080) 9 back_log=5 10 buffer_size=1024 11 12 tcp_client=socket(AF_INET,SOCK_STREAM) 13 tcp_client.connect(ip_port) 14 15 while True: 16 cmd=input('>>: ').strip() 17 if not cmd:continue 18 if cmd == 'quit':break 19 20 tcp_client.send(cmd.encode('utf-8')) 21 22 23 #解決粘包 24 length_data=tcp_client.recv(4) 25 length=struct.unpack('i',length_data)[0] 26 27 recv_size=0 28 recv_data=b'' 29 while recv_size < length: 30 recv_data+=tcp_client.recv(buffer_size) 31 recv_size=len(recv_data) 32 print('命令的執行結果是 ',recv_data.decode('gbk')) 33 tcp_client.close()
執行結果:

#客戶端向服務器發送消息 >>: dir 命令的執行結果是 Volume in drive D is SSD Volume Serial Number is 687D-EF64 Directory of D:\python\day31 2017/01/05 18:31 <DIR> . 2017/01/05 18:31 <DIR> .. 2017/01/05 14:33 244 s1.py 2017/01/05 18:29 752 s1_tcp_socket_client.py 2017/01/05 18:31 679 s1_tcp_socket_client01.py 2017/01/05 18:28 1,325 s1_tcp_socket_server.py 2017/01/05 15:01 498 tcp_socket_client.py 2017/01/05 15:00 670 tcp_socket_server.py 2017/01/05 17:16 391 udp_socket_clinet.py 2017/01/05 17:20 512 udp_socket_server.py 8 File(s) 5,071 bytes 2 Dir(s) 609,921,380,352 bytes free #服務端返回結果: 新的客戶端鏈接 ('127.0.0.1', 53585) 收到客戶端的命令 b'dir'