Python高級 -- 02 網絡(udp、tcp)

一、socket


1、socket簡介


        socket(簡稱 套接字) 是進程間通信的一種方式,它與其他進程間通信的一個主要不同是:它能實現不同主機間的進程間通信,我們網絡上各種各樣的服務大多都是基於 Socket 來完成通信的


2、創建socket


在python中,使用socket模塊的函數可以完成:

import socket

"""
	函數:socket.socket(AddressFamily, Type)
	創建一個socket,該函數有兩個參數:
		AddressFamily:可以選擇AF_INET(用於Internet進程間通信)或者AF_UNIX(用於同一臺機器進程間通信),實際工作中常用AF_INET
		Type:套接字類型,可以是SOCKET_STREAM(流式套接字,主要用於TCP協議)或者SOCK_DGRAM(數據報套接字,主要用於UDP協議)
"""

3、udp發送數據


(1)、ubuntu桌面編寫python代碼




(2)、使用命令運行代碼




(3)、在虛擬機xp系統中查看是否接收到數據




注意:xp中用到的接收軟件在本博客資料中


http://download.csdn.net/download/wingzhezhe/10226876


4、練習:發送帶有退出功能的可多次發送的udp數據




5、接收udp數據


(1)、ubuntu中使用udp接收數據的代碼



import socket
 
 
 def main():
     # 1.創建套接字
     udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
     # 2.給當前套接字對象綁定一個ip和端口號
     #   使用的函數是 :bind((ip, port))   bind的參數是一個元組
     #       ip :當前本機的ip,一般不用寫,用""代替,代表本機ip
     #       port : 給當前套接字對象指定端口號
     local_info = ("", 8888)
     udp_socket.bind(local_info)
 
     # 3.接收數據 : 使用函數recvfrom(args)   參數args表示本次接收的最大字節數
     """
         接收的數據是一個元組類型的 : (字節類型的接收到的內容, (發送方的ip, 發送方的port))
             需要使用 decode(args) 方法將接收的字節類型的數據進行轉碼
     """
     recv_data = udp_socket.recvfrom(1024)
     print(type(recv_data))
 
     # 對接收的數據進行處理
     recv_content = recv_data[0].decode("gbk")   # window中默認編碼時gbk,因此轉碼也要使用gbk
     recv_info = recv_data[1]
 
     # 4.打印數據
     print("接收到的數據內容是:%s \n 發送方的信息是:%s" % (recv_content, recv_info))
 
     # 關閉套接字
     udp_socket.close()
 
 
 if __name__ == "__main__":
     main()


(2)、運行程序,演示操作




6、使用同一個套接字首發upd數據


(1)、在ubuntu系統中安裝網路調試助手


(2)、編寫代碼



def main():
     # 1.創建一個套接字對象
     udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
     # 2.獲取對方的ip/port
     dest_ip = input("請輸入對方的ip:")
     dest_port = int(input("請輸入對方的port:"))
 
     # 3.從鍵盤獲取數據
     content = input("請輸入要發送的內容:")
 
     # 4.使用套接字發送數據
     udp_socket.sendto(content.encode("utf-8"), (dest_ip, dest_port))
 
     # 接收對方發送的套接字數據
     recv = udp_socket.recvfrom(1024)
     print(recv)
     
     # 5.關閉套接字
     udp_socket.close()
 
 
 if __name__ == "__main__":
     main()

(3)、測試運行




7、使用udp實現簡易聊天室




import socket

def send_msg(udp_socket):
    """發送數據"""
    dest_ip = input("請輸入目的地ip:")
    dest_port = int(input("請輸入目的地port:"))
    dest_content = input("請輸入要發送的內容:")
    # 發送數據
    udp_socket.sendto(dest_content.encode("utf-8"), (dest_ip, dest_port))


def recv_msg(udp_socket):
    """接收數據"""
    recv_data = udp_socket.recvfrom(1024)
    print("端口號和ip爲:%s 發送的數據是:%s  " % (recv_data[1], recv_data[0].decode("utf-8")))



def main():
    # 1.創建套接字對象
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 2.給套接字綁定ip和端口號
    udp_socket.bind(("",8888))
    
    # 3.使用循環來實現收發功能
    while True:
        print("---------------XXX聊天室---------------")
        opt = input("請輸入您要進行的操作:1(發送消息)  2(接收消息)   3(退出系統)")
        if opt == "1":
            send_msg(udp_socket)
        elif opt == "2":
            recv_msg(udp_socket)
        elif opt == "3":
            break
        else:
            print("輸入有誤")
    
    # 4.關閉套接字
    udp_socket.close()


if __name__ == "__main__":
    main()

測試運行:




二、tcp


1、tcp協議介紹


TCP協議,傳輸控制協議(英語:Transmission Control Protocol,縮寫爲 TCP)是一種面向連接的、可靠的、基於字節流的傳輸層通信協議,由IETF的RFC 793定義。

TCP通信需要經過創建連接、數據傳送、終止連接三個步驟。、

TCP通信模型中,在通信開始之前,一定要先建立相關的鏈接,才能發送數據


2、Tcp的特點


(1)、面向連接


通信雙方必須先建立連接才能進行數據的傳輸,雙方都必須爲該連接分配必要的系統內核資源,以管理連接的狀態和連接上的傳輸。

雙方間的數據傳輸都可以通過這一個連接進行。

完成數據交換後,雙方必須斷開此連接,以釋放系統資源。

這種連接是一對一的,因此TCP不適用於廣播的應用程序,基於廣播的應用程序請使用UDP協議。


(2)、可靠傳輸


1)TCP採用發送應答機制


TCP發送的每個報文段都必須得到接收方的應答才認爲這個TCP報文段傳輸成功


2)超時重傳


發送端發出一個報文段之後就啓動定時器,如果在定時時間內沒有收到應答就重新發送這個報文段。

TCP爲了保證不發生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然後接收端實體對已成功收到的包發回一個相應的確認(ACK);如果發送端實體在合理的往返時延(RTT)內未收到確認,那麼對應的數據包就被假設爲已丟失將會被進行重傳。


(3)、錯誤校驗


TCP用一個校驗和函數來檢驗數據是否有錯誤;在發送和接收時都要計算校驗和。


4) 流量控制和阻塞管理


流量控制用來避免主機發送得過快而使接收方來不及完全收下。


TCP與UDP的不同點


        面向連接(確認有創建三方交握,連接已創建才作傳輸。)

        有序數據傳輸

        重發丟失的數據包

       捨棄重複的數據包

       無差錯的數據傳輸

       阻塞/流量控制


3、tcp通信模型




4、使用tcp客戶端發送數據



import socket


def main():
    # 1.創建tcp套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2.鏈接服務器
    server_ip = input("請輸入要鏈接的服務器的ip:")
    server_port = int(input("請輸入服務器的ip:"))
    server_info = (server_ip, server_port)
    tcp_socket.connect(server_info)

    # 3.發送數據
    send_data = input("請輸入要發送的內容:")
    tcp_socket.send(send_data.encode("utf-8"))

    # 4.關閉套接字
    tcp_socket.close()


if __name__ == "__main__":
    main()



5、tcp服務器


(1)、創建服務器流程


        socket創建一個套接字

        bind綁定ip和port

        listen使套接字變爲可以被動鏈接

        accept等待客戶端的鏈接

        recv/send接收發送數據


(2)、創建tcp服務器demo



