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+加鹽的方式來實現shell

# 此代碼用真實的隨機字符串
# 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驗證:json

# 調用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.服務器

相關文章
相關標籤/搜索