欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

對python中基于tcp協(xié)議的通信(數(shù)據(jù)傳輸)實(shí)例講解

 更新時間:2019年07月22日 09:45:01   作者:suguangti  
今天小編就為大家分享一篇對python中基于tcp協(xié)議的通信(數(shù)據(jù)傳輸)實(shí)例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

閱讀目錄

tcp協(xié)議:流式協(xié)議(以數(shù)據(jù)流的形式通信傳輸)、安全協(xié)議(收發(fā)信息都需收到確認(rèn)信息才能完成收發(fā),是一種雙向通道的通信)

tcp協(xié)議在OSI七層協(xié)議中屬于傳輸層,它上承用戶層的數(shù)據(jù)收發(fā),下啟網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層??梢哉f很多安全數(shù)據(jù)的傳輸通信都是基于tcp協(xié)議進(jìn)行的。

為了讓tcp通信更加方便需要引入一個socket模塊(將網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層封裝的模塊),我們只要調(diào)用模塊中的相關(guān)接口就能實(shí)現(xiàn)傳輸層下面的繁瑣操作。

簡單的tcp協(xié)議通信模板:(需要一個服務(wù)端和一個客戶端)

服務(wù)端:

from socket import *
# 確定服務(wù)端傳輸協(xié)議↓↓↓↓↓↓↓
server = socket(AF_INET, SOCK_STREAM) # 這里的SOCK_STREAM代表的就是流式協(xié)議TCP,如果是SOCK_DGRAM就代表UDP協(xié)議
# 固定服務(wù)端IP和PORT,讓客戶端能夠通過IP和端口訪問服務(wù)端↓↓↓↓↓↓↓
server.bind(('127.0.0.1', 8080))  # ('127.0.0.1', 8080)這里必須用元組形式傳入IP和PORT,本地訪問本地IP默認(rèn)為'127.0.0.1'
# 設(shè)置半連接池數(shù)量(一般為5)
server.listen(5) # 半連接池:客戶端連接請求個數(shù)的容器,當(dāng)前已連接的客戶端信息收發(fā)未完成前,會有最大5個客戶端連接請求進(jìn)入排隊(duì)狀態(tài),
         # 等待上一個通信完畢后,就可以連接進(jìn)入開始通信。                                           

# 雙向通道建立成功,可以進(jìn)行下一步數(shù)據(jù)的通信了↓↓↓↓↓↓↓
conn, client_addr = server.accept()
# 進(jìn)行一次信息的收與發(fā)
data = conn.recv(1024)  # 每次最大接收1024字節(jié),收到的數(shù)據(jù)為二進(jìn)制Bytes類型

conn.send(data.upper())  # 將收到的數(shù)據(jù)進(jìn)行處理,返回新的數(shù)據(jù),反饋給客戶端(給客戶端發(fā)數(shù)據(jù)),發(fā)的數(shù)據(jù)類型也必須是Bytes類型

# 一輪信息收發(fā)完畢,關(guān)閉已經(jīng)建立的雙向通道
conn.close()


客戶端:
from socket import *
# 確定客戶端傳輸協(xié)議↓↓↓↓↓↓↓(服務(wù)端和客戶端服務(wù)協(xié)議一樣才能進(jìn)行有效的通信)
client = socket(AF_INET, SOCK_STREAM) # 這里的SOCK_STREAM代表的就是流式協(xié)議TCP,如果是SOCK_DGRAM就代表UDP協(xié)議
# 開始連接服務(wù)端IP和PORT,建立雙向鏈接
client.connect(('127.0.0.1', 8080)) # 通過服務(wù)端IP和PORT進(jìn)行連接

# 走到這一步就已經(jīng)建立連接完畢,接下來開始數(shù)據(jù)通信:
client.send('hello,server'.encode('utf-8'))  # 將發(fā)送的信息轉(zhuǎn)碼成Bytes類型數(shù)據(jù)

data = client.recv(1024) # 每次最大收數(shù)據(jù)大小為1024字節(jié)(1kb)

print(data.decode('utf-8')) # 將b類型數(shù)據(jù)轉(zhuǎn)換成字符串格式

# 一次傳輸完畢
client.close()  # 關(guān)閉客戶端連接


啟動服務(wù)端(服務(wù)端開始監(jiān)聽客戶端的連接請求)
啟動客戶端(客戶端給服務(wù)端發(fā)送連接請求)
建立雙向鏈接完成
客戶端給服務(wù)端發(fā)送信息 hello,server
服務(wù)端收到hello,server,將其轉(zhuǎn)換成大寫,返回給客戶端(此時服務(wù)端一輪通信完畢)
客戶端收到服務(wù)端的反饋信息,打印出HELLO,SERVER(此時客戶端一輪通信完畢)

以上是最基本的一次基于tcp協(xié)議通信的過程客戶端發(fā),服務(wù)端收,服務(wù)端處理數(shù)據(jù)然后發(fā),客戶端收到服務(wù)端發(fā)了的反饋數(shù)據(jù)。

TCP協(xié)議的通信粘包問題:

但是由于tcp協(xié)議是一種流式協(xié)議,流式協(xié)議就會有一個特點(diǎn):數(shù)據(jù)的傳輸像一涓涓水流的形式傳輸,我們在收數(shù)據(jù)的時候默認(rèn)最大收數(shù)據(jù)大小為1024字節(jié),當(dāng)發(fā)送的數(shù)據(jù)小于1024字節(jié)時候當(dāng)然不會有問題,一次性全部收完,但是但是但是當(dāng)發(fā)送的數(shù)據(jù)大于1024字節(jié)的時候,我們這邊又不知道發(fā)送的數(shù)據(jù)大小是多少,只能默認(rèn)的1024字節(jié)的時候,數(shù)據(jù)一次性就不可能收完,只能在這次收1024字節(jié),那1024字節(jié)以外的數(shù)據(jù)呢?由于數(shù)據(jù)的傳輸是流式協(xié)議,所以沒有收完的數(shù)據(jù)會依次排隊(duì)在門外等著,等待你下次收數(shù)據(jù)時候再次收取,這樣如果每次傳的數(shù)據(jù)大小不確認(rèn),收的時候數(shù)據(jù)也不知道該收多少的時候,就會導(dǎo)致每次收數(shù)據(jù)的時候收不完,收不完的數(shù)據(jù)就會在緩存中排隊(duì),等待下次收,收不完的數(shù)據(jù)就好像粘粘在一起(zhan nian)。這就叫tcp的流式協(xié)議的通信粘包問題。

這個問題的更形象過程可以見下圖:

知道這粘包的大致過程,就能夠找到方法對癥下藥了:

粘包問題的解決分析:

粘包問題歸根到底是數(shù)據(jù)接收不徹底導(dǎo)致,那么要解決這個問題最直接的方法就是每次都徹底地收完數(shù)據(jù)。

要想達(dá)到這個目的就需要每次在收數(shù)據(jù)之前事先知道我要收數(shù)據(jù)的文件大小,知道了文件大小我們就能有的放矢,準(zhǔn)確的把數(shù)據(jù)收完不遺留。

解決方法:先發(fā)個包含待發(fā)送文件大小長度的報頭文件>>>>再發(fā)送原始文件

引入模塊struct

具體看代碼:

服務(wù)端:
import socket
import struct

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
  conn, client_addr = server.accept()
  print('客戶端已連接')
  while True:
    try:
      head = conn.recv(4)
      size = struct.unpack('i', head)[0]
      data = conn.recv(size)
      print('已收到客戶端信息:', data.decode('utf-8'))
    except ConnectionResetError:
      print('客戶端已中斷連接')
      conn.close()
      break

客戶端:
import socket
import struct
while True:
  try:
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    print('已連接到服務(wù)端')
    while True:
      try:
        msg = 'abcdefghijklmnopqrstuvwxyz1234567890'.encode('utf-8')
        head = struct.pack('i', len(msg))
        client.send(head)
        client.send(msg)

      except ConnectionResetError:
        print('服務(wù)端已中斷連接')
        client.close()
        break

  except ConnectionRefusedError:
    print('無法連接到服務(wù)器')

以上方法只是為了試驗(yàn)解決粘包問題,真正應(yīng)用場景可以是上傳或者下載一個大文件的時候,這時就必須要提前知道接收的文件實(shí)際大小,做到100%精確的接收每一個數(shù)據(jù),這時就需要收數(shù)據(jù)前獲取即將收到的文件大小,然后對癥下藥,做到精確接收,但實(shí)現(xiàn)方法不一定非要用struct模塊,struct模塊只是解決粘包問題中的一個官方正式的方法,自己還可以有自己的想法,比如先直接把要發(fā)送文件的大小已字符串的格式發(fā)送過去,然后再發(fā)送這個文件,目的只有一個,知道我接收的文件的大小,精準(zhǔn)接收文件。

下面寫一個客戶端從服務(wù)端下載文件的實(shí)例,供大家參考:(假設(shè)下載文件在服務(wù)端文件同一級)

下載服務(wù)端:

import socket
import time
import struct
import json

# 計(jì)算當(dāng)前文件夾下文件的md5值、大小
import os, hashlib

def get_info(file_name):
  file_info = {}
  base_dir = os.path.dirname(__file__)
  file_dir = os.path.join(base_dir, file_name)
  if os.path.exists(file_dir):
    # md5計(jì)算時文件數(shù)據(jù)是放在內(nèi)存中的,當(dāng)我們計(jì)算一個大文件時,可以用update方法進(jìn)行分步計(jì)算,
    # 每次添加部分文件數(shù)據(jù)進(jìn)行計(jì)算,減少內(nèi)存占用。
    with open(file_dir, 'rb') as f:
      le = 0
      d5 = hashlib.md5()
      for line in f:
        le += len(line)
        d5.update(line)
      file_info['lenth'] = le # 將文件長度加入報頭字典
      file_md5 = d5.hexdigest()
      file_info['md5'] = file_md5 # 將文件md5加入報頭字典
    file_size = os.path.getsize(file_dir) / float(1024 * 1024)
    file_info['size(MB)'] = round(file_size, 2) # 將文件大小加入報頭字典
    return file_info
  else:
    return file_info


server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
  conn, client_addr = server.accept()
  print('%s >:客戶端(%s)已連接' % (time.strftime('%Y-%m-%d %H:%M:%S'), client_addr))
  while True:
    try:
      download_filename = conn.recv(1024).decode('utf-8')
      download_file_info_dic = get_info(download_filename)
      j_head = json.dumps(download_file_info_dic) # 將文件信息字典轉(zhuǎn)成json字符串格式
      head = struct.pack('i', len(j_head))
      conn.send(head)
      conn.send(j_head.encode('utf-8'))
      if not download_file_info_dic:
        continue
      with open(download_filename, 'rb') as f:
        while True:
          data=f.read(1024)
          conn.send(data)
        # for line in f:
        #   conn.send(line)

    except ConnectionResetError:
      print('%s >:客戶端(%s)已斷開' % (time.strftime('%Y-%m-%d %H:%M:%S'), client_addr))
      conn.close()
      break
下載客戶端:

import socket
import time
import struct
import json

# 進(jìn)度條顯示
def progress(percent,width=30):
  text=('\r[%%-%ds]'%width)%('x'*int(percent*width))
  text=text+'%3s%%'
  text=text%(round(percent*100))
  print(text,end='')

while True:
  try:
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    print('%s >:已連接到服務(wù)端' % time.strftime('%Y-%m-%d %H:%M:%S'))
    while True:
      try:
        file_name = input('請輸入下載文件名稱:')
        client.send(file_name.encode('utf-8'))

        head = client.recv(4) # 收報頭
        j_dic_lenth = struct.unpack('i', head)[0] # 解壓報頭,獲取json格式的文件信息字典的長度
        j_head = client.recv(j_dic_lenth) # 收json格式的信息字典
        file_info_dic = json.loads(j_head) # 反序列化json字典,得到文件信息字典
        if not file_info_dic:
          print('文件不存在')
          continue
        file_lenth = file_info_dic.get('lenth')
        file_size = file_info_dic.get('size(MB)')
        file_md5 = file_info_dic.get('md5')
        rec_len = 0
        with open('cpoy_'+file_name, 'wb') as f:
          while rec_len < file_lenth:
            data = client.recv(1024)
            f.write(data)
            rec_len += len(data)
            per=rec_len/file_lenth
            progress(per)
          print()
            # print('下載比例:%6s %%'%)
          if not rec_len:
            print('文件不存在')
          else:

            print('文件[%s]下載成功: 大?。?s MB|md5值:[%s]' % (file_name, file_size, file_md5))

      except ConnectionResetError:
        print('%s >:服務(wù)端已終止' % time.strftime('%Y-%m-%d %H:%M:%S'))
        client.close()
        break

  except ConnectionRefusedError:
    print('%s >:無法連接到服務(wù)器' % time.strftime('%Y-%m-%d %H:%M:%S'))

文件上傳同理,只是換成客戶端給服務(wù)端發(fā)送文件,服務(wù)端接收。

接下來我們來學(xué)習(xí)一下TCP協(xié)議下通信利用socketserver模塊實(shí)現(xiàn)多客戶端并發(fā)通信的效果:

服務(wù)端:
import socketserver
import time

class MyTcpHandler(socketserver.BaseRequestHandler):
  # 到這里表示服務(wù)端已監(jiān)聽到一個客戶端的連接請求,將通信交給一個handle方法實(shí)現(xiàn),自己再去監(jiān)聽客戶連接請求
  def handle(self):
    # 建立雙向通道,進(jìn)行通信
    print('%s|客戶端%s已連接' % (time.strftime('%Y-%m-%d %H:%M:%S'), self.client_address))
    while True:
      try:
        data = self.request.recv(1024)
        msg = '我已收到您的請求[%s],感謝您的關(guān)注!' % data.decode('utf-8')
        self.request.send(msg.encode('utf-8'))
      except ConnectionResetError:
        print('%s|客戶端%s已斷開連接' % (time.strftime('%Y-%m-%d %H:%M:%S'), self.client_address))
        break

if __name__ == '__main__':
  server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyTcpHandler)  # 綁定服務(wù)端IP和PORT,并產(chǎn)生并發(fā)方法對象
  print('等待連接請求中...')
  server.serve_forever() # 服務(wù)端一直開啟
客戶端:
from socket import *
import time
server_addr = ('127.0.0.1', 8080)
count = 1
while True:
  if count > 10:
    time.sleep(1)
    print('%s|連接%s超時' % (time.strftime('%Y-%m-%d %H:%M:%S'), server_addr))
    break
  try:
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))
    count = 1
    print('%s|服務(wù)端%s連接成功' % (time.strftime('%Y-%m-%d %H:%M:%S'), server_addr))
    while True:
      try:
        client.send('北鼻'.encode('utf-8'))
        data = client.recv(1024)
        print(data.decode('utf-8'))
        time.sleep(0.5)
      except ConnectionResetError:
        print('%s|服務(wù)端%s已中斷' % (time.strftime('%Y-%m-%d %H:%M:%S'), server_addr))
        client.close()
        break
  except ConnectionRefusedError:
    print('無法連接到服務(wù)端')
    count += 1

同時再添加客戶端2、客戶端3,將發(fā)送數(shù)據(jù)稍微修改一下,實(shí)現(xiàn)多客戶端并發(fā)通信服務(wù)端。

通過subprocess模塊,實(shí)現(xiàn)遠(yuǎn)程shell命令行命令

服務(wù)端
import socketserver
import struct
import subprocess


class MyTcpHandler(socketserver.BaseRequestHandler):
  def handle(self):
    while True:
      print('客戶端<%s,%s>已連接' % self.client_address)
      try:
        cmd = self.request.recv(1024).decode('utf-8')
        res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout = res.stdout.read()
        stderr = res.stderr.read()
        head = struct.pack('i', len(stdout + stderr))
        self.request.send(head)
        self.request.send(stdout)
        self.request.send(stderr)
      except ConnectionResetError:
        print('客戶端<%s,%s>已中斷連接' % self.client_address)
        self.request.close()
        break


if __name__ == '__main__':
  server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyTcpHandler)
  server.serve_forever()
客戶端
from socket import *
import struct

while True:
  try:
    client = socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))
    while True:
      try:
        cmd = input('>>>>>>>:').strip().encode('utf-8')
        client.send(cmd)
        head = client.recv(4)
        size = struct.unpack('i', head)[0]
        cur_size = 0
        result = b''
        while cur_size < size:
          data = client.recv(1024)
          cur_size += len(data)
          result += data
        print(result.decode('gbk'))  # windows系統(tǒng)默認(rèn)編碼是gbk,解碼肯定也要用gbk
      except ConnectionResetError:
        print('服務(wù)端已中斷')
        client.close()
        break

  except ConnectionRefusedError:
    print('無法連接服務(wù)端')

