| 章節(jié)號(hào) | 內(nèi)容???????????? |
|---|---|
| 1圖片格式(png) | 寬度大于620px,保持高寬比減低為620px |
| 12 | 123 |
| 1-1-1 | 方法 |
| 1-1-1 | 方法 |
第1章節(jié)?網(wǎng)路初步
-
1-1?網(wǎng)路初步—tcp ip協(xié)議(族)
??4層次:鏈路層、網(wǎng)絡(luò)層、傳輸層、應(yīng)用層
??7層次:物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會(huì)話層、表示層、應(yīng)用層


-
1-2?端口
??就是用來區(qū)分不同的進(jìn)程。范圍從0-65535。
??知名端口:大家都知道的端口。范圍:0-1023
??動(dòng)態(tài)端口:動(dòng)態(tài)分配的端口。范圍:1024-65535
??查看端口:netstat -an
li@li-ThinkPad-T420s:~$ netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:139 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:445 0.0.0.0:* LISTEN
tcp 0 0 192.168.0.119:53582 182.242.213.227:443 ESTABLISHED
tcp 0 0 192.168.0.119:42430 180.101.136.110:443 ESTABLISHED
tcp 0 0 192.168.0.119:51912 182.131.6.245:443 ESTABLISHED
tcp 0 0 192.168.0.119:53408 52.37.42.64:443 ESTABLISHED
tcp6 0 0 :::139 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 :::445 :::* LISTEN
udp 0 0 127.0.0.53:53 0.0.0.0:*
udp 0 0 0.0.0.0:68 0.0.0.0:*
udp 0 0 192.168.0.255:137 0.0.0.0:*
udp 0 0 192.168.0.119:137 0.0.0.0:*
udp 0 0 0.0.0.0:137 0.0.0.0:*
udp 0 0 192.168.0.255:138 0.0.0.0:*
udp 0 0 192.168.0.119:138 0.0.0.0:*
udp 0 0 0.0.0.0:138 0.0.0.0:*
udp 0 0 0.0.0.0:51687 0.0.0.0:*
udp 0 0 0.0.0.0:631 0.0.0.0:*
udp 0 0 0.0.0.0:5353 0.0.0.0:*
udp6 0 0 :::55239 :::*
udp6 0 0 :::5353 :::*
raw6 0 0 :::58 :::* 7
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 36076 /run/user/1000/systemd/notify
unix 2 [ ] DGRAM 24517 /run/user/120/systemd/notify
unix 2 [ ACC ] SEQPACKET LISTENING 16396 /run/udev/control
unix 2 [ ACC ] STREAM LISTENING 39171 /tmp/sogou-qimpanelli
unix 2 [ ACC ] STREAM LISTENING 36185 /tmp/ssh-nNqqPOVeFdvP/agent.3540
unix 2 [ ACC ] STREAM LISTENING 36079 /run/user/1000/systemd/private
unix 2 [ ACC ] STREAM LISTENING 24520 /run/user/120/systemd/private
unix 2 [ ACC ] STREAM LISTENING 36083 /run/user/1000/gnupg/S.gpg-agent.ssh
unix 2 [ ACC ] STREAM LISTENING 23412 /run/user/120/gnupg/S.gpg-agent.browser
unix 2 [ ACC ] STREAM LISTENING 36084 /run/user/1000/gnupg/S.gpg-agent.browser
unix 2 [ ACC ] STREAM LISTENING 36085 /run/user/1000/gnupg/S.gpg-agent.extra
unix 2 [ ACC ] STREAM LISTENING 23413 /run/user/120/pulse/native
unix 2 [ ACC ] STREAM LISTENING 36086 /run/user/1000/gnupg/S.dirmngr
unix 2 [ ACC ] STREAM LISTENING 23414 /run/user/120/bus
unix 2 [ ACC ] STREAM LISTENING 36087 /run/user/1000/bus
unix 2 [ ACC ] STREAM LISTENING 23415 /run/user/120/gnupg/S.gpg-agent.extra
unix 2 [ ACC ] STREAM LISTENING 36088 /run/user/1000/gnupg/S.gpg-agent
unix 2 [ ACC ] STREAM LISTENING 23416 /run/user/120/gnupg/S.dirmngr
unix 2 [ ] DGRAM 28191 /var/lib/samba/private/msg.sock/1031
unix 2 [ ACC ] STREAM LISTENING 23417 /run/user/120/gnupg/S.gpg-agent.ssh
.
.
.
??端口的用處:一個(gè)ip地址可以提供多種服務(wù),使用端口來區(qū)分不同的服務(wù)功能。如80的http,21的ftp。
??為什么不用pid?多臺(tái)電腦中,pid相同的程序,可能并不是同一種類的程序。
-
1-3?ip地址
??作用:在邏輯上唯一的標(biāo)記一臺(tái)電腦。
??c類地址????192.??168.???1.????1
??二進(jìn)制表現(xiàn):1100000.10101000.00000001.00000001

