1. 程式人生 > >網絡編程基礎【day08】:socke編程入門

網絡編程基礎【day08】:socke編程入門

decode 無限 key 註意 實現簡單 int python3 管理 cep

本節內容

  1. OSI七層模型
  2. socke通信介紹
  3. 簡單socket實例
  4. 通過socket實現簡單ssh客戶端

OSI七層模型

技術分享

socke通信介紹

一、概述   

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

  socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,對於文件用【打開】【讀寫】【關閉】模式來操作。socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)

二、關系圖

  • file模塊是針對某個指定文件進行【打開】【讀寫】【關閉】
  • socket模塊是針對 服務器端 和 客戶端Socket 進行【打開】【讀寫】【關閉】

技術分享

三、代碼邏輯圖

技術分享

四、socket概念

4.1 Socket Families(地址簇)

  1. socket.AF_UNIX unix本機進程間通信
  2. socket.AF_INET IPV4 
  3. socket.AF_INET6 IPV6

4.2 Socket Types

  1. socket.SOCK_STREAM #for tcp
  2. socket.SOCK_DGRAM #for udp
  3. socket.SOCK_RAW #原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。
  4. socket.SOCK_RDM #是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用。
  5. socket.SOCK_SEQPACKET #廢棄了

簡單socket實例

一、概述

  之前我們只是介紹了soket的概念和一些邏輯圖表,下面我們來看看,socket的客戶端和服務端到底是怎麽用的?

二、socket實例

2.1 客戶端

2.1.1 客戶端代碼邏輯圖

技術分享

2.1.2 客戶端代碼

1 2 3 4 5 6 7 8 9 10 import socket #導入socket模塊 client = socket.socket() #創建socket實例 client.connect(("localhost",6969)) #建立連接 send_data = "hello word!" #發送的字符串 send_data = send_data.encode() #因為發送是bytes類型,所以這邊先轉碼成bytes類型 client.send(send_data) #發送數據,這邊發送的是字節類型,也就是bytes類型 data = client.recv(1024) #接收服務端的數據,這邊設置接收1024字節 1kb=1024字節 print("server rece:",data.decode()) client.close() #關閉與服務端的鏈接

2.2 服務端

2.2.1 服務端代碼邏輯圖

技術分享

2.2.2 服務端代碼

1 2 3 4 5 6 7 8 9 10 11 12 import socket sever = socket.socket() #創建服務端實例 sever.bind(("localhost",6969)) #綁定客戶端ip和端口 sever.listen() #監聽端口 print("我在電話了....") conn,addr = sever.accept() #接收客戶端,並且返回連接標誌位(conn)實例,和對方的ip地址(addr) data = conn.recv(1024) #接收客戶端發過來的數據,接收的也是bytes類型的數據 print("我的電話來了") print("client data:",data.decode()) conn.send(data.upper()) #發送數據至服務端,註意這邊發送的也是字節類型,是bytes類型 sever.close() #關閉服務端

三、總結

  1. python2中可客戶端或者服務端均可發送字符串和字節,但是在python3中只能發字節,也就bytes類型的比特流
  2. 服務端接收客戶端的鏈接時,會返回跟客戶端的鏈接標誌位的實例和對方ip地址+隨機端口號
  3. 此例子只是TCP/IP協議的socket鏈接,後面繼續學習UDP的鏈接

通過socket實現簡單ssh客戶端

一、概述

  本篇博客講一下,如果socket客戶端斷了,另外的客戶端怎麽接入服務端,還有模擬ssh的鏈接等。

二、socket發送中文

  因為在python 3中只能接受bytes類型的數據,bytes類型只能接受ASCII碼裏面的數據類型。因為bytes類型是一個ASCII 0-255的數字組合。所以在客戶端向服務端傳中文時一定要先轉成bytes類型,也就是encode(),接收方需要解碼,也就是decode()才能識別中文。

2.1、發送中文代碼

①客戶端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 import socket client = socket.socket() client.connect(("localhost",6969)) msg = "小高最帥了" client.send(msg.encode()) #傳中文至服務端,需要先編碼 server_data = client.recv(1024) print("recv:",server_data) #未解碼 print("recv:",server_data.decode()) #解碼 client.close() #輸出 recv: b‘\xe5\xb0\x8f\xe9\xab\x98\xe6\x9c\x80\xe5\xb8\x85\xe4\xba\x86‘ #bytes類型 recv: 小高最帥了 #字符串

②服務端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import socket sever = socket.socket() sever.bind(("127.0.0.1",6969)) #綁定ip地址和端口 sever.listen() #監聽 conn,address = sever.accept() #獲取接收實例和ip地址 print("電話來了") client_data = conn.recv(1024) #接收客戶端數據 print("recv:",client_data) print("recv:",client_data.decode()) conn.send(client_data) #發送給客戶端 sever.close() #輸出 電話來了 recv: b‘\xe5\xb0\x8f\xe9\xab\x98\xe6\x9c\x80\xe5\xb8\x85\xe4\xba\x86‘ #bytes類型 recv: 小高最帥了 #解碼後的結果

註意了:所有的數據發送和接收都用bytes類型就可以了,省的有什麽異常情況。

三、重復發送和多次接收

  上面的代碼只能客戶端只能發送一次,服務端接收一次,就這麽結束了,感覺很不爽,那怎麽實現客戶端發送多次,服務端接收多次吶?

3.1、重復發送和多次接收代碼

