socket編程進階


1、   動態導入模塊

第一種方法(python解釋器自己內部用的):

上圖是我程序的目錄結構

下面代碼是動態導入模塊3.py的源碼:

1 #AUTHOR:FAN
2 lib_dir = __import__('lib.aa')
3 print(lib)
4 obj = lib.aa.C()
5 print(obj.name)

lib包目錄下aa.py的源碼如下:

1 #AUTHOR:FAN
2 
3 class C:
4     def __init__(self):
5         self.name = "dean"

這個時候運行動態導入模塊.py程序,運行結果如下:

1 D:\python35\python.exe D:/python培訓/s14/day8/動態導入模塊3.py
2 <module 'lib' from 'D:\\python培訓\\s14\\day8\\lib\\__init__.py'>
3 dean
4 Process finished with exit code 0

對上述動態導入模塊3.py代碼進行分析:

lib_dir = __import__('lib.aa')

print(lib)

打印出的內容是:

<module 'lib' from 'D:\\python培訓\\s14\\day8\\lib\\__init__.py'>

從打印結果可以看出__import__('lib.aa')這個得到的就是lib目錄

所以這個時候想要調用lib目錄下aa中類中的數據直接:

obj = lib.aa.C()

print(obj.name)

這樣就取得了aa.py程序中C類中初始化參數的name的值

第二種方法(官方建議):

程序目錄如下:

動態導入模塊4.py的代碼如下:

1 #AUTHOR:FAN
2 import importlib
3 aa = importlib.import_module('lib.aa')
4 print(aa)
5 obj = aa.C()
6 print(obj.name)

lib目錄下的aa.py不變化還是上述相同

這個時候運行動態導入模塊4.py,運行結果如下:

1 D:\python35\python.exe D:/python培訓/s14/day8/動態導入模塊4.py
2 <module 'lib.aa' from 'D:\\python培訓\\s14\\day8\\lib\\aa.py'>
3 dean
4 
5 Process finished with exit code 0

對上述代碼進行分析:

aa = importlib.import_module('lib.aa')

print(aa)

打印出的內容如下:

<module 'lib.aa' from 'D:\\python培訓\\s14\\day8\\lib\\aa.py'>

可以看出打印出的是lib.aa,所以這個時候可以直接實例化aa.C,並取得類中初始化參數中的name的值:

obj = aa.C()

print(obj.name)

通過上述兩種方法也可以得出,兩者雖然最終結果是相同的,但是過程中還是有區別的:

第一種方法:lib_dir = __import__('lib.aa'),這種方法得到的module是lib

第二種方法:

aa = importlib.import_module('lib.aa')這種方法得到的module是lib.aa

 

2、斷言assert

 

先看如下代碼:

1 #AUTHOR:FAN
2 name = "dean"
3 assert type(name) is str
4 print(name)

運行結果如下:

1 D:\python35\python.exe D:/python培訓/s14/day8/斷言.py
2 dean
3 
4 Process finished with exit code 0

斷言其實就是對值進行判斷,看是否滿足條件,如果滿足就向下執行,如果不滿足就報錯,我們將代碼進行更改,再次運行:

1 #AUTHOR:FAN
2 name = 123
3 assert type(name) is str
4 print(name)

運行結果如下:

1 D:\python35\python.exe D:/python培訓/s14/day8/斷言.py
2 Traceback (most recent call last):
3   File "D:/python培訓/s14/day8/斷言.py", line 5, in <module>
4     assert type(name) is str
5 AssertionError
6 
7 Process finished with exit code 1

3、Socket

socket通常也稱作“套接字”,用於描述IP地址和端口,是一個通信鏈的句柄,應用程序通常通過“套接字”向網絡發出請求或者應答網絡請求

下面是一些功能:

我們再通常使用socket的時候都需要先導入socket模塊

即import socket,然后實例化socket,例如:

sk = socket(socket.AF_INET,socket.SOCK_STREAM,0)

參數一:地址簇

在這個參數中包含以下幾個參數:

socket.AF_INET  表示IPV4(默認)

socket.AF_INET6 表示IPV6

socket.AF_UNIX   只能用於單一的Unix系統進程間的通信

參數二:類型

socket.SOCK_STREAM  流式socket for TCP(默認)

socket.SOCK_DGRAM   數據格式socket,for UDP

