網絡編程基礎【day08】:socke編程入門
本節內容
- OSI七層模型
- socke通信介紹
- 簡單socket實例
- 通過socket實現簡單ssh客戶端
OSI七層模型
socke通信介紹
一、概述
socket通常也稱作"套接字",用於描述IP地址和端口,是一個通信鏈的句柄,應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求。
socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,對於文件用【打開】【讀寫】【關閉】模式來操作。socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)
二、關系圖
- file模塊是針對某個指定文件進行【打開】【讀寫】【關閉】
- socket模塊是針對 服務器端 和 客戶端Socket 進行【打開】【讀寫】【關閉】
三、代碼邏輯圖
四、socket概念
4.1 Socket Families(地址簇)
socket.
AF_UNIX unix本機進程間通信
socket.
AF_INET IPV4
socket.
AF_INET6 IPV6
4.2 Socket Types
socket.
SOCK_STREAM #for tcp
socket.
SOCK_DGRAM #for udp
socket.
SOCK_RAW #原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。
socket.
SOCK_RDM #是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用。
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() #關閉服務端
|
三、總結
- python2中可客戶端或者服務端均可發送字符串和字節,但是在python3中只能發字節,也就bytes類型的比特流
- 服務端接收客戶端的鏈接時,會返回跟客戶端的鏈接標誌位的實例和對方ip地址+隨機端口號
- 此例子只是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編程入門