一、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__":