??私有ip:10.0.0.0-10.255.255.255
??????172.16.0.0-172.31.255.255
??????192.168.0.0-192.168.255.255
-
1-4?socket的簡介(要用2臺(tái)電腦測(cè)試)
??本地進(jìn)程間通信(IPC),如果在同一臺(tái)電腦上,可以使用隊(duì)列、同步等。
??網(wǎng)絡(luò)間進(jìn)程的通信,使用socket套接字。
??ip區(qū)分電腦,端口確定程序,協(xié)議確定通信細(xì)節(jié),三確定就可以通信。
In [1]: import socket
In [3]: ?socket.socket()
Init signature: socket.socket(family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, proto=0, fileno=None)
Docstring: A subclass of _socket.socket adding the makefile() method.
File: /usr/lib/python3.6/socket.py
Type: type
In [4]: help(socket.socket)
Help on class socket in module socket:
class socket(_socket.socket)
| A subclass of _socket.socket adding the makefile() method.
|
| Method resolution order:
| socket
| _socket.socket
| builtins.object
|
| Methods defined here:
|
| __enter__(self)
|
| __exit__(self, *args)
|
| __getstate__(self)
|
| __init__(self, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, proto=0, fileno=None)
| Initialize self. See help(type(self)) for accurate signature.
.
.
.
??下面進(jìn)行簡單的sockte代碼演示,使用udp協(xié)議發(fā)送。
import socket
#創(chuàng)建UDP套接字
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#設(shè)置接收方的ip和端口號(hào)
addr=("192.168.0.108",8080)
#python3注意這里要加上一個(gè)字母`b`,否則產(chǎn)生下面的報(bào)錯(cuò)
data=b"hello socket"
#發(fā)送!?。。?!
us.sendto(data,addr)
#關(guān)閉
us.close()
Exception has occurred: TypeError
a bytes-like object is required, not 'str'
File "/home/li/Desktop/py/ceshi.py", line 5, in <module>
us.sendto(data,addr)
??在接收方我們使用一個(gè)軟件“網(wǎng)絡(luò)調(diào)試助手”來輔助完成。按照?qǐng)D示設(shè)置完畢后,發(fā)送方直接運(yùn)行程序,即可接收到信息如圖示。

-
1-5?端口的問題、綁定端口
??地址、端口,協(xié)議,三者缺一不可。
??UDP類比于寫信。有個(gè)地址就可以郵寄出去。每封信都有地址。
??TCP類比于打電話。要先建立通路。才開始講話。
import socket
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
addr=("192.168.0.108",8080)
data=b"hello socket"
us.sendto(data,addr)
us.sendto(data,addr)
us.sendto(data,addr)
us.close()
??代碼如上圖,我們連續(xù)運(yùn)行四次這段代碼,看接收端的情況:
【Receive from 192.168.0.119 : 46425】:hello sockethello sockethello socket
【Receive from 192.168.0.119 : 52666】:hello sockethello sockethello socket
【Receive from 192.168.0.119 : 33521】:hello sockethello sockethello socket
【Receive from 192.168.0.119 : 34876】:hello sockethello sockethello socket
??可以看到每次發(fā)送的端口都是不一樣的。
??PS:在同一個(gè)OS中,如果一個(gè)端口被占用了,那么其他的程序就不能再使用。很可能這個(gè)程序就要出錯(cuò)。
??如何讓自己的端口不是隨機(jī)呢?那就要用到端口綁定!
import socket
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
addr=("192.168.0.108",8080)
#這里是關(guān)鍵
#如果不寫ip,就表示本機(jī)的任何一個(gè)ip。baddr=("",7787)
baddr=("192.168.0.119",7787)
data=b"hello socket"
#這里是關(guān)鍵
us.bind(baddr)
us.sendto(data,addr)
us.sendto(data,addr)
us.sendto(data,addr)
us.close()
【Receive from 192.168.0.119 : 7787】:hello sockethello
??一般來說,發(fā)送方不綁定。
??接收方要綁定,為服務(wù)方。
??下面編寫一個(gè)接收數(shù)據(jù)的小程序:
import socket
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
baddr=("",7787)
us.bind(baddr)
data=us.recvfrom(1024)
print(data)
us.close()
??運(yùn)行后程序會(huì)阻塞住,等待接收數(shù)據(jù)。

