02Python學(xué)習(xí)筆記之二.十【網(wǎng)絡(luò)編程初步】2019-08-26

章節(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寫聊天程序


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 網(wǎng)絡(luò)編程 一.楔子 你現(xiàn)在已經(jīng)學(xué)會(huì)了寫python代碼,假如你寫了兩個(gè)python文件a.py和b.py,分別去運(yùn)...
    go以恒閱讀 2,250評(píng)論 0 6
  • tcp/ip簡介 為了把全世界的所有不同類型的計(jì)算機(jī)都連接起來,就必須規(guī)定一套全球通用的協(xié)議,為了實(shí)現(xiàn)互聯(lián)網(wǎng)這個(gè)目...
    盛夏光年_49e9閱讀 590評(píng)論 0 0
  • 話說兩臺(tái)電腦要通訊就必須遵守共同的規(guī)則,就好比兩個(gè)人要溝通就必須使用共同的語言一樣。一個(gè)只懂英語的人,和一個(gè)只懂中...
    哲逗年閱讀 1,041評(píng)論 0 0
  • ·網(wǎng)絡(luò)就是一種輔助雙方或者多方能夠連接在一起的工具 ·如果沒有網(wǎng)絡(luò)可想單機(jī)的世界是多么的孤單 2.使用網(wǎng)絡(luò)的目的 ...
    chen_000閱讀 222評(píng)論 0 1
  • 計(jì)算機(jī)網(wǎng)絡(luò)概述 網(wǎng)絡(luò)編程的實(shí)質(zhì)就是兩個(gè)(或多個(gè))設(shè)備(例如計(jì)算機(jī))之間的數(shù)據(jù)傳輸。 按照計(jì)算機(jī)網(wǎng)絡(luò)的定義,通過一定...
    蛋炒飯_By閱讀 1,370評(píng)論 0 10

友情鏈接更多精彩內(nèi)容