def main():
    # 1.創建tcp套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	
    # 2.綁定本地信息
    tcp_server_socket.bind(("", 8888))
	
    # 3.讓默認的套接字由主動變爲被動
    tcp_server_socket.listen(128)
    print("----------程序啓動,等待客戶端鏈接------------")
	
    """
        4.等待客戶端的鏈接,返回值是一個元組(client_socket, client_addr)
            步驟1創建的套接字對象只負責等待客戶端進行鏈接,如果有鏈接,服務端就創建一個套接字,用來爲客戶端服務
            client_socket : 服務器創建的套接字對象,用來爲客戶端服務
            client_addr : 存放的是客戶端的信息,是一個元組(client_ip, client_port)
    """
    client_socket, client_addr = tcp_server_socket.accept()
    print("-----------接收到客戶端鏈接-----------")
    print(client_addr)
	
    # 5.接收客戶端發送過來的數據,返回值就是一個字節數據
    recv_data = client_socket.recv(1024)
    print(recv_data)
	
    # 6.服務器接收到數據後,給客戶端返回數據
    client_socket.send("數據已經收到".encode("utf-8"))
	
    # 7.關閉套接字
    client_socket.close()
    tcp_server_socket.close()
	
	
if __name__ == "__main__":
    main()



(3)、實現tcp服務端循環爲多個客戶端服務






(4)、實現tcp服務器循環爲多個客戶服務,並且多次爲一個客戶提供服務






6、模擬下載文件


(1)、tcp服務端代碼

import socket
 
 
def send_file_2_client(client_socket, client_addr):

    # 1.接收客戶端發過來的信息(要下載的文件名)
    file_name = client_socket.recv(1024).decode("utf-8")
    print("客戶端要下載的文件名爲:%s" % file_name)

    # 2. 打開文件,讀取文件內容
    file_content = None
    try:
        f = open(file_name, "rb")
        file_content = f.read()
        f.close()
    except Exception as e:
        print("沒有要下載的文件:%s" % file_name)

    # 3.發送數據給客戶端
    if file_content:
        client_socket.send(file_content)


def main():
    # 1.創建服務端的套接字對象
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2.綁定服務端信息
    tcp_server_socket.bind(("", 8888))

    # 3.讓默認的套接字由主動變爲被動(listen)
    tcp_server_socket.listen(128)

    # 讓下載程序爲多個客戶端服務
    while True:
        # 4.等待客戶端的鏈接
        client_socket, client_addr = tcp_server_socket.accept()

        # 5.調用方法,讀取文件內容,併發送給客戶端
        send_file_2_client(client_socket, client_addr)


        # 關閉套接字
        client_socket.close()

    # 6.關閉套接字
    tcp_server_socket.close()


if __name__ == "__main__":
    main()    main()

(2)、tcp客戶端代碼

import socket
 
 
 def main():
     # 1.創建tcp套接字對象
     tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
     # 2.獲取服務器的ip和port
     server_ip = input("請輸入服務器的ip:")
     server_port = int(input("請輸入服務器的port:"))
 
     # 3.鏈接服務器
     tcp_socket.connect((server_ip, server_port))
 
     # 4.獲取要下載的文件的名字
     download_file_name = input("請輸入要下載的文件名:")
 
     # 5.將文件名發送到服務器
     tcp_socket.send(download_file_name.encode("utf-8"))
 
     # 6.接收從服務器中發送多來的文件中的數據,參數是最大接收的字節數,1024代表1K
     recv_data = tcp_socket.recv(1024)
 
     """
         7.保存數據到一個文件中
         
         with open(fileName, option) as f:
 
         以上代碼的作用 :在文件能打開的前提下,替代了使用try...except的時候,
         如果拋出異常,需要手動關閉文件流的操作,
         即,使用以上寫法,如果在操作過程中,出現異常,系統自動會關閉文件流
         如果沒有出現異常,也會在操作結束的時候自動關閉文件流
     """
     if recv_data:
         # 如果文件內容不是空,才進行下載
         with open("[下載]" + download_file_name, "wb") as f:
             f.write(recv_data)
 
     # 8.關閉套接字
     tcp_socket.close()
 
 
 if __name__ == "__main__":
 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章