??如上圖在另一端發(fā)送數(shù)據(jù)“123”,程序?qū)⒆詣?dòng)解除阻塞并打印接收到的數(shù)據(jù)。
(b'123', ('192.168.0.108', 8080))
-
1-6?python3的編碼問題及解決
import socket
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
addr=("192.168.0.108",8080)
#這里是關(guān)鍵
#如果不寫ip,就表示本機(jī)的任何一個(gè)ip。baddr=("",7787)
baddr=("192.168.0.119",7787)
data=bytes("我的".encode("utf-8"))
#這里是關(guān)鍵
# us.bind(baddr)
us.sendto(data,addr)
us.sendto(data,addr)
us.sendto(data,addr)
us.close()
data=bytes("123aaa".encode("utf-8"))

??這里使用了encode()編碼功能把字符串進(jìn)行了utf-8的編碼,接收方在接收漢字的時(shí)候呈現(xiàn)了亂碼(倒數(shù)第二和第三行),那么如何解決這個(gè)問題呢?
data=bytes("我的".encode("gb2312"))
【Receive from 192.168.0.119 : 38276】:我的我的我的
??↑這里得到一個(gè)思考,那就是如果你發(fā)送的數(shù)據(jù)對(duì)方接收到了是亂碼,則一定要考慮字符編碼的一致性問題。
# 對(duì)str進(jìn)行解碼操作,使用gb2312的方式來解碼
str.decode("gb2312"))
??↓下面看一下元組的解包操作。
In [1]: a=(1111,2222)
In [2]: a
Out[2]: (1111, 2222)
In [3]: b,c=a
In [4]: b
Out[4]: 1111
In [5]: c
Out[5]: 2222
??中文網(wǎng)站的數(shù)據(jù),要么是utf-8,要么是gb2312
-
1-7?網(wǎng)絡(luò)通信大體過程

-
1-8?簡單的聊天室
import socket
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
baddr=("",7788)
us.bind(baddr)
while True:
data=us.recvfrom(1024)
print(data)
??↑不停的接收數(shù)據(jù),只要收到就打印出來。
li@li-ThinkPad-T420s:~/Desktop/py$ cd /home/li/Desktop/py ; env PYTHONIOENCODING=UTF-8 PYTHONUNBUFFERED=1 /usr/bin/python3 /home/li/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/ptvsd_launcher.py --default --client --host localhost --port 42633 /home/li/Desktop/py/ceshi=======.py
(b'1234', ('192.168.0.108', 8081))
(b'1234', ('192.168.0.108', 8081))
(b'1234', ('192.168.0.108', 8081))
(b'1234', ('192.168.0.108', 8081))
(b'1234', ('192.168.0.108', 8081))
(b'1234', ('192.168.0.108', 8081))
Terminated
??發(fā)送方使用網(wǎng)絡(luò)調(diào)試助手即可。
-
1-9?echo服務(wù)器
??接收到數(shù)據(jù)后,給予適當(dāng)?shù)姆答?/p>
import socket
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
baddr=("",7788)
us.bind(baddr)
while True:
data=us.recvfrom(1024)
print(data)
#關(guān)鍵在此句
us.sendto(b"echo",data[1])
【2019-08-27 08:23:37:985】echo
【2019-08-27 08:23:38:720】echo
【2019-08-27 08:23:39:736】echo
【2019-08-27 08:23:40:329】echo
【2019-08-27 08:23:41:364】echo
【2019-08-27 08:23:42:395】echo
【2019-08-27 08:23:43:427】echo
??↑網(wǎng)絡(luò)調(diào)試助手收到的反饋。
-
1-10?多線程模擬聊天程序