socket.SOCK_RAW     原始套接字,普通的套接字無法處理ICMP,IGMP等網絡報文,可以通過IP_HDRINCL套接字選項由用戶構造IP頭

socket.SOCK_RDM      是一種可靠的UDP形式,即保證交付數據報但不保證順序,SOCK_RDM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文,SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用

socket.SOCK_SEQPACKET  可靠的連續數據包服務

參數三:協議

默認與特定地址家族相關的協議,如果是0 則系統就會根據地址格式和套接類別,自動選擇一個合適的協議

sk.bind((ip地址,port端口))

這種是在默認的AF_INET下這樣以元組的形式存在即(ip,port)

sk.listen(backlog)

開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量

backlog等於5,表示內核已經接到了連接請求,但服務器還沒有調用accept進行處理的連接個數最大為5

這個值不能無限大,因為需要在內核中維護連接隊列

sk.setblocking(bool)

是否阻塞(默認為True),如果設置False,那么accept和recv時一旦無數據,就會報錯

sk.accept()

接收連接並返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據,address是連接客戶端的地址

接收TCP客戶端的連接(阻塞)等待連接的到來

sk.connect(address)

連接到address處的套接字,一般,address的格式為元組(hostname,port),如果連接出錯,返回socket,error錯誤

sk.connect_ex(address)

同上的sk.connect只不過會有返回值,連接成功時返回0,連接失敗時返回編碼

sk.close()

關閉套接字

sk.recv(bufsize[,flag])

接收套接字的數據,數據以字符串形式返回,bufsize指定最多可以接收的數量,flag提供有關消息的其他信息,通常可以忽略

sk.recvfrom(bufsize[.flag])

與recv()類似,但返回值是(data,address)其中data是包含接收數據的字符串,address是發送數據的套接字地址

sock.send(string[,flag])

將string中的數據發送到連接的套接字,返回值是要發送的字節數量,該數量可能小於string的字節大小,即:可能未將指定內容全部發送

sk.sendall(string[,flag])

將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有的數據,成功返回None,失敗則拋出異常

內部通過遞歸調用send,將所有內容發送出去

sk.sendto(string[,flag],address)

將數據發送到套接字,address是形式為(ip地址,port)的元組,指定遠程地址,返回值時發送的字節數,該函數主要用於UDP協議

sk.settimeout(timeout)

設置套接字操作的超時期,timeout是一個浮點數,但是為秒

值為None表示沒有超時期,一般超時期應該在剛創建套接字時設置,因為他們可能用於連接的操作

sk.getpeername()

返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。

sk.fileno()

套接字的文件描述符

用socket寫一個簡單的類似ssh的工具:

服務端:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 
 7 server.listen()
 8 
 9 while True:
10     conn,addr = server.accept()
11     print("一個新的連接:",addr)
12     while True:
13         print("等待新指令")
14         data = conn.recv(1024)
15         if not data:
16             print("客戶端已經斷開")
17             break
18         print("執行指令:",data)
19         cmd_res = os.popen(data.decode()).read()
20         print("send before")
21         if len(cmd_res) == 0:
22             cmd_res = "cmd has no output......"
23         conn.send(cmd_res.encode())
24         print("send done")
25 server.close()

客戶端:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 
 6 client.connect(('127.0.0.1',9999))
 7 
 8 while True:
 9         cmd = input(">>:").strip()
10         if len(cmd) == 0:continue
11         client.send(cmd.encode("utf-8"))
12         cmd_res = client.recv(1024)
13         print(cmd_res.decode())
14 client.close()

先啟動服務端,在啟動客戶端,並在客戶端執行ipconfig命令(先在windows上測試),運行結果如下:

客戶端運行后結果顯示:

 1 D:\python35\python.exe D:/python培訓/s14/day8/sock_ssh_client.py
 2 >>:ipconfig  3 
 4 Windows IP 配置
 5 
 6 
 7 以太網適配器 Bluetooth 網絡連接:
 8 
 9    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