通過客戶端輸入命令,在服務(wù)端執(zhí)行shell命令,通過服務(wù)端執(zhí)行subprocess模塊達(dá)到遠(yuǎn)程shell命令操作,此過程主要需要考慮2個難點(diǎn),①解決命令產(chǎn)生結(jié)果數(shù)據(jù)的發(fā)送粘包問題,②注意返回結(jié)果的shell命令結(jié)果是gbk編碼,接收后需要用gbk解碼一下。

以上這篇對python中基于tcp協(xié)議的通信(數(shù)據(jù)傳輸)實(shí)例講解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • pytorch中使用LSTM詳解

    pytorch中使用LSTM詳解

    這篇文章主要介紹了pytorch中使用LSTM,可以在troch.nn模塊中找到LSTM類,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-07-07
  • pycharm配置python環(huán)境的詳細(xì)圖文教程

    pycharm配置python環(huán)境的詳細(xì)圖文教程

    PyCharm是一款功能強(qiáng)大的Python編輯器,具有跨平臺性,下面這篇文章主要給大家介紹了關(guān)于pycharm配置python環(huán)境的詳細(xì)圖文教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Django使用HTTP協(xié)議向服務(wù)器傳參方式小結(jié)

    Django使用HTTP協(xié)議向服務(wù)器傳參方式小結(jié)

    本文主要介紹了Django使用HTTP協(xié)議向服務(wù)器傳參方式小結(jié),用戶發(fā)送請求時攜帶的參數(shù)后端需要使用,而不同的發(fā)送參數(shù)的方式對應(yīng)了不同的提取參數(shù)的方式,本文就詳細(xì)的介紹一下
    2021-08-08
  • Python數(shù)據(jù)解析之BeautifulSoup4的用法詳解

    Python數(shù)據(jù)解析之BeautifulSoup4的用法詳解

    Beautiful?Soup?是一個可以從?HTML?或?XML?文件中提取數(shù)據(jù)的?Python?庫,這篇文章主要來和大家介紹一下BeautifulSoup4的用法,需要的可以參考一下
    2023-06-06
  • Python與HTTP服務(wù)交互的三種方式

    Python與HTTP服務(wù)交互的三種方式

    本文主要介紹了Python與HTTP服務(wù)交互的三種方式,通過http.client,requests,RissionPage,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • python 將對象設(shè)置為可迭代的兩種實(shí)現(xiàn)方法

    python 將對象設(shè)置為可迭代的兩種實(shí)現(xiàn)方法

    今天小編就為大家分享一篇python 將對象設(shè)置為可迭代的兩種實(shí)現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-01-01
  • 淺析Python+OpenCV使用攝像頭追蹤人臉面部血液變化實(shí)現(xiàn)脈搏評估

    淺析Python+OpenCV使用攝像頭追蹤人臉面部血液變化實(shí)現(xiàn)脈搏評估

    這篇文章主要介紹了Python+OpenCV使用攝像頭追蹤人臉面部血液變化實(shí)現(xiàn)脈搏評估,本文通過一段代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10
  • Python matplotlib安裝和使用的完整教程

    Python matplotlib安裝和使用的完整教程

    Python Matplotlib是一個強(qiáng)大的數(shù)據(jù)可視化庫,它允許用戶創(chuàng)建各種類型的靜態(tài)、動態(tài)圖表和圖形,Matplotlib基于NumPy和Pandas等科學(xué)計(jì)算庫,提供了豐富的繪圖選項(xiàng),本文介紹了Python matplotlib安裝和使用的完整教程,需要的朋友可以參考下
    2024-07-07
  • 在Matplotlib圖中插入LaTex公式實(shí)例

    在Matplotlib圖中插入LaTex公式實(shí)例

    這篇文章主要介紹了在Matplotlib圖中插入LaTex公式實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • Python加密與解密模塊hashlib與hmac

    Python加密與解密模塊hashlib與hmac

    這篇文章介紹了Python中的加密與解密模塊hashlib與hmac,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06

最新評論