??↑含服務(wù)器的聊天軟件的大致過程
import socket
import threading
from threading import Thread
addr=()
def rec():
while True:
global addr
data=us.recvfrom(1024)
if addr== ():
addr=data[1]
print("\r>>"+ str(data[0].decode("gb2312")))
print(">>"+ str(data[1]))
def sed():
while True:
d=input("\r<<1").encode("gb2312")
sdata=bytes(d)
if addr== ():
print("waiting for some one")
else:
us.sendto(sdata,addr)
us=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
baddr=("",7788)
us.bind(baddr)
t1=Thread(target=rec)
t2=Thread(target=sed)
t1.start()
t2.start()
t1.join()
t2.join()
li@li-ThinkPad-T420s:~/Desktop/py$ cd /home/li/Desktop/py ; env PYTHONIOENCODING=UTF-8 PYTHONUNBUFFERED=1 /usr/bin/python3 /home/li/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/ptvsd_launcher.py --default --client --host localhost --port 40533 /home/li/Desktop/py/ceshi.py
<<14234234
waiting for some one
<<1234234
waiting for some one
>>1234
>>('192.168.0.108', 8081)
>>1234412341234
>>('192.168.0.108', 8081)
fasfsa
<<1asfsafd
<<1asdfasfd
<<1afdasfd
<<1Terminated
【2019-08-27 08:30:58:590】fasfsa
【2019-08-27 08:30:59:402】asfsafd
【2019-08-27 08:31:00:175】asdfasfd
??↑聊天的多線程實(shí)現(xiàn),一個(gè)線程等待接收,一個(gè)線程等待輸入,不會(huì)造成整個(gè)程序的阻塞。python端先等待對(duì)方發(fā)送信息,接收到對(duì)方的ip和端口信息后再發(fā)送。
-
1-11?wireshark

??↑安裝一定要勾選此選項(xiàng)。

-
1-12?tftp分析
??首先下載tftp

??打開后,點(diǎn)擊Browse,選定一個(gè)文件路徑,作為下載支持路徑。此刻下載服務(wù)器已經(jīng)搭建好。
??下面我們開始編寫一個(gè)下載器。首先:
??思考如下問題:什么叫下載,它的過程是什么?
??創(chuàng)建空文件,寫數(shù)據(jù),關(guān)閉。相當(dāng)于接收。
程序概念:
f=open("文件名","bw")
while True:
rec=udpsocket.recvfrom(1024)
if xxx:
沒有數(shù)據(jù)
break
else:
f.write(rec)
??思考如下問題:什么叫上傳?相當(dāng)于發(fā)送。
程序概念:
us.sendto(sdata,addr)
??那么如何與服務(wù)器端進(jìn)行連接呢?換句話來說,協(xié)議是什么呢?
??RFC 1350 - The TFTP Protocol (Revision 2)
??RFC 959 - File Transfer Protocol

??本例用的程序默認(rèn)服務(wù)端口為69
??1、客戶端按照
讀寫請(qǐng)求的格式,向服務(wù)器發(fā)送請(qǐng)求。??2、服務(wù)器向客戶端發(fā)送
數(shù)據(jù)包格式的數(shù)據(jù)。數(shù)據(jù)包最大516字節(jié),一旦數(shù)據(jù)包小于516字節(jié),代表整個(gè)文件傳輸完畢。如果是操作碼3,則表示數(shù)據(jù)正確,你要回復(fù)信息,格式為ACK。??如果文件不存在,服務(wù)器返回
ERROR格式的錯(cuò)誤信息。收到這個(gè),不需要回復(fù)。??3、服務(wù)器給你發(fā)送信息,是隨機(jī)端口。==69==只接收下載請(qǐng)求,確認(rèn)包全部發(fā)往==隨機(jī)端口==。
??如何再windows下查看端口??使用cmd
C:>>netstat -ano | findstr 69
UDP 0.0.0.0:69 *:* 8720
??找到69對(duì)應(yīng)的進(jìn)程id(假如為8720)。
C:>>tasklist | findstr 8720
tftp64.exe 8720 Console 1 30,764 K
??現(xiàn)在已經(jīng)確定端口號(hào)無誤,可以開始了。
??前置知識(shí):struct模塊的.pack()方法。
??前置知識(shí):大端和小端。大端:是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中。
??在網(wǎng)絡(luò)上,數(shù)據(jù)都要以大端發(fā)送。
reqtext=struck.pack("!H8sb5sb",1,"test.jpg",0,"octet",0)
!表示大端順序
H表示占2 字節(jié)
s表示占1個(gè)字節(jié),8s表示8字節(jié)。等效于ssssssss。"test.jpg"共8個(gè)字符。
b表示1字節(jié)。
5s同上。

In [1]: import struct
In [2]: struct.pack
Out[2]: <function _struct.pack>
In [3]: ?struct.pack
Docstring:
pack(fmt, v1, v2, ...) -> bytes
Return a bytes object containing the values v1, v2, ... packed according
to the format string fmt. See help(struct) for more on format strings.
Type: builtin_function_or_method
??現(xiàn)在直接發(fā)送請(qǐng)求報(bào)文,看服務(wù)器端的反應(yīng)。
import socket
import struct
reqtext=struct.pack("!H8sb5sb",1,b"1234.png",0,b"octet",0)
udps=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server=("192.168.0.108",69)
udps.sendto(reqtext,server)

??↑可以看到服務(wù)器端已經(jīng)收到了報(bào)文的請(qǐng)求,但是一會(huì)就消失了,這是因?yàn)槲覀儧]有后續(xù)的應(yīng)答導(dǎo)致。本來這里需要抓包分析,突然發(fā)現(xiàn)wireshark失靈。

??↑原本使用正常的,突然系統(tǒng)自己進(jìn)行了升級(jí),出現(xiàn)了好多問題,win10真是一定要關(guān)閉掉破爛升級(jí)功能。
??后續(xù)幾番搜索解決都沒有用,包括右鍵cmd管理員運(yùn)行,均提示無效。
net start npf
??最后無奈把wireshark及所有配屬包全部卸載掉,然后重裝,重啟。

??↑抓包。使用
udp port == 69來過濾包,具體數(shù)據(jù)如上圖。這里已經(jīng)看到,請(qǐng)求報(bào)文已經(jīng)發(fā)出去了,編程實(shí)現(xiàn)和預(yù)想是符合的。但是為什么只有一個(gè)包呢?因?yàn)橄拗屏硕丝?,所以其他包都被屏蔽了。下面要換一個(gè)過濾方式,來看服務(wù)器的回應(yīng)是什么。
??↑使用tftp來過濾。這里一共看到了3個(gè)包。其中:
??1、是我們發(fā)送的請(qǐng)求包。
??2、是服務(wù)器返回的一個(gè)數(shù)據(jù)包。數(shù)據(jù)塊號(hào)為1。
??3、服務(wù)器發(fā)送的一個(gè)錯(cuò)誤包。
import socket
import struct
g_blknum = 0
g_rf = None
reqtext = struct.pack("!H14sb5sb", 1, b"ipython.tar.gz", 0, b"octet", 0)
print(reqtext)
udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server = ("192.168.0.108", 69)
udps.sendto(reqtext, server)
while True:
data = udps.recvfrom(1024)
# print(len(data[0]))
# print(data[0])
# data是個(gè)元組,服務(wù)器返回的數(shù)據(jù)和服務(wù)器ip及端口
# 把純數(shù)據(jù)部分存入rdata
rdata = data[0]
# 把端口號(hào)存入serverport
serverport = data[1][1]
# print(rdata)
# print(serverport)
# 把返回?cái)?shù)據(jù)的操作碼和塊號(hào)存入opblk,再拆包
opblk = struct.unpack("!HH", rdata[0:4])
opcode, block = opblk
# print(opcode)
# print(block)
if opcode == 3:
if block == 1:
g_rf = open("ipython.tar.gz", "wb")
g_rf.write(rdata[4:])
g_blknum += 1
ackt=struct.pack("!HH", 4, block)
print(ackt)
udps.sendto(ackt, ("192.168.0.108",serverport))
if (g_blknum+1) == block:
if len(data[0]) == 516:
g_rf.write(rdata[4:])
g_blknum += 1
ackt=struct.pack("!HH", 4, block)
# print(ackt)
udps.sendto(ackt, ("192.168.0.108",serverport))
else:
g_rf.write(rdata[4:])
ackt=struct.pack("!HH", 4, block)
udps.sendto(ackt, ("192.168.0.108",serverport))
g_rf.close()
break
print(g_blknum)
??↑上例是個(gè)簡單的下載程序,但是在測(cè)試過程中,通過抓包發(fā)現(xiàn)還是會(huì)有服務(wù)器發(fā)送數(shù)據(jù)后,下載程序沒有回復(fù)導(dǎo)致出錯(cuò)中止的問題。
import socket
import struct
# 全局變量計(jì)算包的數(shù)目
g_blknum = 0
# 全局變量保存文件對(duì)象
g_rf = None
# 創(chuàng)建一個(gè)請(qǐng)求報(bào)文字段
reqtext = struct.pack("!H14sb5sb", 1, b"ipython.tar.gz", 0, b"octet", 0)
print(reqtext)
# 創(chuàng)建UDP套接字
udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 構(gòu)建服務(wù)器的數(shù)據(jù)元組,包含ip和端口
server = ("192.168.0.108", 69)
# 發(fā)送報(bào)文請(qǐng)求
udps.sendto(reqtext, server)
while True:
# 接收服務(wù)器端返回的數(shù)據(jù)
data = udps.recvfrom(1024)
# print(len(data[0]))
# print(data[0])
# data是個(gè)元組,服務(wù)器返回的數(shù)據(jù)和服務(wù)器ip及端口
# 把純數(shù)據(jù)部分存入rdata
rdata = data[0]
# 把端口號(hào)存入serverport
serverport = data[1][1]
# print(rdata)
# print(serverport)
# 把返回?cái)?shù)據(jù)的操作碼和塊號(hào)存入opblk,再拆包
opblk = struct.unpack("!HH", rdata[0:4])
opcode, block = opblk
# print(opcode)
print("reveive:")
print(block)
# 如果是數(shù)據(jù)包,則開始處理
if opcode == 3:
print("ipcode3")
# 如果是第一塊,則要?jiǎng)?chuàng)建文件,并寫入
if block == 1:
print("block 1")
g_rf = open("ipython.tar.gz", "wb")
g_rf.write(rdata[4:])
g_blknum += 1
ackt = struct.pack("!HH", 4, block)
print(ackt)
udps.sendto(ackt, ("192.168.0.108", serverport))
# 后續(xù)塊,判斷順序是否是預(yù)期希望的,是則繼續(xù)寫入
if (g_blknum+1) == block:
# print("block %d" % block)
if len(data[0]) == 516:
g_rf.write(rdata[4:])
g_blknum += 1
ackt = struct.pack("!HH", 4, block)
print(ackt)
print("ackbolk"+str(block))
udps.sendto(ackt, ("192.168.0.108", serverport))
else:
#如果不是516字節(jié),則是最后一個(gè)數(shù)據(jù)包,寫完就關(guān)閉
g_rf.write(rdata[4:])
ackt = struct.pack("!HH", 4, block)
udps.sendto(ackt, ("192.168.0.108", serverport))
g_rf.close()
print("Down load Success!")
break
#差錯(cuò)控制,如果發(fā)來的數(shù)據(jù)塊是上次接收過的,那么直接確認(rèn)數(shù)據(jù)包,不再寫入
elif g_blknum == block:
ackt = struct.pack("!HH", 4, block)
udps.sendto(ackt, ("192.168.0.108", serverport))
else:
print("g_blknum="+str(g_blknum))
print("block="+str(block))
if opcode == 5:
print("Down load fialed")
break
print(g_blknum)
??↑附加了差錯(cuò)控制,貌似沒有再發(fā)生中途出錯(cuò)問題。
-
1-13?UDP廣播
??udp有一種應(yīng)用叫做廣播。
li@li-System-Product-Name:~$ ifconfig
enp40s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.113 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::b8d4:4d8d:b4bd:3b4d prefixlen 64 scopeid 0x20<link>
ether 88:d7:f6:c7:41:25 txqueuelen 1000 (Ethernet)
RX packets 24616 bytes 27148422 (27.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 19719 bytes 2421643 (2.4 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 1871 bytes 230274 (230.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1871 bytes 230274 (230.2 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
??首先查看linux的ip地址,為下步抓取數(shù)據(jù)用。
ip.addr==192.168.0.1 //過濾ip地址
data.len==8 //過濾data部分長度為8的數(shù)據(jù)包
data.data == 00:08:30:03:00:00:00:00 //過濾指定內(nèi)容的數(shù)據(jù)包
udp.srcport == 10092 //過濾經(jīng)過本機(jī)10092端口的udp數(shù)據(jù)包
udp.dstport == 80 //過濾目標(biāo)機(jī)器10092端口的udp數(shù)據(jù)包
udp.port==10092 //過濾本機(jī)或目標(biāo)機(jī)器10092端口的數(shù)據(jù)包
udp.length == 20 //過濾指定長度的
??wireshark的過濾規(guī)則
import socket
import sys
dest=("<broadcast>",7788)
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#設(shè)置套接字以發(fā)送廣播
s.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
s.sendto(b"hi",dest)

??↑可以看到廣播成功發(fā)送
-
1-14?tcp服務(wù)器代碼編寫服務(wù)器

In [1]: import socket
...:
...:
...: sTCP=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
...:
In [2]: ?sTCP.accept
Signature: sTCP.accept()
Docstring:
accept() -> (socket object, address info)
Wait for an incoming connection. Return a new socket
representing the connection, and the address of the client.
For IP sockets, the address info is a pair (hostaddr, port).
File: /usr/lib/python3.6/socket.py
Type: method
import socket
#創(chuàng)建一個(gè)tcp套接字
sTCP=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
addr=("",8899)
sTCP.bind(addr)
#最多可以同時(shí)5個(gè)
sTCP.listen(5)
調(diào)用accept(),返回一個(gè)新套接字,和一個(gè)地址元組
sockobj,addrinfo=sTCP.accept()
data=sockobj.recv(1024)
print(sockobj)
print(addrinfo)
print(data)
sockobj.close()
sTCP.close()
??↑編寫了一個(gè)簡單的tcp服務(wù),客戶端使用網(wǎng)絡(luò)調(diào)試助手。查看端口是否建立可以使用如下命令:
li@li-System-Product-Name:~$ netstat -an | grep 8899

??↑注意這里程序打開以后,默認(rèn)識(shí)別的網(wǎng)卡地址一定要和服務(wù)器在同一網(wǎng)段,出現(xiàn)識(shí)別錯(cuò)誤的情況一定要禁用其他網(wǎng)卡,否則會(huì)連接錯(cuò)誤。
li@li-System-Product-Name:~/Documents$ cd /home/li/Documents ; env PYTHONIOENCODING=UTF-8 PYTHONUNBUFFERED=1 /usr/bin/python3 /home/li/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/ptvsd_launcher.py --default --client --host localhost --port 44557 /home/li/Documents/1.py
<socket.socket fd=18, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.113', 8899), raddr=('192.168.1.140', 51387)>
('192.168.1.140', 51387)
b'312312312312312313'
??小結(jié):
??1、socket創(chuàng)建一個(gè)套接字
??2、bind綁定ip和port
??3、listen使套接字變?yōu)楸粍?dòng)鏈接
??4、accept等待客戶端連接
??5、recv/send接收發(fā)送數(shù)據(jù)
-
1-15?簡單tcp客戶端編寫
from socket import *
cTCP=socket(AF_INET,SOCK_STREAM)
#服務(wù)器的ip和端口,這里和網(wǎng)絡(luò)調(diào)試助手里的設(shè)置要相同。
cAddr=("192.168.0.108",8080)
#連接服務(wù)器
cTCP.connect(cAddr)
sData=b"im tcp client"
#發(fā)送中文要編碼
sData1=bytes("哈哈哈哈".encode("gb2312"))
#發(fā)送不需要地址,因?yàn)橐呀?jīng)連接好了服務(wù)器
#UDP發(fā)送數(shù)據(jù)因?yàn)闆]有之前的連接,所以需要每次發(fā)送填寫接收方地址。
cTCP.send(sData)
cTCP.send(sData1)
rData=cTCP.recv(1024)
print(rData)
cTCP.close()
li@li-ThinkPad-T420s:~/Desktop/py$ cd /home/li/Desktop/py ; env PYTHONIOENCODING=UTF-8 PYTHONUNBUFFERED=1 /usr/bin/python3 /home/li/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/ptvsd_launcher.py --default --client --host localhost --port 44409 /home/li/Desktop/py/ceshi.py
b'fasgdsfgsdgsdg'

??使用網(wǎng)絡(luò)調(diào)試助手測(cè)試成功
-
1-16?使用tcp寫聊天程序