10    連接特定的 DNS 后綴 . . . . . . . : 
11 
12 以太網適配器 本地連接:
13 
14    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
15    連接特定的 DNS 后綴 . . . . . . . : 
16 
17 無線局域網適配器 無線網絡連接:
18 
19    連接特定的 DNS 后綴 . . . . . . . : DHCP HOST
20    本地鏈接 IPv6 地址. . . . . . . . : fe80::85b7:6c65:f032:d29%12
21    IPv4 地址 . . . . . . . . . . . . : 192.168.1.102
22    子網掩碼  . . . . . . . . . . . . : 255.255.255.0
23    默認網關. . . . . . . . . . . . . : 192.168.1.1
24 
25 以太網適配器 VMware Network Adapter VMnet1:
26 
27    連接特定的 DNS 后綴 . . . . . . . : 
28    本地鏈接 IPv6 地址. . . . . . . . : fe80::9c7d:99a2:b09b:fa49%15
29    IPv4 地址 . . . . . . . . . . . . : 10.0.10.22
30    子網掩碼  . . . . . . . . . . . . : 255.255.255.0
31    默認網關. . . . . . . . . . . . . : 
32 
33 以太網適配器 VMware Network Adapter 
34 >>:dir 35 VMnet8:
36 
37    連接特定的 DNS 后綴 . . . . . . . : 
38    本地鏈接 IPv6 地址. . . . . . . . : fe80::4d7:4817:776d:6386%16
39    IPv4 地址 . . . . . . . . . . . . : 172.16.1.200
40    子網掩碼  . . . . . . . . . . . . : 255.255.255.0
41    默認網關. . . . . . . . . . . . . : 
42 
43 隧道適配器 isatap.DHCP HOST:
44 
45    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
46    連接特定的 DNS 后綴 . . . . . . . : DHCP HOST
47 
48 隧道適配器 Teredo Tunneling Pseudo-Interface:
49 
50    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
51    連接特定的 DNS 后綴 . . . . . . . : 
52 
53 隧道適配器 isatap.{AEC4AF99-6E8C-4696-B0CE-1044479986E4}:
54 
55    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
56    連接特定的 DNS 后綴 . . . . . . . : 
57 
58 隧道適配器 isatap.{E8300FF5-2E5A-4E63-8F58-33282553726B}:
59 
60    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
61    連接特定的 DNS 后綴 . . . . . . . : 
62 
63 >>:

服務端顯示:

 1 D:\python35\python.exe D:/python培訓/s14/day8/sock_ssh_server.py
 2 一個新的連接: ('127.0.0.1', 62969)
 3 等待新指令
 4 執行指令: b'ipconfig'
 5 send before
 6 send done
 7 等待新指令
 8 執行指令: b'dir'
 9 send before
10 send done
11 等待新指令

從上面我們可以看出,當我們執行ipconfig命令的時候,顯示的內容並不全,當再次執行dir命令的時候剛才沒有顯示的部分,從這里也可以看出,ipconfig第一次沒有顯示的內容被放在了緩沖區里,當dir執行的時候,先將緩沖區的內容顯示,而這個時候dir命令顯示的內容又被存在了緩沖區中………

所以需要解決上述問題:

解決的思路就是在發送數據之前需要將要發送文件的大小先發送過去,這樣客戶端根據收到數據的大小和這個文件的大小比較,直到收完為止(切記一個問題,整數不能直接encode()必須轉換成字符串)

服務端代碼如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 server.listen()
 7 while True:
 8         conn,addr = server.accept()
 9         print("一個新的連接:",addr)
10         while True:
11             print("等待新指令")
12             data = conn.recv(1024)
13             if not data:
14                 print("客戶端已經斷開")
15                 break
16             print("執行指令:",data)
17             cmd_res = os.popen(data.decode()).read()
18             print("send before")
19             if len(cmd_res) == 0:
20                 cmd_res = "cmd has no output......"
21             conn.send(str(len(cmd_res)).encode())
22             conn.send(cmd_res.encode())
23             print("send done")
24 server.close()

客戶端代碼如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 
 6 client.connect(('127.0.0.1',9999))
 7 
 8 while True:
 9         cmd = input(">>:").strip()
10         if len(cmd) == 0:continue
11         client.send(cmd.encode("utf-8"))
12         cmd_res_size = client.recv(1024)
13         print("命令結果大小:",cmd_res_size)
14     #received_data = b''
15     received_data_size = 0
16     while received_data_size < int(cmd_res_size.decode()):
17         data = client.recv(1024)
18         received_data_size+= len(data)
19         #print(data.decode())
20         #cmd_res = client.recv(1024)
21         print(received_data_size)
22         # print(cmd_res.decode())
23     else:
24         print("cmd res receive done")
25 client.close()

這個時候現將收到命令結果給注釋,先打印收到服務端發送命令數據的大小,已經自己收到的數據累加的大小

同樣先啟動服務端再啟動客戶端

運行結果如下:(下面是客戶端的測試結果)

 1 D:\python35\python.exe D:/python培訓/s14/day8/sock_ssh_client.py
 2 >>:ipconfig
 3 命令結果大小: b'1494'
 4 1024
 5 1960
 6 cmd res receive done
 7 >>:dir
 8 命令結果大小: b'734'
 9 852
10 cmd res receive done
11 >>:ipconfig /all
12 命令結果大小: b'4660'
13 1024
14 2048
15 3072
16 4096
17 5120
18 cmd res receive done
19 >>

從戶可以看出,服務端發送給客戶端命令結果的大小和客戶端實際通過多次收到數據的總和不匹配,出現了客戶端收到的數據大小大於服務器端發送過來的大小。

將服務端代碼放到linux系統上執行,在windows運行客戶端,運行結果如下:

 1 D:\python35\python.exe D:/python培訓/s14/day8/sock_ssh_client.py
 2 >>:pwd
 3 命令結果大小: b'11'
 4 11
 5 cmd res receive done
 6 >>:ifconfig
 7 命令結果大小: b'902'
 8 902
 9 cmd res receive done
10 >>:

從這里可以看出是相同的了,並且判斷出是因為在windows下有中文,同時在服務端發送的時候,有點問題,服務端代碼的問題是下面部分:

conn.send(str(len(cmd_res)).encode())

這個發送的時候應該先改為:

conn.send(str(len(cmd_res.encode())).encode())

這樣就解決了漢字發送后大小不匹配的問題

這樣重新在windows上測試服務端程序和客戶端程序:

更改之后客戶端的運行結果如下:

1 D:\python35\python.exe D:/python培訓/s14/day8/sock_ssh_client.py
2 >>:ipconfig
3 命令結果大小: b'1960'
4 1024
5 1960
6 cmd res receive done
7 >>:

這樣就完美的解決了之前的問題

這樣重新將服務端整理好的代碼如下:

服務端代碼如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 
 7 server.listen()
 8 
 9 while True:
10         conn,addr = server.accept()
11         print("一個新的連接:",addr)
12         while True:
13             print("等待新指令")
14             data = conn.recv(1024)
15             if not data:
16                 print("客戶端已經斷開")
17                 break
18             print("執行指令:",data)
19             cmd_res = os.popen(data.decode()).read()
20             print("send before")
21             if len(cmd_res) == 0:
22                 cmd_res = "cmd has no output......"
23             conn.send(str(len(cmd_res.encode())).encode())           
         conn.send(cmd_res.encode())
24 print("send done") 25 server.close()

客戶端代碼如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 client.connect(('127.0.0.1',9999))
 6 
 7 while True:
 8         cmd = input(">>:").strip()
 9         if len(cmd) == 0:continue
10         client.send(cmd.encode("utf-8"))
11         cmd_res_size = client.recv(1024)
12         print("命令結果大小:",cmd_res_size)
13         received_data = b''
14         received_data_size = 0
15         while received_data_size < int(cmd_res_size.decode()):
16             data = client.recv(1024)
17             received_data_size+= len(data)這里用len判斷長度是因為服務器每次發來的不一定是1024,這個問題一定要注意 18             print(received_data_size)
19             received_data+=data
20         else:
21             print("cmd res receive done")
22             print(received_data.decode())
23 client.close()

客戶端運行結果如下:

 1 D:\python35\python.exe D:/python培訓/s14/day8/sock_ssh_client.py
 2 >>:dir
 3 命令結果大小: b'852'
 4 852
 5 cmd res receive done
 6  驅動器 D 中的卷是 新加卷
 7  卷的序列號是 7095-8443
 8 
 9  D:\python培訓\s14\day8 的目錄
10 
11 2016/09/12  23:25    <DIR>          .
12 2016/09/12  23:25    <DIR>          ..
13 2016/09/11  16:54    <DIR>          cc
14 2016/05/12  17:25       181,238,643 jdk8.tar.gz
15 2016/09/10  10:00    <DIR>          lib
16 2016/09/12  23:25               681 sock_ssh_client.py
17 2016/09/12  23:22               608 sock_ssh_server.py
18 2016/09/10  09:34                11 __init__.py
19 2016/09/10  10:26               292 動態導入模塊.py
20 2016/09/10  10:06                88 動態導入模塊2.py
21 2016/09/11  16:12               102 動態導入模塊3.py
22 2016/09/11  16:23               113 動態導入模塊4.py
23 2016/09/11  16:45                66 斷言.py
24                9 個文件    181,240,604 字節
25                4 個目錄 181,201,088,512 可用字節
26 
27 >>:ipconfig
28 命令結果大小: b'1960'
29 1024
30 1960
31 cmd res receive done
32 
33 Windows IP 配置
34 
35 
36 以太網適配器 Bluetooth 網絡連接:
37 
38    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
39    連接特定的 DNS 后綴 . . . . . . . : 
40 
41 以太網適配器 本地連接:
42 
43    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
44    連接特定的 DNS 后綴 . . . . . . . : 
45 
46 無線局域網適配器 無線網絡連接:
47 
48    連接特定的 DNS 后綴 . . . . . . . : DHCP HOST
49    本地鏈接 IPv6 地址. . . . . . . . : fe80::85b7:6c65:f032:d29%12
50    IPv4 地址 . . . . . . . . . . . . : 192.168.1.103
51    子網掩碼  . . . . . . . . . . . . : 255.255.255.0
52    默認網關. . . . . . . . . . . . . : 192.168.1.1
53 
54 以太網適配器 VMware Network Adapter VMnet1:
55 
56    連接特定的 DNS 后綴 . . . . . . . : 
57    本地鏈接 IPv6 地址. . . . . . . . : fe80::9c7d:99a2:b09b:fa49%15
58    IPv4 地址 . . . . . . . . . . . . : 10.0.10.22
59    子網掩碼  . . . . . . . . . . . . : 255.255.255.0
60    默認網關. . . . . . . . . . . . . : 
61 
62 以太網適配器 VMware Network Adapter VMnet8:
63 
64    連接特定的 DNS 后綴 . . . . . . . : 
65    本地鏈接 IPv6 地址. . . . . . . . : fe80::4d7:4817:776d:6386%16
66    IPv4 地址 . . . . . . . . . . . . : 172.16.1.200
67    子網掩碼  . . . . . . . . . . . . : 255.255.255.0
68    默認網關. . . . . . . . . . . . . : 
69 
70 隧道適配器 isatap.DHCP HOST:
71 
72    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
73    連接特定的 DNS 后綴 . . . . . . . : DHCP HOST
74 
75 隧道適配器 Teredo Tunneling Pseudo-Interface:
76 
77    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
78    連接特定的 DNS 后綴 . . . . . . . : 
79 
80 隧道適配器 isatap.{AEC4AF99-6E8C-4696-B0CE-1044479986E4}:
81 
82    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
83    連接特定的 DNS 后綴 . . . . . . . : 
84 
85 隧道適配器 isatap.{E8300FF5-2E5A-4E63-8F58-33282553726B}:
86 
87    媒體狀態  . . . . . . . . . . . . : 媒體已斷開
88    連接特定的 DNS 后綴 . . . . . . . : 
89 
90 >>:

這個時候命令結果顯示正常,並且結果大小也顯示正常

但是當把程序放到linux上執行時發現出現問題了,客戶端提示錯誤如下,並且提示錯誤后程序退出,服務端提示客戶端斷開連接:

1 >>:ifconfig
2 命令結果大小: b'906ens33     Link encap:Ethernet  HWaddr 00:0c:29:96:2f:bc  \n          inet addr:192.168.1.105  Bcast:192.168.1.255  Mask:255.255.255.0\n          inet6 addr: fe80::b011:2300:ecc8:c70d/64 Scope:Link\n          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1\n          RX packets:814292 errors:0 dropped:0 overruns:0 frame:0\n          TX packets:65371 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txqueuelen:1000 \n          RX bytes:923448604 (923.4 MB)  TX bytes:4865728 (4.8 MB)\n\nlo        Link encap:Local Loopback  \n          inet addr:127.0.0.1  Mask:255.0.0.0\n          inet6 addr: ::1/128 Scope:Host\n          UP LOOPBACK RUNNING  MTU:65536  Metric:1\n          RX packets:1444 errors:0 dropped:0 overruns:0 frame:0\n          TX packets:1444 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txqueuelen:1 \n          RX bytes:115764 (115.7 KB)  TX bytes:115764 (115.7 KB)\n\n'
3 Traceback (most recent call last):
4   File "socket_ssh_client.py", line 16, in <module>
5     while received_data_size < int(cmd_res_size.decode()):
6 ValueError: invalid literal for int() with base 10: '906ens33     Link encap:Ethernet  HWaddr 00:0c:29:96:2f:bc  \n          inet addr:192.168.1.105  Bcast:192.168.1.255  Mask:255.255.255.0\n          inet6 addr: fe80::b011:2300:ecc8:c70d/64 Scope:Link
7 root@python:~/0912#

出現上述錯誤的原因是因為在服務端的兩行代碼:

conn.send(str(len(cmd_res.encode())).encode())

conn.send(cmd_res.encode())

這兩句代碼連着發導致了粘包:

兩次連着send緩沖區會將兩次的數據合並為一條發送給客戶端從而導致數據粘在一起

解決方法:

服務端代碼:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 
 7 server.listen()
 8 
 9 while True:
10         conn,addr = server.accept()
11         print("一個新的連接:",addr)
12         while True:
13             print("等待新指令")
14             data = conn.recv(1024)
15             if not data:
16                 print("客戶端已經斷開")
17                 break
18             print("執行指令:",data)
19             cmd_res = os.popen(data.decode()).read()
20             print("send before")
21             if len(cmd_res) == 0:
22                 cmd_res = "cmd has no output......"
23             conn.send(str(len(cmd_res.encode())).encode())
24             client_ack = conn.recv(1024) 25             print("來自客戶端的確認:",client_ack.decode()) 26             conn.send(cmd_res.encode())
27             print("send done")
28 server.close()

客戶端:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 
 6 client.connect(('127.0.0.1',9999))
 7 #client.connect(('192.168.1.105',9999))
 8 
 9 while True:
10         cmd = input(">>:").strip()
11         if len(cmd) == 0:continue
12         client.send(cmd.encode("utf-8"))
13         cmd_res_size = client.recv(1024)
14         client.send("准備好接收數據了".encode())
15         print("命令結果大小:",cmd_res_size)
16         received_data = b''
17         received_data_size = 0
18         while received_data_size < int(cmd_res_size.decode()):
19             data = client.recv(1024)
20             received_data_size+= len(data)
21             print(received_data_size)
22             received_data+=data
23         else:
24             print("cmd res receive done")
25             print(received_data.decode())
26 client.close()

這樣就完美的解決了粘包的問題

4、 SocketServer模塊

SocketServer內部使用IO多路復用以及“多線程”和“多進程”,從而實現並發處理多個客戶端請求的Socket服務端,即:每個客戶端請求連接到服務器時,Socket服務端都會在服務器上創建一個線程或進程專門負責處理當前客戶端的所有請求

關於SocketServer的使用:

a. 必須自己創建一個請求處理類,並且這個類要繼承BaseRequestHandler並且還要重寫父類里的handle()

b. 必須實例化TCPServer,並且傳遞server ip和上面創建的請求處理類給這個TCPServer

c. server.handle_request()只處理一個請求

server.server_forever()處理多個請求,永遠執行

d. 最后關閉socket

下面是一個socketserver的一個例子:

服務端:

 1 #AUTHOR:FAN
 2 
 3 import socketserver
 4 #對應上面所說的自己創建一個請求處理類並繼承BaseRequestHandler
 5 class MyTCPHandler(socketserver.BaseRequestHandler):
 6 
 7 #對應上面的重寫父類里的handle(),切記所有的交互都在handle()里面切記
 8 def handle(self):  
 9         while True:
10             try:
11                 self.data = self.request.recv(1024).strip()
12                 print("{} wrote:".format(self.client_address[0]))
13                 print(self.data)
14                 self.request.sendall(self.data.upper())
15             except ConnectionResetError as e:
16                 print(e)
17                 break
18 if __name__ == "__main__":
19         HOST,PORT = '127.0.0.1',9999
20 #這里對應上面的實例化TCPServer,並傳遞server ip和上面創建的請求處理類,也就是MyTCPHandler
21 server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
22 server.serve_forever()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM