網路編程tcp協議與socket以及單例的補充 一、單例補充 實現單列的幾種方式 二、tcp協議以及粘包問題 理論知識 三、socket套接字 socket套接字初級版本 演示 socket套接字升級版本 socket套接字高級版本 socket套接字終級版本 四、粘包問題 粘包問題的出現以及幾種情 ...
網路編程tcp協議與socket以及單例的補充
一、單例補充
- 實現單列的幾種方式
#方式一:classmethod
# class Singleton:
#
# __instance = None
#
# @classmethod
# def singleton(cls):
#
# if not cls.__instance:
# cls.__instance = cls()
#
# return cls.__instance
#
# obj1 = Singleton.singleton()
# obj2 = Singleton.singleton()
# print(obj1)
# print(obj2)
# <__main__.Singleton object at 0x000002127F230D08>
# <__main__.Singleton object at 0x000002127F230D08>
#方式二: __new__
# class Singleton:
#
# __instance = None
#
# def __new__(cls, *args, **kwargs):
#
# if not cls.__instance:
# cls.__instance = object.__new__(cls)
#
# return cls.__instance
#
# obj1 = Singleton()
# obj2 = Singleton()
# print(obj1)
# print(obj2)
# <__main__.Singleton object at 0x00000257AAE10A88>
# <__main__.Singleton object at 0x00000257AAE10A88>
#方式三:裝飾器
# def singleton(cls): #cls---> Father
#
# __instance = {}
#
# def inner(*args, **kwargs):
# if cls not in __instance:
# obj = cls(*args, **kwargs)
# __instance[cls] = obj
#
#
# return __instance[cls]
# return inner
#
# @singleton
# class Father:
# pass
#
# print(Father())
# print(Father())
# <__main__.Father object at 0x000001F17EB21548>
# <__main__.Father object at 0x000001F17EB21548>
#方式四
先定義一個Singletoncls的py文件內容如下:
class Singletoncls:
pass
obj = Singletoncls()
# from Singletoncls import obj
# print(obj)
# from Singletoncls import obj
# print(obj)
# from Singletoncls import obj
# print(obj)
# <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
# <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
# <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
# 方式五:元類
二、tcp協議以及粘包問題
- 理論知識
傳輸層:
-tcp協議
-udp協議
埠(port):標識一臺電腦上的某一個軟體。
-0-1024:禁止使用,因為是操作系統在用
-8000---->以後接著用
- 以下的一些軟體的固定埠不要碰:
django:8000
mysql:3306
redis:6379
flask:5000
tomcat:8080
mongodb:27017
...
要想要傳輸數據,必須建立雙向通道
1、tcp協議:三次握手,四次揮手
-tcp協議建立雙向通道
- 三次握手,鍵連接:
1:客戶端向服務端發送建立連接的請求
2:服務端返回收到的請求信息給客戶端,並且發送往客戶端建立的請求
3:客戶端接收到服務端的請求,返回請求成功給服務端,完成雙向連接
- 反饋機制:
客戶端往服務端發送請求,服務端必須返迴響應
告訴客戶端收到請求了,並且將服務端的數據一併返回給客戶端
c--->s:一次請求,必須有一次響應
- 缺點:
- 洪水攻擊:
指的是通過偽造大量的請求,往對方伺服器發送請求
導致對方伺服器跟不上,以至於癱瘓。
Linux系統有個參數可以限制
- 半連接池listen:限制用戶在同一時間段內的訪問數量
- 四次揮手,斷開連接
1:客戶端向服務端發送斷開連接的請求
2:服務端返回收到請求的信息給客戶端
3:服務端確認所有的數據發送完成以後,再發送同意斷開連接的請求給客戶端
4:客戶端返回收到斷開連接的請求,給服務端
2、socket套接字通信:
- 什麼是socket?
socket是一個模塊,又稱套接字,用來封裝 互聯網協議(應用層以下的層)
- 為什麼要有socket?
socket可以實現 互聯網協議應用層已下的層的工作
- 提高開發效率
- 怎麼使用socket?
import socket
寫socket套接字:
client
sever
3、粘包問題
-1)問題:無法確認對方發送過來的數據的大小
-2)問題:在發送數據間隔短並且數據量小的情況下,會將所有數據一次性發送
解決:確認對方數據的大小
4、解決粘包問題(struct模塊)
-無論那一段發送數據
-客戶端
-1)先製作報頭,併發送(struct)
-2)發送真實數據
服務端
-1)接收報頭,並解包獲取 真實的數據長度
-2)根據真實數據長度 接收真實數據
recv(真實數據長度)
三、socket套接字
- socket套接字初級版本
- 演示
-sever:
'''
先啟動套接服務端
'''
import socket
#買手機
sever = socket.socket()
#綁定手機卡
sever.bind(
('127.0.0.1', 9876)
)
#半連接池
sever.listen(5) #最多同時五個人坐椅子,實際上==6
print(
'服務端正在運行...'
)
#等待電話接入
#coon:指的是服務端往客戶端的管道
coon, addr = sever.accept()
#接聽對方講話的內容
#data客戶端發送過來的消息
data = coon.recv(1024) #一次性可接受1024bytes的數據
print(data)
>>>服務端正在運行...
>>>b'hello'
#掛電話
coon.close()
-client:
'''
先啟動服務端後再啟動客戶端
'''
import socket
#買手機
client = socket.socket()
#撥打電話
client.connect(
('127.0.0.1', 9876)
)
print('客戶端正在運行...')
#必鬚髮送bytes類型的數據
#開始講話
client.send(b'hello')
# 或client.send('hello'.encode('utf_8'))
>>>客戶端正在運行...
- socket套接字升級版本
- sever
'''
註意:
客戶端法送一次,我服務端就得先接受一次,之後才可以再向客戶端發送消息
'''
import socket
#買手機
sever = socket.socket()
#綁定手機卡
#裡面綁定的是一個元祖
sever.bind(
('127.0.0.1', 9876)
)
#半連接池
sever.listen(5)
print('服務端正在等待服務...')
#等待電話接入
#coon:指的是服務端往客戶端的管道
coon, addr = sever.accept()
#接聽對方講話的內容
data = coon.recv(1024)
print(data)
#服務端往客戶端發送消息
coon.send(b'hi i am sever')
#掛電話
coon.close()
>>>服務端正在等待服務...
>>>b'hello i am client...'
- client
'''
啟動服務端後再啟動客戶端
'''
import socket
#買手機
client = socket.socket()
#撥號
client.connect(
('127.0.0.1', 9876)
)
print('客戶端正在發送請求...')
client.send(b'hello i am client...')
#接收服務端的請求
data = client.recv(1024)
print(data)
client.close()
>>>客戶端正在發送請求...
>>>b'hi i am sever'
- socket套接字高級版本
-sever
import socket
#買手機
sever = socket.socket()
#綁定手機卡
sever.bind(
('127.0.0.1', 9876)
)
#半連接池
sever.listen(5)
print('服務端正在運行...')
#等待電話接入
coon, addr = sever.accept()
while True:
#接收對方講話內容
#data客戶端發送過來的消息
data = coon.recv(1024)
if len(data) == 0:
break
if data.decode('utf-8') == 'q':
break
print(data.decode('utf-8'))
send_data = input('服務端...')
coon.send(send_data.encode('utf-8'))
#掛電話
coon.close()
服務端正在運行...
服務端...你好啊亞峰
-client
import socket
#買手機
client = socket.socket()
#撥打電話
client.connect(
('127.0.0.1', 9876)
)
print('客戶端正在發送請求...')
while True:
send_data = input('客戶端>>>:').strip()
client.send(send_data.encode('utf-8'))
data = client.recv(1024)
if data.decode('utf-8') == 'q':
break
if len(data) == 0:
break
print(data.decode('utf-8'))
client.close()
>>>客戶端正在發送請求...
>>>客戶端>>>:你好啊熱巴
>>>好啊亞峰
- socket套接字終級版本
- sever
import socket
#買手機
sever = socket.socket()
#綁定手機卡
sever.bind(
('127.0.0.1', 9876)
)
#半連接池
sever.listen(5)
print('服務端正在服務...')
#迴圈實現可接受多個用戶訪問
while True:
coon, addr = sever.accept()
print(addr)
#迴圈實現通信
while True:
try:
#監聽代碼是否有異常出現
#接聽對方講話的內容
#data客戶端發送過來的消息
data = coon.recv(1024)
if len(data) == 0:
break
if data.decode('utf-8') =='q':
break
print(data.decode('utf-8'))
send_data = input('服務端>>>...')
#服務端向客戶端發送消息
coon.send(send_data.encode('utf-8'))
except Exception as e:
print(e)
break
#掛電話
coon.close()
>>>
服務端正在服務...
('127.0.0.1', 52467)
我想找迪麗熱巴
服務端>>>...你好啊亞峰,我是熱巴,有什麼能幫你嗎
熱巴youare beautiful
服務端>>>...謝謝亞峰
-client
import socket
#買手機
client = socket.socket()
#撥打號碼
client.connect(
('127.0.0.1', 9876)
)
print('客戶端正在發送請求...')
while True:
send_data = input('客戶端>>>:')
client.send(send_data.encode('utf-8'))
data = client.recv(1024)
if data.decode('utf-8') == 'q':
break
if len(data) == 0:
break
print(data.decode('utf-8'))
>>>
client.close()
客戶端正在發送請求...
客戶端>>>:我想找熱巴
你好啊亞峰,我是熱巴,有什麼能幫你嗎
客戶端>>>:熱巴you are beautiful
謝謝亞峰
客戶端>>>:
四、粘包問題
- 粘包問題的出現以及幾種情況
- 第一個問題
-sever
#問題一不知道數據的具體長度
# import socket
#
# import subprocess
#
# #買手機
# sever = socket.socket()
#
# #綁定電話卡
# sever.bind(
# ('127.0.0.1', 9867)
# )
#
# #半整數池
# sever.listen(5)
#
# while True:
# coon, addr = sever.accept()
# print(addr)
#
# while True:
# try:
# #recv從記憶體中獲取數據
# cmd = coon.recv(1024)
#
# if len(cmd) == 0:
# continue
# cmd = cmd.decode('utf-8')
# if cmd == 'q':
# break
#
# #調用subprocess連接終端,對終端進行操作,並獲取操作後正確或錯誤的結果
# obj = subprocess.Popen(
# cmd, shell=True, stdout=subprocess.PIPE,
# stderr=subprocess.PIPE
# )
# #結果交給result變數名
# result = obj.stdout.read() + obj.stderr.read()
# print(len(result))
#
# #windows預設是gbk
# print(result.decode('gbk'))
#
# #將結果返回給客戶端
# coon.send(result)
# except Exception as e:
# print(e)
# break
#
# coon.close()
-client
# import socket
#
# client = socket.socket()
#
# client.connect(
# ('127.0.0.1', 9867)
# )
#
# while True:
#
# cmd = input('客戶端輸入的內容:')
#
# client.send(cmd.encode('utf-8'))
#
# data = client.recv(19190)
#
# print(len(data))
#
# print(data.decode('gbk'))
- 第二種問題
-sever
#問題二:當發送多次傳入的數據長度卻不是很長的時候,服務端多次接收後面接收的沒內容
import socket
sever = socket.socket()
sever.bind(
('127.0.0.1', 9000)
)
sever.listen(5)
coon, addr = sever.accept()
data = coon.recv(10)
print(data)
data = coon.recv(1024)
print(data)
data = coon.recv(1024)
print(data)
>>>b'hellohello'
>>>b'hello'
>>>b''
- client
#問題二
import socket
client = socket.socket()
client.connect(
('127.0.0.1', 9000)
)
client.send(b'hello')
client.send(b'hello')
client.send(b'hello')
五、解決粘包問題
- 演示
- sever
import socket
import subprocess
import struct
sever = socket.socket()
sever.bind(
('127.0.0.1', 9000)
)
sever.listen(5)
while True:
coon, addr = sever.accept()
print(addr)
while True:
try:
#獲取客戶端傳過來的報頭
header = coon.recv(4)
#解包獲取真實的數據長度
data_len = struct.unpack('i', header)[0]
#準備接收真實數據
cmd = coon.recv(data_len)
if len(cmd) == 0:
continue
cmd = cmd.decode('utf-8')
if cmd == 'q':
break
#調用subprocess連接終端,對終端進行操作,並獲取操作後的正確或錯誤的結果
obj = subprocess.Popen(
cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
#結果交給result變數名
result = obj.stdout.read() + obj.stderr.read()
print('發送給客戶端的真實長度', len(result))
#將結果返回給客戶端,做一個報頭,返回給客戶端
header = struct.pack('i', len(result))
print(len(header))
coon.send(header)
coon.send(result)
except Exception as e:
print(e)
break
coon.close()
- client
import struct
import socket
client = socket.socket()
client.connect(
('127.0.0.1', 9000)
)
while True:
cmd = input('客戶端輸入的內容:')
cmd_bytes = cmd.encode('utf-8')
#做一個報頭
header = struct.pack('i', len(cmd_bytes))
print(len(header))
client.send(header)
#待伺服器確認長度以後,再發送真實數據長度
client.send(cmd_bytes)
#接收服務端返回的報頭
s_header = client.recv(4)
#解包,接收服務端返回的真實數據
data_len = struct.unpack('i', s_header)[0]
result = client.recv(data_len)
print('接收伺服器返回的真實數據長度', len(result))
print(result.decode('gbk'))
- 演示二
-sever
import socket
import struct
import json
sever = socket.socket()
sever.bind(
('127.0.0.1', 9000)
)
sever.listen(5)
while True:
coon, addr = sever.accept()
print(addr)
while True:
try:
#獲取客戶端傳來的報頭
header = coon.recv(4)
#解包,獲取真實的數據長度
json_len = struct.unpack('i', header)[0]
#接收json(dictionary)的真實長度
json_bytes_data = coon.recv(json_len)
#反序列化將bytes類型數據變成json數據
json_data = json_bytes_data.decode('utf-8')
back_dic = json.loads(json_data)
print(back_dic)
print(back_dic.get('movie_len'))
except Exception as e:
print(e)
break
coon.close()
>>>('127.0.0.1', 53414)
>>>{'movie_name': '色戒', 'movie_len': 100000}
>>>100000
-client
import struct
import socket
import json
client = socket.socket()
client.connect(
('127.0.0.1', 9000)
)
while True:
movie_name = input('請輸入上傳電影的名字:').strip()
#偽裝電影真實數據
movie_len = 100000
send_dic = {
'movie_name': movie_name,
'movie_len': movie_len
}
#序列化
json = json.dumps(send_dic)
# print(json)
# print(json.encode('utf-8'))
# print(len(json.encode('utf-8')))
json_bytes = json.encode('utf-8')
#做一個報頭
header = struct.pack('i', len(json_bytes))
#發送報頭
client.send(header)
#發送真實數據
client.send(json_bytes)
>>>請輸入上傳電影的名字:色戒