日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

socket编程初级

發布時間:2023/12/19 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket编程初级 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是socket

  • 定義

socket通常也稱作套接字,用于描述IP地址和端口,是一個通信鏈的句柄,應用程序通常通過套接字向網絡發出請求或者應答網絡請求。

socket起源于Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,對于文件用【打開】【讀寫】【關閉】模式來操作。socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)

  • socket和file的區別:

    • file模塊是針對某個指定文件進行【打開】【讀寫】【關閉】

    • socket模塊是針對 服務器端 和 客戶端Socket 進行【打開】【讀寫】【關閉】

  • python相關

Python 提供了兩個基本的 socket 模塊。py2位大寫,py3全部小寫
第一個是 Socket,它提供了標準的 BSD Sockets API。
第二個是 SocketServer, 它提供了服務器中心類,可以簡化網絡服務器的開發

socket編程實現

  • 流程圖:

  • 說明:

    • 服務端

      1.服務端需要導入socket模塊,并創建套接字(實例化為一個對象)

      import?socket s?=?socket.socket()

      ?

      2.綁定套接字s到本地IP和端口

      ip_port?=?('127.0.0.1',8080) s.bind(ip_port)

      ?

      3.監聽連接

      s.listen(0)PS:0表示緩沖區可掛起的連接數量?0表示不限制,1表示?可掛起一個,那么意思就是連接一個、掛起一個,第三個再連接的話,就無法連接,會超時

      ?

      4.接收客戶端建立連接的請求

      conn,addr?=?s.accept() PS:conn為一個客戶端和服務器建立的連接,addr為客戶端ip

      ?

      5.接收客戶端的消息,并做相應處理

      recv_data?=?conn.recv(1024) send_data?=?recv_data.upper()??#將客戶端發送的內容轉換為大寫,注意。python3里面客戶端發送的都是二進制數據,python2里可以發送字符串

      ?

      6.給客戶端回消息

      conn.send(send_data)

      ?

      7.關閉連接

      conn.close()

      ?

    • 客戶端
      1.創建套接字

      import?sockets?=?socket.socket()

      ?

      2.連接服務端

      ip_port?=?('127.0.0.1',8080) s.connect(ip_port)

      ?

      3.給服務端發送消息

      send_data?=?input('請輸入:?') s.send(send_data.encode())??#注意py3發送的數據需要轉換為二進制,不能直接發送字符串

      ?

      4.接收服務端消息,并打印

      recv_data?=?s.recv(1024)print(recv_data.decode())??#服務端回應的是二進制,所以需要轉換為字符串

      ?

      5.關閉連接

      s.close()

      ?

