1. 程式人生 > >19 網絡編程--Socket 套接字方法

19 網絡編程--Socket 套接字方法

描述符 兩種 元組 最大數 門面 blocking 需要 地址 3次握手

1、Socket(也稱套接字)介紹

socket這個東東幹的事情,就是幫你把tcp/ip協議層的各種數據封裝啦、數據發送、接收等通過代碼已經給你封裝好了

,你只需要調用幾行代碼,就可以給別的機器發消息了。

參考https://www.cnblogs.com/weizhixiang/p/6298523.html

2、套接字

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

套接字可以根據通信性質分類,這種性質對於用戶是可見的。應用程序一般僅在同一類的
套接字間進行通信。不過只要底層的通信協議允許,不同類型的套接字間也照樣可以通信。套

接字有兩種不同的類型:流套接字和數據報套接字。

3、套接字的工作原理

要通過互聯網進行通信,你至少需要一對套接字,其中一個運行於客戶機端,我們稱之為
ClientSocket,另一個運行於服務器端,我們稱之為ServerSocket。
根據連接啟動的方式以及本地套接字要連接的目標,套接字之間的連接過程可以分為三個
步驟:服務器監聽,客戶端請求,連接確認。

所謂服務器監聽,是服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的
狀態,實時監控網絡狀態。
所謂客戶端請求,是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接
字。為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的

地址和端口號,然後就向服務器端套接字提出連接請求。
所謂連接確認,是指當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求,它
就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦
客戶端確認了此描述,連接就建立好了。而服務器端套接字繼續處於監聽狀態,繼續接收其他
客戶端套接字的連接請求。

4、網絡編程的解釋

網絡編程的目的就是指直接或間接地通過網絡協議與其他計算機進行通訊。網絡編程中
有兩個主要的問題,一個是如何準確的定位網絡上一臺或多臺主機,另一個就是找到主機後
如何可靠高效的進行數據傳輸。在TCP/IP協議中IP層主要負責網絡主機的定位,數據傳輸的

路由,由IP地址可以唯一地確定Internet上的一臺主機。而TCP層則提供面向應用的可靠的
或非可靠的數據傳輸機制,這是網絡編程的主要對象,一般不需要關心IP層是如何處理數據
的。

目前較為流行的網絡編程模型是客戶機/服務器(C/S)結構。即通信雙方一方作為服務
器等待客戶提出請求並予以響應。客戶則在需要服務時向服務器提出申請。服務器一般作為
守護進程始終運行,監聽網絡端口,一旦有客戶請求,就會啟動一個服務進程來響應該客
戶,同時自己繼續監聽服務端口,使後來的客戶也能及時得到服務。

在Internet上IP地址和主機名是一一對應的,通過域名解析可以由主機名得到機器的IP,
由於機器名更接近自然語言,容易記憶,所以使用比IP地址廣泛,但是對機器而言只有IP地
址才是有效的標識符。

通常一臺主機上總是有很多個進程需要網絡資源進行網絡通訊。網絡通訊的對象準確的講
不是主機,而應該是主機中運行的進程。這時候光有主機名或IP地址來標識這麽多個進程顯然
是不夠的。端口號就是為了在一臺主機上提供更多的網絡資源而采取得一種手段,也是TCP層
提供的一種機制。只有通過主機名或IP地址和端口號的組合才能唯一的確定網絡通訊中的對象:
進程。

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。

在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部。

技術分享圖片

5、編寫Socket套接字的流程

技術分享圖片

1、服務器端程序的編寫步驟:

(1)創建調用socket()函數創建一個用於通信的套接字。#phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

(2)s.bind() 綁定(主機,端口號)到套接字。 #phone.bind((‘127.0.0.1‘,8080))

(3)s.listen() 開始TCP監聽 。#phone.listen(5)# 5 代表最多掛機數

(4)s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來。# conn,client_addr = phone.accept()

(5)處理客戶端的連接請求。 # 1、s.recv() 接收數據 /data = conn.recv(1024) # 2、反饋給客戶端:s.send() 發送數據

(6)關閉套接字/ s.send() 發送數據

2、客戶端程序的編寫步驟:

(1)調用socket()函數創建一個用於通信的套接字。# phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

(2)s.connect() 主動初始化TCP服務器連接 # phone.connect((‘127.0.0.1‘,8080))

(3)調用讀寫函數發送或者接收數據。# s.send() 發送數據(send在待發送數據量大於己端緩存區剩余空間時,數據丟失,不會發完,可後面通過實例解釋)

例如:phone.send(‘hello‘.encode(‘utf-8‘))

(4)接受服務端的反饋信息: s.recv() 接收數據 # 例如:
data = phone.recv(1024)
(5)關閉sokect
技術分享圖片

技術分享圖片
參考:https://www.cnblogs.com/lixiaoliuer/p/6543968.html

② server.bind(address)

  server.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址

③ server.listen(backlog)

  開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量。backlog等於5,表示內核已經接到了連接請求,但服務器還沒有調用accept進行處理的連接個數最大為5,這個值不能無限大,因為要在內核中維護連接隊列

④ server.setblocking(bool)

  是否阻塞(默認True),如果設置False,那麽accept和recv時一旦無數據,則報錯

⑤ conn,addr = server.accept() 

  接受連接並返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。接收TCP 客戶的連接(阻塞式)等待連接的到來

⑥ client.connect(address)

  連接到address處的套接字。一般,address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。

⑦ client.connect_ex(address)

  同上,只不過會有返回值,連接成功時返回 0 ,連接失敗時候返回編碼,例如:10061

⑧ client.close()

  關閉套接字

⑨ client.recv(bufsize[,flag])

  接受套接字的數據。數據以字符串形式返回,bufsize指定最多可以接收的數量。flag提供有關消息的其他信息,通常可以忽略

⑩ client.recvfrom(bufsize[.flag])

  與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址

? server.send(string[,flag])

  將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容全部發送

? server.sendall(string[,flag])  

  將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常;

      內部通過遞歸調用send,將所有內容發送出去

? server.sendto(string[,flag],address)

  將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議

? sk.settimeout(timeout)

  設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因為它們可能用於連接的操作(如 client 連接最多等待5s )

? sk.getpeername()

  返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)

? sk.getsockname()

  返回套接字自己的地址。通常是一個元組(ipaddr,port)

? sk.fileno()

  套接字的文件描述符

6、代碼實例

6.1、簡單的套接字通信

服務端和客戶端在一個IDE下

服務端:

import socket

# 1 買手機
# phone 為套接字對象
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#(sock_stream 流)
# 2 綁定手機卡,127.0.0.1是回送地址,指本地機,一般用來測試使用。客戶端、服務端在一臺主機上
phone.bind((127.0.0.1,8081))# 端口 0-65535:0-1024給操作系統使用

# 3 開機
phone.listen(5)# 5 代表最多掛機數

# 4 等電話連接 # 在同一個IDE上執行客戶端、服務端,,先執行服務端後,在執行客戶端
# 直接執行客戶端會發生錯誤
print(------starting-------)
conn,client_addr = phone.accept() # accept()對於 connect()做的3次握手
# print(‘==========>‘)
# print(res)

# 5 收、發消息
data = conn.recv(1024) # 1024個字節 代表接受數據最大數,,單位bytes字節
print(客戶端數據,data)

conn.send(data.upper()) # 服務端回數據給客戶端
# 6 掛電話
conn.close()

# 7 關機
phone.close()

客戶端:

import socket

# 1 買手機
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#(sock_stream 流)

# 2 撥號,127.0.0.1是回送地址,指本地機,一般用來測試使用。客戶端、服務端在一臺主機上
phone.connect((127.0.0.1,8081))# 端口 0-65535:0-1024給操作系統使用

# 3 發、收 消息
phone.send(hello.encode(utf-8))
data = phone.recv(1024)
print(data)
# 4 關閉
phone.close()

服務端先執行,一直開著,然後再執行客戶端

6.2、加上鏈接循環

技術分享圖片

技術分享圖片

6.3、簡單的遠程執行命令程序開發

#-----------windows-----------------
#dir:查看某一個文件夾下的子文件名與子文件夾名
#ipconfig:查看本地網卡的ip信息
#tasklist:查看運行的進程

#----------linux---------------:
#ls
#ifconfig
#ps aux

#執行系統命令,並且拿到命令的結果
# import os
# res=os.system(‘xxxxlxxxs /‘)
# print(‘命令的結果是:‘,res)

import subprocess
obj=subprocess.Popen(xxxxxxls /,shell=True,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE)

print(obj)
print(stdout 1--->: ,obj.stdout.read().decode(utf-8))
# print(‘stdout 2--->: ‘,obj.stdout.read().decode(‘utf-8‘))
#print(‘stderr 1--->: ‘,obj.stderr.read().decode(‘utf-8‘))

服務端:

import socket
import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind((127.0.0.1,9901)) #0-65535:0-1024給操作系統使用
phone.listen(5)

print(starting...)
while True: # 鏈接循環
    conn,client_addr=phone.accept()
    print(client_addr)

    while True: #通信循環
        try:
            #1、收命令
            cmd=conn.recv(1024)
            if not cmd:break #適用於linux操作系統

            #2、執行命令,拿到結果
            obj = subprocess.Popen(cmd.decode(utf-8), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            #3、把命令的結果返回給客戶端
            print(len(stdout)+len(stderr))
            conn.send(stdout+stderr) #+是一個可以優化的點

        except ConnectionResetError: #適用於windows操作系統
            break
    conn.close()

phone.close()

客戶端:

import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect((127.0.0.1,9901))

while True:
    #1、發命令
    cmd=input(>>: ).strip() #ls /etc
    if not cmd:continue
    phone.send(cmd.encode(utf-8))

    #2、拿命令的結果,並打印
    data=phone.recv(1024)
    print(data.decode(‘gbk))# window用gbk

phone.close()

技術分享圖片



19 網絡編程--Socket 套接字方法