31.Python之網(wǎng)絡編程(socket模塊)

Python之網(wǎng)絡編程(socket模塊)

  1. 什么是socket帽衙?

    • Socket是應用層與TCP / IP協(xié)議族通信的中間軟件抽象層,它是一組接口贞绵。在設計模式中厉萝,Socket其實就是一個“門面模式”,它把復雜的TCP / IP協(xié)議族隱藏在Socket接口后面榨崩,對用戶來說谴垫,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù)母蛛,以符合指定的協(xié)議翩剪。
    • 基于文件類型的套接字家族:
      • 套接字家族的名字:AF_UNIX
        Unix一切皆文件,基于文件的套接字調用的就是底層的文件系統(tǒng)來取數(shù)據(jù)彩郊,兩個套接字進程運行在同一機器前弯,可以通過訪問同一個文件系統(tǒng)間接完成通信。
    • 基于網(wǎng)絡類型的套接字家族
      • 套接字家族的名字:AF_INET
        還有AF_INET6被用于ipv6秫逝,還有一些其他的地址家族恕出,不過,他們要么是只用于某個平臺违帆,要么就是已經(jīng)被廢棄浙巫,或者是很少被使用,或者是根本沒有實現(xiàn)刷后,所有地址家族中的畴,AF_INET是使用最廣泛的一個,Python支持很多種地址家族尝胆,但是由于我們只關心網(wǎng)絡編程丧裁,所以大部分時候我么只使用AF_INET。

  1. 為什么要用socket班巩?

    • 使用socket渣慕,我們無需深入理解TCP/UDP協(xié)議,socket已經(jīng)為我們封裝好了抱慌,我們只需要遵循socket的規(guī)則去編程逊桦,寫出的程序自然就是遵循TCP/UDP標準的。

  1. 基于TCP協(xié)議的socket(無并發(fā))

    • 服務端

      # tcp是基于可靠鏈接的抑进,必須先啟動服務端强经,然后再啟動客戶端去鏈接服務端
      import socket
      
      # 由于 socket 模塊中有太多的屬性。因此可以使用'from module import *'語句寺渗。也就是 'from socket import *',這樣我們就把 socket 模塊里的所有屬性都帶到我們的命名空間里了,這樣能大幅減短代碼匿情。
      # from socket import *
      # socket_server = socket(AF_INET,SOCK_STREAM)
      socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 創(chuàng)建服務器套接字,SOCK_STREAM流式協(xié)議信殊,指的是TCP協(xié)議炬称;SCOK_DGRAM數(shù)據(jù)報協(xié)議,指的是UDP協(xié)議。
      socket_server.bind(('127.0.0.1', 8080))  # 把地址綁定到套接字涡拘,只有服務端需要綁定玲躯,IP地址填寫服務器IP,端口是數(shù)字類型鳄乏,1025-65530任選跷车,端口0-1024系統(tǒng)占用
      socket_server.listen(5)  # 監(jiān)聽鏈接,backlog = 5 表示同一時間能接受5個請求橱野,并不是最大連接數(shù)
      
      # 等待連接
      conn, client_address = socket_server.accept()  # 程序阻塞朽缴,等待連接,有兩個參數(shù),一個連接對象conn水援,一個客戶端地址client_address(包含IP和端口),對象conn是tcp三次握手的產(chǎn)物裹唆,用來收發(fā)消息誓斥,而socket_server對象是專門用來建立連接的
      
      # 收發(fā)消息
      msg = conn.recv(1024)  # 收消息许帐,有個返回值給msg劳坑,1024是一個最大的限制,表示最多能收 1024 bytes
      conn.send('Hello!!!'.encode('utf-8'))  # 發(fā)消息成畦,網(wǎng)絡中只能傳輸bytes類型
      
      conn.close()  # 斷開(關閉)客戶端套接字距芬,回收系統(tǒng)資源。完成TCP四次揮手循帐。
      
      socket_server.close()  # 斷開(關閉)服務器套接字框仔,回收系統(tǒng)資源
      
    • 客戶端

      import socket
      
      socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 創(chuàng)建客戶端套接字,SOCK_STREAM流式協(xié)議拄养,指的是TCP協(xié)議离斩;SCOK_DGRAM數(shù)據(jù)報協(xié)議,指的是UDP協(xié)議银舱。
      
      socket_client.connect(('127.0.0.1', 8080))  # 與服務器建立連接,地址為服務器的IP地址和端口號
      
      socket_client.send('Hello!'.encode('utf-8'))  # 發(fā)消息跛梗,注意字符串不能直接直接發(fā)寻馏,需要轉換成二進制
      msg = socket_client.recv(1024)  # 收消息
      
      socket_client.close()
      

  1. 加上通信循環(huán)和連接循環(huán)的socket(無并發(fā))(解決服務端不可以循環(huán)接收客戶端信息的問題)

    • 服務端

      import socket
      
      socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 如果什么參數(shù)都不傳,默認就是這個
      socket_server.bind(('127.0.0.1', 8080))
      socket_server.listen(5)
      
      while True:  # 加連接循環(huán)
          conn, client_address = socket_server.accept()
      
          while True:  # 加通信循環(huán)
              try:
                  msg = conn.recv(1024)
                  if not msg: break  # 針對Linux操作系統(tǒng)
                  print('客戶端:', client_address)
                  conn.send(msg + b'_SB')
              except ConnectionResetError:
                  break
      
          conn.close()
      
      socket_server.close()
      
    • 客戶端

      import socket
      
      socke_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      
      socke_client.connect(('127.0.0.1', 8080))
      
      while True:
          msg = input('>>>:')
          socke_client.send(msg.encode('utf-8'))
          msg = socke_client.recv(1024)
          print(msg)
      
      socke_client.close()
      

  1. 遠程執(zhí)行命令的小程序(無并發(fā))

    • 服務端

      from socket import *
      import subprocess
      import struct  # Python提供了一個struct模塊來解決str和其他二進制數(shù)據(jù)類型的轉換核偿。struct的pack函數(shù)把任意數(shù)據(jù)類型變成字符串
      
      socket_server = socket(AF_INET, SOCK_STREAM)
      socket_server.bind(('127.0.0.1', 8080))
      socket_server.listen(5)
      
      while True:
          conn, client_address = socket_server.accept()
          print('正在監(jiān)聽……')
          while True:
              try:
                  cmd = conn.recv(1024)
                  if not cmd: break
                  print('開始接收文件……')
                  obj = subprocess.Popen(cmd.decode('utf-8'),
                                         shell=True,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE
                                         )
                  res_stdout = obj.stdout.read()
                  res_stderr = obj.stderr.read()
              except ConnectionResetError:
                  break
      
              # -*- 解決TCP粘包問題 -*-#
              # 制作固定長度的報頭
              total_size = len(res_stdout) + len(res_stderr)
              header = struct.pack('i', total_size)  # 制作固定長度的報頭
      
              # 發(fā)送報頭
              conn.send(header)
      
              # conn.send(res_stdout+res_stderr)
              # 由于TCP的優(yōu)化诚欠,使用了Nagle算法,將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù)漾岳,合并成一個大的數(shù)據(jù)塊轰绵,然后進行封包,實際上以下兩條命令會合并到一起發(fā)送尼荆。
              conn.send(res_stdout)
              conn.send(res_stderr)
      
          conn.close()
      
      socket_server.close()
      
    • 客戶端

      from socket import *
      import struct
      
      socket_client = socket(AF_INET, SOCK_STREAM)
      socket_client.connect(('127.0.0.1', 8080))
      
      while True:
          cmd = input('>>>:')
          if not cmd: continue  # 判斷cmd不為空左腔,if x is not None:continue 是最好的寫法
          socket_client.send(cmd.encode('utf-8'))
      
          # -*- 解決TCP粘包問題 -*-#
          # 先收固定長度的報頭
          header = socket_client.recv(4)
      
          # 解析報頭
          total_size = struct.unpack('i', header)[0]
      
          # 根據(jù)報頭,收取數(shù)據(jù)捅儒,為防止數(shù)據(jù)過大翔悠,撐爆內存,以小單位循環(huán)收取
          recv_size = 0
          res = b''
          while recv_size < total_size:
              recv_date = socket_client.recv(1024)
              res += recv_date
              recv_size += len(recv_date)
          # info = socket_client.recv(1024)
          print(res.decode('gbk'))
      
      socket_client.close()
      

  1. 遠程執(zhí)行命令的小程序(自定義報頭野芒,無并發(fā))

    • 服務端

      from socket import *
      import subprocess
      import struct  # Python提供了一個struct模塊來解決str和其他二進制數(shù)據(jù)類型的轉換蓄愁。struct的pack函數(shù)把任意數(shù)據(jù)類型變成字符串
      import json
      
      socket_server = socket(AF_INET, SOCK_STREAM)
      socket_server.bind(('127.0.0.1', 8081))
      socket_server.listen(5)
      
      while True:
          conn, client_address = socket_server.accept()
          print('正在監(jiān)聽……')
          while True:
              try:
                  cmd = conn.recv(1024)
                  if not cmd: break
                  print('開始接收文件……')
                  obj = subprocess.Popen(cmd.decode('utf-8'),
                                         shell=True,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE
                                         )
                  res_stdout = obj.stdout.read()
                  res_stderr = obj.stderr.read()
              except ConnectionResetError:
                  break
      
              # 制作報頭
              header_dic = {'total_size': len(res_stdout) + len(res_stderr), 'md5': 'xxxxxxxxxxxx', 'filename': 'xxx.py'}
              header_json = json.dumps(header_dic)  # 將字典轉換成字符串
              header_bytes = header_json.encode('utf-8')  # 將字符串轉換成
      
              # 獲取報頭長度
              header_size = len(header_bytes)
      
              # 發(fā)送報頭
              conn.send(struct.pack('i', header_size))
      
              # conn.send(res_stdout+res_stderr)
              # 由于TCP的優(yōu)化,使用了Nagle算法狞悲,將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù)撮抓,合并成一個大的數(shù)據(jù)塊,然后進行封包摇锋,實際上以下兩條命令會合并到一起發(fā)送丹拯。
              # 發(fā)送數(shù)據(jù)
              conn.send(res_stdout)
              conn.send(res_stderr)
      
          conn.close()
      
      socket_server.close()
      
    • 客戶端

      from socket import *
      import struct
      import json
      
      socket_client = socket(AF_INET, SOCK_STREAM)
      socket_client.connect(('127.0.0.1', 8081))
      
      while True:
          cmd = input('>>>:')
          if not cmd: continue  # 判斷cmd不為空,if x is not None:continue 是最好的寫法
          socket_client.send(cmd.encode('utf-8'))
      
          # 先收報頭的長度
          header_size = struct.unpack('i', socket_client.recv(4))[0]
      
          # 接收報頭
          header_bytes = socke_client.recv(header_size)
      
          # 解析報頭
          header_json = header_bytes.decode('utf-8')
          header_dic = json.loads(header_json)
          total_size = header_dic['total_size']
      
          # 根據(jù)報頭荸恕,收取數(shù)據(jù)乖酬,為防止數(shù)據(jù)過大,撐爆內存融求,以小單位循環(huán)收取
          recv_size = 0
          res = b''
          while recv_size < total_size:
              recv_date = socke_client.recv(1024)
              res += recv_date
              recv_size += len(recv_date)
          # info=socket_client.recv(1024)
          print(res.decode('gbk'))
      
      socket_client.close()
      
  1. 基于UDP的socket

    • 數(shù)據(jù)報協(xié)議(UDP)沒有粘包問題咬像,UDP協(xié)議面向無連接,發(fā)送數(shù)據(jù)生宛,無需對方確認县昂,發(fā)送效率高,但UDP協(xié)議有效傳輸數(shù)據(jù)大小為 512 bytes陷舅,超過這個大小就非常容易丟包倒彰,DNS服務使用UDP協(xié)議,由于這個限制莱睁,導致全球根服務器數(shù)量限制在13臺待讳。

    • 服務端

      import socket
      
      socket_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
      socket_server.bind(('127.0.0.1', 8080))
      
      while True:
          client_msg, client_address = socket_server.recvfrom(1024)
          socket_server.sendto(client_msg.upper(), client_address)
      
    • 客戶端

      import socket
      
      socket_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
      
      while True:
          msg = input('>>>:').strip()
          socket_client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
          server_msg, server_address = socket_client.recvfrom(1024)
          print(server_msg.decode('utf-8'))
      
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載芒澜,如需轉載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末创淡,一起剝皮案震驚了整個濱河市撰糠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辩昆,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旨袒,死亡現(xiàn)場離奇詭異汁针,居然都是意外死亡,警方通過查閱死者的電腦和手機砚尽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門施无,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人必孤,你說我怎么就攤上這事猾骡。” “怎么了敷搪?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵兴想,是天一觀的道長。 經(jīng)常有香客問我赡勘,道長嫂便,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任闸与,我火速辦了婚禮毙替,結果婚禮上,老公的妹妹穿的比我還像新娘践樱。我一直安慰自己厂画,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布拷邢。 她就那樣靜靜地躺著袱院,像睡著了一般瞭稼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弛姜,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音苍在,去河邊找鬼绝页。 笑死寂恬,一個胖子當著我的面吹牛,可吹牛的內容都是我干的初肉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼臼隔,長吁一口氣:“原來是場噩夢啊……” “哼妄壶!你這毒婦竟也來了?” 一聲冷哼從身側響起氨淌,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盛正,失蹤者是張志新(化名)和其女友劉穎屑埋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壤蚜,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡徊哑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了著蟹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梢莽。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昏名,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情洪鸭,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布置鼻,位于F島的核電站蜓竹,受9級特大地震影響,放射性物質發(fā)生泄漏嘶是。R本人自食惡果不足惜姨蝴,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一左医、第九天 我趴在偏房一處隱蔽的房頂上張望浮梢。 院中可真熱鬧彤路,春花似錦秕硝、人聲如沸洲尊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丽涩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間矢渊,已是汗流浹背继准。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留矮男,地道東北人移必。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像避凝,于是被迫代替她去往敵國和親舞萄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容

  • 網(wǎng)絡編程 一.楔子 你現(xiàn)在已經(jīng)學會了寫python代碼含思,假如你寫了兩個python文件a.py和b.py崎弃,分別去運...
    go以恒閱讀 2,022評論 0 6
  • 大綱 一.Socket簡介 二.BSD Socket編程準備 1.地址 2.端口 3.網(wǎng)絡字節(jié)序 4.半相關與全相...
    VD2012閱讀 2,346評論 0 5
  • 代碼不好排版,可以到微信訂閱號(xuanhun521)查看原文饲做。 Python黑帽編程2.8套接字編程 套接字編程...
    玄魂閱讀 655評論 1 2
  • 網(wǎng)絡 理論模型,分為七層物理層數(shù)據(jù)鏈路層傳輸層會話層表示層應用層 實際應用,分為四層鏈路層網(wǎng)絡層傳輸層應用層 IP...
    FlyingLittlePG閱讀 774評論 0 0
  • 套接字(Socket) 伯克利套接字(BSD Socket) 套接字(socket)是一個抽象層,應用程序可以通過...
    夙小葉閱讀 1,719評論 0 0