以上就是一個簡單的客戶端和服務端socket連接,并發送消息,讀消息,回消息的過程,初學者可能一下子就懵了,請看下面的類比,

  • 類比

    通過上面的服務端和客戶端的一個簡單的交互,可以將其比作打電話,小明是服務端,小紅是客戶端

    小紅:你好 此時小紅是發消息,小明此時處于收消息的狀態
    小明:你好 小明收到小紅發的你好消息,做出回應,此時小明開始給小紅發消息,小紅處于收消息狀態
    最后小紅收到了小明的消息,小明此時已經掛斷電話,最后此次通信已斷

    注意此次通信只是一個簡單的交互過程,交互完成之后,則先完成方會主動關系連接。如果要持續通信,請繼續往下看

    • 小明

    • 小紅

    • 小紅和小明交互

  • 小紅在和小明打電話前得有個通信工具等等,所以需要找到一部手機,類同創建一個套接字

  • 小紅需要知道小明的電話號碼,并撥打電話,此步驟就等于客戶端連接服務端

  • 小明為了接收電話,他首先得買個手機,此步驟類同創建socket套接字

  • 小明有了手機,需要辦一張電話卡,此步驟類同綁定套接字搭配監聽的ip和端口

  • 小明有了手機和電話卡,則手機開機,處于待機狀態 此步驟類同監聽客戶端連接

  • 當小紅打電話進來之后,需要接電話,此類同于接收客戶端建立連接的請求

  • 實現服務端保持連接,不受客戶端斷開而斷開,并實現客戶端和服務端持續交互過程

    服務端

    import?socketip_port?=?('127.0.0.1',8080)s?=?socket.socket() s.bind(ip_port)s.listen(0)while?True:????#此次while循環用于客戶端斷開連接之后,重新循環建立新連接conn,addr?=?s.accept()????while?True:????#此while循環用于客戶端和服務器持續交互recv_data?=?conn.recv(1024)???????if?not?recv_data:?break???#判斷消息是否為空,當消息為空時,跳出循環,如果不判斷的話,客戶端那邊如果主動斷開連接,將會導致服務端處于一個不停的收消息的死循環中,因為連接已斷開,處于非阻塞狀態send_data?=?recv_data.upper()??#將客戶消息轉換為大寫conn.send(send_data)conn.close()

    ?

    客戶端:

    import?sockets?=?socket.socket()ip_port?=?('127.0.0.1',8080)s.connect(ip_port)while?True:send_data?=?input('請輸入:?')????if?send_data?==?'exit':breakelif?send_data?==?'':continues.send(send_data.encode())recv_data?=?s.recv(1024)????print(recv_data.decode()) s.close()

    ?

    運行服務端和客戶端,效果如下:

    請輸入:?hello HELLO 請輸入:?Jeck JECK 請輸入:?123 123請輸入:? 請輸入:?exitProcess?finished?with?exit?code?0

    ?

    socket模塊功能

    • socket 類型

      socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

      • 參數一:地址簇
           socket.AF_INET IPv4(默認)
           socket.AF_INET6 IPv6
           socket.AF_UNIX 只能夠用于單一的Unix系統進程間通信

      • 參數二:類型
          socket.SOCK_STREAM  流式socket , for TCP (默認)
          socket.SOCK_DGRAM   數據報式socket , for UDP
          socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。
          socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限于高級用戶或管理員運行的程序使用。
          socket.SOCK_SEQPACKET 可靠的連續數據包服務

      • 參數三:協議

       0  (默認)與特定的地址家族相關的協議,如果是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議

    • socket方法

      將套接字綁定到地址。address地址的格式取決于地址族。在AF_INET下,以元組(host,port)的形式表示地址。

      是否阻塞(默認True),如果設置False,那么accept和recv時一旦無數據,則報錯。

      接受連接并返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。
      接收TCP 客戶的連接(阻塞式)等待連接的到來

      連接到address處的套接字。一般,address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。

      同上,只不過會有返回值,連接成功時返回 0 ,連接失敗時候返回編碼,例如:10061

      關閉套接字

      接受套接字的數據。數據以字符串形式返回,bufsize指定最多可以接收的數量。flag提供有關消息的其他信息,通常可以忽略

      與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。

      將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小于string的字節大小。即:可能未將指定內容全部發送。

      將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。內部通過遞歸調用send,將所有內容發送出去。

      將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用于UDP協議。

      設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因為它們可能用于連接的操作(如 client 連接最多等待5s )

      返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。

      返回套接字自己的地址。通常是一個元組(ipaddr,port)

      • sk.fileno()

      • sk.getsockname()

      • sk.getpeername()

      • sk.settimeout(timeout)

      • sk.sendto(string[,flag],address)

      • sk.sendall(string[,flag])

      • sk.send(string[,flag])

      • sk.recvfrom(bufsize[.flag])

      • sk.recv(bufsize[,flag])

      • sk.close()

      • sk.connect_ex(address)

      • sk.connect(address)

      • sk.accept()

      • sk.listen(backlog)
          開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量。backlog等于5,表示內核已經接到了連接請求,但服務器還沒有調用accept進行處理的連接個數最大為5,這個值不能無限大,因為要在內核中維護連接隊列

      • sk.setblocking(bool)

      • sk.bind(address)

      套接字的文件描述符

    • 案例:模擬ssh

      • 服務端:

    import?socketimport??subprocessip_port?=?('127.0.0.1',8080)s?=?socket.socket() s.bind(ip_port)s.listen(0)while?True:conn,addr?=?s.accept()????while?True:????????try:recv_data?=?conn.recv(1024)????????????if?not?recv_data:?breakp?=?subprocess.Popen(str(recv_data,encoding='utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)????#執行shell命令,并將標準輸出和錯誤輸出放到緩沖區res?=?p.stdout.read()????????????if?not?res:send_data?=?p.stderr.read()????????????else:send_data?=?resdata_size?=?len(send_data)conn.send(send_data)????????except?Exception:????????????breakconn.close()

    ?

    *?客戶端

    import?socketip_port?=?('127.0.0.1',8080)s?=?socket.socket()s.connect(ip_port)while?True:send_data?=?input('>>:??')????if?send_data?==?'exit':exit()????elif?not?send_data:continues.send(bytes(send_data,encoding='utf-8'))recv_data?=?s.recv(1024)????print(recv_data.decode()) s.close()

    ?

    執行結果:

    >>:??df?-h Filesystem??????Size??Used?Avail?Use%?Mounted?on/dev/disk1??????112G???51G???62G??45%?/>>:??netstat?-lnt Active?Internet?connections Proto?Recv-Q?Send-Q??Local?Address??????????Foreign?Address????????(state)???? tcp4???????0??????0??172.16.23.42.57334?????23.83.227.252.8023?????ESTABLISHED tcp4???????0??????0??127.0.0.1.1080?????????127.0.0.1.57333????????ESTABLISHED tcp4???????0??????0??127.0.0.1.57333????????127.0.0.1.1080?????????ESTABLISHED tcp4???????0??????0??127.0.0.1.8080?????????127.0.0.1.57332????????ESTABLISHED tcp4???????0??????0??127.0.0.1.57332????????127.0.0.1.8080?????????ESTABLISHED tcp4???????0??????0??172.16.23.42.57328?????223.252.199.7.80???????CLOSE_WAIT? tcp4???????0??????0??172.16.23.42.57269?????163.177.72.143.993?????ESTABLISHED tcp4???????0??????0??10.255.0.10.57047??????203.130.45.175.9000????ESTABLISHED tcp4??????27??????0??172.16.23.42.57045?????163.177.90.125.993?????CLOSE_WAIT? tcp4???????0??????0??172.16.23.42.56988?????114.215.186.163.443????ESTABLISHED tcp4??????27??????0??172.16.23.42.56632?????163.177.72.143.993?????CLOSE_WAIT? tcp4???????0??????0??10.255.0.10.56374??????10.2 >>:??route?-n0.7.12.22??????????ESTABLISHED tcp4??????27??????0??172.16.23.42.56229?????163.177.90.125.993?????CLOSE_WAIT? tcp4???????0??????0??10.255.0.10.54889??????203.130.45.175.9000????ESTABLISHED tcp4???????0??????0??10.255.0.10.54605??????203.130.45.173.6929????ESTABLISHED tcp4???????0??????0??10.255.0.10.53228??????10.20.7.12.22??????????ESTABLISHED tcp4???????0??????0??10.255.0.10.53122??????203.130.45.175.9000????ESTABLISHED tcp4???????0??????0??172.16.23.42.52902?????42.62.89.250.1194??????ESTABLISHED tcp4???????0??????0??127.0.0.1.1337?????????127.0.0.1.52901????????ESTABLISHED tcp4???????0??????0??127.0.0.1.52901????????127.0.0.1.1337?????????ESTABLISHED tcp4???????0??????0??172.16.23.42.52899?????17.172.232.10.5223?????ESTABLISHED tcp4???????0??????0??172.16.23.42.52855?????17.252.236.157.5223????ESTABLISHED tcp4???????0??????0??172.16.23.42.52790?????223.252.199.6.6003?????ESTABLISHED tcp4???????0??????0??172.16.23.42.50124?????223.167.82.210.80??????ESTABLISHED tcp4???????0??????0??172.16.23.42.50026?????1

    ?

    從結果中發現,執行df -h 返回正常結果,執行netstat -lnt返回了一半的結果,繼續執行命令,仍然返回的是netstat -lnt的結果,這就發生了粘包現象

    • 粘包解決

      所謂粘包現象就是服務端把數據發過來之后,客戶端接收時會按一定大小來接收,決定此操作的是s.recv(1024),1024是每次接收的包大小,第一次沒有接收完的話,第二次會繼續接收原來的數據包,這就是粘包現象,解決辦法就是,服務端在發送數據時,現告訴客戶端本次數據的大小,然后再發送數據,客戶端收到數據大小之后,循環接收數據,知道接收完成再終止此次循環,這樣就可以拿到所有的數據,解決了粘包現象

      • 服務端改造:

    #!/usr/bin/env?python#?-*-?coding:?UTF-8?-*-#pyversion:python3.5#owner:fuzjimport?socketimport??subprocessip_port?=?('127.0.0.1',8080)s?=?socket.socket() s.bind(ip_port)s.listen(0)while?True:conn,addr?=?s.accept()????while?True:????????try:recv_data?=?conn.recv(1024)????????????if?not?recv_data:?breakp?=?subprocess.Popen(str(recv_data,encoding='utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)res?=?p.stdout.read()????????????if?not?res:send_data?=?p.stderr.read()????????????else:send_data?=?resdata_size?=?len(send_data)???#計算數據大小conn.send(bytes(str(data_size),encoding='utf-8'))??#發送數據大小res?=?conn.recv(1024)??#接收客戶端狀態conn.send(send_data)???#發送數據????????except?Exception:????????????breakconn.close()

    ?

    *?客戶端改造:

    import?socketip_port?=?('127.0.0.1',8080)s?=?socket.socket()s.connect(ip_port)while?True:send_data?=?input('>>:??')????if?send_data?==?'exit':exit()????elif?not?send_data:continues.send(bytes(send_data,encoding='utf-8'))recv_size?=?0data?=?b''data_size?=?str(s.recv(1024),encoding='utf-8')??#接收數據大小s.send(bytes('ok',encoding='utf-8'))??#發送此時的狀態????while?recv_size?<?int(data_size):????#循環接收數據,直到接收完所有數據recv_data?=?s.recv(1024)data?+=?recv_datarecv_size?+=?len(recv_data)????print(str(data,encoding='utf-8'))s.close()

    ?

    運行結果:發現已經解決上述問題

    >>:??df?-h Filesystem??????Size??Used?Avail?Use%?Mounted?on/dev/disk1??????112G???51G???62G??45%?/>>:??netstat?-lnt Active?Internet?connections Proto?Recv-Q?Send-Q??Local?Address??????????Foreign?Address????????(state)???? tcp4???????0??????0??172.16.23.42.57476?????223.252.199.7.80???????CLOSE_WAIT? tcp4???????0??????0??127.0.0.1.8080?????????127.0.0.1.57475????????ESTABLISHED tcp4???????0??????0??127.0.0.1.57475????????127.0.0.1.8080?????????ESTABLISHED tcp4???????0??????0??172.16.23.42.57474?????223.252.199.7.80???????LAST_ACK??? tcp4???????0??????0??172.16.23.42.57465?????23.83.227.252.8023?????ESTABLISHED tcp4???????0??????0??127.0.0.1.1080?????????127.0.0.1.57464????????ESTABLISHED tcp4???????0??????0??127.0.0.1.57464????????127.0.0.1.1080?????????ESTABLISHED tcp4???????0??????0??172.16.23.42.57461?????23.83.227.252.8023?????ESTABLISHED tcp4???????0??????0??127.0.0.1.1080?????????127.0.0.1.57460????????ESTABLISHED tcp4???????0??????0??127.0.0.1.57460????????127.0.0.1.1080?????????ESTABLISHED tcp4???????0??????0??172.16.23.42.57455?????163.177.72.143.993?????CLOSE_WAIT? tcp4???????0??????0??10.255.0.10.57047??????203.130.45.175.9000????ESTABLISHED tcp4??????27??????0??172.16.23.42.57045?????163.177.90.125.993?????CLOSE_WAIT? tcp4???????0??????0??172.16.23.42.56988?????114.215.186.163.443????ESTABLISHED tcp4??????27??????0??172.16.23.42.56632?????163.177.72.143.993?????CLOSE_WAIT? tcp4???????0??????0??10.255.0.10.56374??????10.20.7.12.22??????????ESTABLISHED tcp4??????27??????0??172.16.23.42.56229?????163.177.90.125.993?????CLOSE_WAIT? tcp4???????0??????0??10.255.0.10.54889??????203.130.45.175.9000????ESTABLISHED tcp4???????0??????0??10.255.0.10.54605??????203.130.45.173.6929????ESTABLISHED tcp4???????0??????0??10.255.0.10.53228??????10.20.7.12.22??????????ESTABLISHED tcp4???????0??????0??10.255.0.10.53122??????203.130.45.175.9000????ESTABLISHED tcp4???????0??????0??172.16.23.42.52902?????42.62.89.250.1194??????ESTABLISHED tcp4???????0??????0??127.0.0.1.1337?????????127.0.0.1.52901????????ESTABLISHED tcp4???????0??????0??127.0.0.1.52901????????127.0.0.1.1337?????????ESTABLISHED tcp4???????0??????0??172.16.23.42.52899?????17.172.232.10.5223?????ESTABLISHED tcp4???????0??????0??172.16.23.42.52855?????17.252.236.157.5223????ESTABLISHED tcp4???????0??????0??172.16.23.42.52790?????223.252.199.6.6003?????ESTABLISHED tcp4???????0??????0??172.16.23.42.50124?????223.167.82.210.80??????ESTABLISHED tcp4???????0??????0??172.16.23.42.50026?????123.151.10.187.14000???ESTABLISHED tcp4???????0??????0??172.16.23.42.49612?????163.177.90.125.993?????ESTABLISHED tcp4???????0??????0??127.0.0.1.49871????????127.0.0.1.49375????????ESTABLISHED tcp4???????0??????0??127.0.0.1.49375????????127.0.0.1.49871????????ESTABLISHED tcp4???????0??????0??127.0.0.1.49871????????127.0.0.1.49370????????ESTABLISHED tcp4???????0??????0??127.0.0.1.49370????????127.0.0.1.49871????????ESTABLISHED tcp4???????0??????0??192.168.123.164.49282??112.90.83.61.443???????ESTABLISHED

    ?

    socketserver 實現支持多客戶端

    上述ssh模擬客戶端只能支持一定數量的客戶端,受s.listen(0)參數限制。下面可以實現支持多客戶端操作

    SocketServer內部使用 IO多路復用 以及 “多線程” 和 “多進程” ,從而實現并發處理多個客戶端請求的Socket服務端。即:每個客戶端請求連接到服務器時,Socket服務端都會在服務器是創建一個“線程”或者“進程” 專門負責處理當前客戶端的所有請求

    • ThreadingTCPServer

      ThreadingTCPServer實現的Soket服務器內部會為每個client創建一個 “線程”,該線程用來和客戶端進行交互

    • 實現步驟:

      • 1.創建一個類,并繼承SocketServer.BaseRequestHandler 的類

      • 2.在新類中需要創建一個handle的方法

      • 3.啟動ThreadingTCPServer

    代碼如下:

    import?socketserverimport?subprocessclass?MyServer(socketserver.BaseRequestHandler):??#繼承????def?handle(self):???#handle方法。注意此時send和recv時調用的self.request方法self.request.sendall(bytes('Welcome',encoding='utf-8'))????????while?True:????????????try:recv_data?=?self.request.recv(1024)????????????????if?not?recv_data:?breakp?=?subprocess.Popen(str(recv_data,?encoding='utf-8'),?shell=True,?stdout=subprocess.PIPE,stderr=subprocess.PIPE)res?=?p.stdout.read()????????????????if?not?res:send_data?=?p.stderr.read()????????????????else:send_data?=?res????????????????if?not?send_data:send_data?=?'no?output'.encode()data_size?=?len(send_data)self.request.send(bytes(str(data_size),?encoding='utf-8'))self.request.recv(1024)self.request.send(send_data)????????????except?Exception:????????????????breakif?__name__?==?'__main__':server?=?socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)????#啟動serverserver.serve_forever()

    ?

    PS:SocketServer.BaseRequestHandler類源碼:其定義了三個方法:setup(),handle()he finish()
    執行順序為:setup(0-->handle()-->finish()
    ```

    class?BaseRequestHandler:def?__init__(self,?request,?client_address,?server):self.request?=?requestself.client_address?=?client_addressself.server?=?serverself.setup()????try:self.handle()????finally:self.finish()def?setup(self):????passdef?handle(self):????passdef?finish(self):????passSocketServer.BaseRequestHandler

    ?

    ```

    • ThreadingTCPServer源碼剖析

    • 內部調用流程

      • 啟動服務端程序

      • 執行 TCPServer.__init__ 方法,創建服務端Socket對象并綁定 IP 和 端口

      • 執行 BaseServer.__init__ 方法,將自定義的繼承自SocketServer.BaseRequestHandler 的類 MyRequestHandle賦值給 self.RequestHandlerClass

      • 執行 BaseServer.server_forever 方法,While 循環一直監聽是否有客戶端請求到達 ...

      • 當客戶端連接到達服務器

      • 執行 ThreadingMixIn.process_request 方法,創建一個 “線程” 用來處理請求

      • 執行 ThreadingMixIn.process_request_thread 方法

      • 執行 BaseServer.finish_request 方法,執行 self.RequestHandlerClass() 即:執行 自定義 MyRequestHandler 的構造方法(自動調用基類BaseRequestHandler的構造方法,在該構造方法中又會調用 MyRequestHandler的handle方法)



    轉載于:https://blog.51cto.com/studys/1827235

    總結

    以上是生活随笔為你收集整理的socket编程初级的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。