①客戶端

說明:客戶端在發送處設置死循環(while True),實現重復發送。

1 2 3 4 5 6 7 8 9 10 11 12 import socket client = socket.socket() client.connect(("localhost",6969)) while True: #進入死循環,設置無限次發送 msg = input(">>>:") client.send(msg.encode()) data = client.recv(1024) print("recv:",data.decode()) client.close()

②服務端

說明:服務端在接收時,設置死循環,實現重復接收。

1 2 3 4 5 6 7 8 9 10 11 12 13 import socket sever = socket.socket() sever.bind(("127.0.0.1",6969)) sever.listen() conn,address = sever.accept() #接收連接實例 print("電話來了") while True: #設置死循環,接收多次 data = conn.recv(1024) print("recv:",data.decode()) conn.send(data) sever.close()

註:這邊註意了,在服務端,while True千萬不能寫在conn,address = sever.accept()前面,這個是為什麽呢?因為客戶端跟服務端只能實現建立一個連接,如果你把while True放在前面,則服務端接收數據後,又要重新建立一個新的等待連接,這樣,客戶端和服務端都會卡主。

服務端的代碼如下:

技術分享

表現現象:

技術分享技術分享

3.2、處理多個鏈接

說明:我們在客戶端一起鏈接服務端,我們都知道,一個服務端只能跟一個客戶端進行鏈接通信,那如果說,我這個正在通信的客戶端斷開跟服務端的通信,那其他的某個客戶端就能跟客戶端正常通信了,這個實驗一定要在Linux服務器上去完成,因為在Windows上就是只要客戶端一斷開,服務端就斷開了。

跟上面一樣,客戶端的代碼沒有變,我們現在來變一下服務端的代碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import socket sever = socket.socket() sever.bind(("127.0.0.1",6969)) sever.listen() while True: #在建立連接之前加一個死循環 conn,address = sever.accept() print("電話來了")   count = 0 #加一個計數器 while True: data = conn.recv(1024)      if not data:break #這邊如果接受客戶端數據為空,則重新建立連接 print("recv:",data.decode()) conn.send(data)      count += 1 sever.close()

註意:上面if not data:break這段代碼不寫的後果是:當客戶端斷開鏈接時,服務端進入死循環,不斷接收客戶端的空數據。

現象如圖:

①客戶端

技術分享

②服務端

技術分享

3.3、 客戶端發送數據為空

說明:我們之前演示都是客戶端輸入內容,服務端給出相應,那客戶端輸入的是空的話,服務端會有什麽反映吶?

客戶端代碼如圖:

技術分享

服務端代碼跟上面的一樣,執行結果如下:

客戶端:

技術分享

服務端:

技術分享

原因是:客戶端輸入的為空,服務端還一直以為客戶端在send,然後都卡主了。

那代碼怎麽改進呢?代碼改進如下:

客戶端代碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 import socket client = socket.socket() client.connect(("localhost",6969)) while True: msg = input(">>>:") if len(msg) == 0:continue #這邊判斷輸入的字符是否為空,為空就跳過 client.send(msg.encode()) data = client.recv(1024) print("recv:",data.decode()) client.close()

表現現象:

客戶端:

技術分享

服務端:

技術分享

四、模擬ssh客戶端

4.1、模擬ssh訪問

說明:以下代碼,是在Linux下環境,而且還是python2.7的環境,如果是python 3的話,需要編碼和解碼。

①客戶端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #! /usr/bin/env python # -*- coding:utf-8 -*- import socket client = socket.socket() client.connect(("localhost",6969)) while True: msg = raw_input(">>>:") if len(msg) == 0:continue client.send(msg) data = client.recv(1024) print(data) client.close()

②服務端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #! /usr/bin/env python # -*- coding:utf-8 -*- import socket,os #導入os模塊 sever = socket.socket() sever.bind(("127.0.0.1",6969)) sever.listen(5) #最大允許有多少個鏈接 while True: conn,address = sever.accept() print("電話來了") count = 0 while True: data = conn.recv(1024) if not data:break res = os.popen(data).read() #調用linux命令 conn.send(res) #執行的命令返回值 sever.close()

註:conn.send(res)這邊如果需要發送全部的話,需要conn.sendall(res)

4.2、下載文件

①客戶端

1 2 3 4 5 6 7 8 9 10 11 12 13 import socket client = socket.socket() client.connect(("localhost",6969)) while True: msg = input(">>>:") if len(msg) == 0:continue client.send(msg.encode()) data = client.recv(1024000) #這邊設置的大一點,防止文件的內容接收不到 with open("test_put","wb") as test_put_file: #把下載下來的內容寫入到文件中 test_put_file.write(data) client.close()

②服務端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import socket sever = socket.socket() sever.bind(("127.0.0.1",6969)) sever.listen() conn,address = sever.accept() print("電話來了") while True: data = conn.recv(1024) if not data: print("數據為空") break with open("test","rb") as test_file: all_data_bytes = test_file.read() #讀取需要下載的文件,發送給客戶端 conn.sendall(all_data_bytes) sever.close()

註意:這邊客戶端的接收時有限制的,如果超出了客戶端的限制,客戶端只接收自己的一部分,而剩余的會在還是在緩沖去,下一次服務端再send的時候,不會發新數據,先把緩沖區剩下的數據發送到客戶端。

如圖:

技術分享

網絡編程基礎【day08】:socke編程入門