python 網絡編程(socketserver,阻塞,其他方法)


重點回顧:

 

(重點)粘包 :  就是因為接收端不知道如何接收數據,造成接收數據的混亂的問題
      只發生在tcp協議上. 因為tcp協議的特點是面向數據流形式的傳輸
      粘包的發生主要是因為tcp協議有兩個機制: 合包機制(nagle算法),拆包機制
    
    subprocess 模塊 有一個方法可以執行系統命令 Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    
    struct 模塊  有一個方法可以將21.3E以內的數據,打包成4個長度的bytes
    r = struct.pack('i',num)
    struct.unpack('i',r)
    

  (重點)架構: C/S架構   B/S架構(優點:統一了應用的接口)
  
    多台電腦通信 : 交換機+路由器
    
    mac地址: 物理地址,全球唯一        身份證號
    ip地址 : 虛擬地址,四位點分十進制  學號
    
  (重點)如何判斷兩台主機是否在同一個局域網?
        ip地址 & 子網掩碼  = 網段
    
  (重點)arp協議: 通過目標ip地址,獲取目標mac地址
    
    端口:操作系統給予,通過端口號,可以確定某一個應用程序
  (重點)ip+端口:唯一確定某一個主機上的某一個應用程序
    回環地址:127.0.0.1
    
    osi五層模型:
       應用層       py文件,應用
       傳輸層       tcp/udp協議
       網絡層       ip協議
       數據鏈路層   arp協議,網卡
       物理層       網線,集線器,光纖
    
    (重點)tcp協議:安全,可靠,面向連接,面向數據流形式的傳輸
       三次握手:
         (面試回答)
         首先,必須先由客戶端發起連接的請求
         接下來,服務器接收到請求之后,回復給客戶端兩個標識,一個syn表示
            服務器接收到請求,一個ack表示服務器在做准備工作,兩個標識一起
            回復給客戶端
         最后,客戶端接收到服務器的回復,客戶端准備連接的所有資源,開始進行連接
         發送給服務器一個ack表示客戶端的連接准備工作已經完成
         (此時表示客戶端和服務器可以相互連接了)
         如果面試官問你,哪句代碼體現了三次握手? 
           回答: 服務器端的accept,客戶端connect
           
       四次揮手:
         (面試回答)
          (1)首先由連接雙方任意一方發起斷開連接的請求,發起方發送的請求表示
          是我沒有數據要繼續發送了,可以斷開連接了,但是你如果還有數據可以繼續向我發送數據.
          (2)接收方回復給發起方,表示接到了發起放的斷開請求,開始着手准備斷開事宜
          (3)接收方准備完成后,給發起方發送一個標識,表示接受方沒有數據繼續發送了
             可以斷開連接了
          (4)發起方接收到消息后,准備斷開連接,回收資源
          如果面試官問你,哪句代碼體現了四次揮手?
            回答: close()
    
    (重點)udp協議:快. 不安全,不可靠,不面向連接,面向數據包形式的傳輸

 

官方文檔對socket模塊下的socket.send()和socket.sendall()解釋如下:

socket.send(string[, flags])
Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data.

send()的返回值是發送的字節數量,這個數量值可能小於要發送的string的字節數,也就是說可能無法發送string中所有的數據。如果有錯誤則會拋出異常。

–

socket.sendall(string[, flags])
Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from string until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

嘗試發送string的所有數據,成功則返回None,失敗則拋出異常。

故,下面兩段代碼是等價的:

#sock.sendall('Hello world\n')

#buffer = 'Hello world\n'
#while buffer:
#    bytes = sock.send(buffer)
#    buffer = buffer[bytes:]

send和sendall方法
send與sendall官方文檔

 

 ----------socket的更多方法介紹---------------

 

服務端套接字函數
s.bind()    綁定(主機,端口號)到套接字
s.listen()  開始TCP監聽
s.accept()  被動接受TCP客戶的連接,(阻塞式)等待連接的到來

客戶端套接字函數
s.connect()     主動初始化TCP服務器連接
s.connect_ex()  connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的套接字函數
s.recv()            接收TCP數據
s.send()            發送TCP數據
s.sendall()         發送TCP數據
s.recvfrom()        接收UDP數據
s.sendto()          發送UDP數據
s.getpeername()     連接到當前套接字的遠端的地址
s.getsockname()     當前套接字的地址
s.getsockopt()      返回指定套接字的參數
s.setsockopt()      設置指定套接字的參數
s.close()           關閉套接字

面向鎖的套接字方法
s.setblocking()     設置套接字的阻塞與非阻塞模式
s.settimeout()      設置阻塞套接字操作的超時時間
s.gettimeout()      得到阻塞套接字操作的超時時間

面向文件的套接字的函數
s.fileno()          套接字的文件描述符
s.makefile()        創建一個與該套接字相關的文件
更多方法

 

sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 允許一個端口號重用的解決方法,同時開啟多個服務端.
(重點)setblocking(True)  阻塞
   setblocking(False) 非阻塞
   settimeout(int)    針對阻塞狀態,設置一個延時等待
   gettimeout()       獲得延時的時間

 

 

 

驗證客戶端鏈接的合法性

如果你想在分布式系統中實現一個簡單的客戶端鏈接認證功能,又不像SSL那么復雜,那么利用hmac+加鹽的方式來實現

# 此代碼用真實的隨機字符串
# import socket
# import hashlib
# import os
# sk = socket.socket()
# sk.bind(('127.0.0.1',9090))
# sk.listen()
# conn,addr = sk.accept()
# key = '天王蓋地虎'# 這個是固定鹽
# ch = os.urandom(10)
# conn.send(ch)# 把隨機字符串發給client
# md5_obj = hashlib.md5(key.encode('utf-8'))
# md5_obj.update(ch)
# re = md5_obj.hexdigest()
# # 固定的用鹽的加密方式
# client_re = conn.recv(1024).decode('utf-8')# 接收client端加密后的結果
#
# if re == client_re:
#     print('你好機油!')
#     '''收發數據的邏輯'''
# else:
#     print('你根本不是老司機')
#     conn.close()
# sk.close()
服務端
# import socket
# import hashlib
# sk = socket.socket()
# sk.connect(('127.0.0.1',9090))
#
# key = '天王蓋地虎'
# ch = sk.recv(1024)
# md5_obj = hashlib.md5(key.encode('utf-8'))
# md5_obj.update(ch)
# re = md5_obj.hexdigest()
# sk.send(re.encode('utf-8'))
#
# sk.close()
客戶端

用hmac驗證:

# 調用hmac模塊中的加密方法
import socket
import hashlib
import os
import hmac
sk = socket.socket()
sk.bind(('127.0.0.1',9090))
sk.listen()
conn,addr = sk.accept()
key = '天王蓋地虎'# 這個是固定鹽
ch = os.urandom(10)
conn.send(ch)# 把隨機字符串發給client
obj = hmac.new(key.encode('utf-8'),ch)
re = obj.digest()
# 固定的用鹽的加密方式
client_re = conn.recv(1024)# 接收client端加密后的結果

if re == client_re:
    print('你好機油!')
    '''收發數據的邏輯'''
else:
    print('你根本不是老司機')
    conn.close()
sk.close()
服務端
# 此代碼用hmac模塊實現機密
import socket
import hashlib
import hmac
sk = socket.socket()
sk.connect(('127.0.0.1',9090))

key = '天王蓋地虎'
ch = sk.recv(1024)
obj = hmac.new(key.encode('utf-8'),ch)
re = obj.digest()
sk.send(re)

sk.close()
客戶端

 

---------------socketserver-----------------

# import socketserver
# #sk  conn 等效於 self.requset.
# class Myserver(socketserver.BaseRequestHandler):
#     def handle(self):
#         # print(123)
#         # self.request.send()
#         print(self.request.recv(1024).decode('utf-8'))
#
# server = socketserver.TCPServer(('192.168.19.200',9090),Myserver)
#
# server.serve_forever()

# ========================================
import socketserver
#sk  conn
import json
import hashlib
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        while 1:
            dic_str = self.request.recv(1024).decode('utf-8')# 接收到序列化的字典
            dic = json.loads(dic_str)# 反序列化字典,字典中有用戶名和密碼
            # 用戶名當鹽   加上密碼去做md5
            with open('info',encoding='utf-8') as f:
                for user_info in f:# 旭哥 | 7d79a61dd0bd94a3df2f765ac12fe492
                    username,pawd = user_info.split('|')
                    if username.strip() == dic['username']:# 先去對比用戶名正確與否
                        '''如果用戶名存在的情況下
                         加密方式 : 用戶名當鹽,對密碼加密'''
                        md5_obj = hashlib.md5(dic['username'].encode('utf-8'))
                        md5_obj.update(dic['passwd'].encode('utf-8'))
                        re = md5_obj.hexdigest()
                        if re == pawd.strip():# 拿加密完的密碼密文對比文件中密碼的密文
                            self.request.send(b'success!')
                            '''通信的邏輯'''
                        else:
                            '''失敗,返回給客戶端信息.....'''
                            self.request.send(b'failed!')
                        break
                else:
                    '''對應for   如果用戶名不存在'''
                    print('用戶不存在!')

server = socketserver.TCPServer(('127.0.0.1',9090),Myserver)# 綁定一個服務
server.serve_forever()# 永久性開啟服務
服務端
import socket
import time
import json
sk = socket.socket()
sk.connect(('127.0.0.1',9090))
dic = {'username':None,'passwd':None}
while 1:
    username = input('用戶名>>>')
    passwd = input('密碼>>>')
    dic['username'] = username
    dic['passwd'] = passwd
    dic_str = json.dumps(dic)
    sk.send(dic_str.encode('utf-8'))
    print(sk.recv(1024))



sk.close()
客戶端

handle方法內寫收發邏輯,   conn  ==   self.requset.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM