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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

网络编程(part9)--socket套接字编程之TCP套接字

發(fā)布時間:2023/12/19 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络编程(part9)--socket套接字编程之TCP套接字 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

鄙人學習筆記


文章目錄

    • 套接字介紹
        • 定義
        • 套接字分類(針對TCP和UDP的分類)
      • TCP套接字編程
        • 服務端流程
        • 代碼實現
          • 舉個例子
        • 客戶端流程
        • 代碼實現
          • 舉個例子
        • TCP套接字數據傳輸特點
          • 做個練習
        • 網絡收發(fā)緩沖區(qū)
          • 舉個例子
        • TCP粘包


套接字介紹

定義

套接字是實現網絡編程進行數據傳輸的一種技術手段

套接字分類(針對TCP和UDP的分類)

①流式套接字(SOCK_STREAM): 以字節(jié)流方式(就像是管道中的水流一樣)傳輸數據,實現TCP網絡傳輸方案。

②數據報套接字(SOCK_DGRAM):以數據報形式(就像是用瓶子打包好的水一樣)傳輸數據,實現UDP網絡傳輸方案。

TCP套接字編程

服務端流程

先來看一個流程圖:

代碼實現

  • 創(chuàng)建套接字
  • sockfd=socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0) 功能:創(chuàng)建套接字對象 參數: socket_family 網絡地址類型:AF_INET表示ipv4 socket_type 套接字類型:SOCK_STREAM 流式;SOCK_DGRAM 數據報 proto(在應用層網絡編程中用不到) 選擇子協(xié)議:通常為0 返回值: 套接字對象

    備注:底層/系統(tǒng)層套接字也針對某種協(xié)議,這些協(xié)議,有的時候會產生一些分支,也就是子協(xié)議。那么為啥在應用層網絡編程中用不到proto這個參數呢?因為TCP協(xié)議和UDP協(xié)議沒有子協(xié)議,所以參數設為0,即不選擇任何子協(xié)議。

  • 綁定地址
  • sockfd.bind(addr) 功能: 綁定本機網絡地址(如果不寫本機網絡地址,則會報錯) 參數: 二元元組 (ip,port) 比如('0.0.0.0',8888)

    備注:addr為一個元組,這個元組有兩個元素一個是網絡地址(IP),一個是端口號(port)。

  • 設置監(jiān)聽
  • sockfd.listen(n) 功能 : 將套接字設置為監(jiān)聽套接字,確定監(jiān)聽隊列大小 參數 : 監(jiān)聽隊列大小

    備注1:并不是所有的套接字都具備監(jiān)聽的功能,即被客戶端連接的功能。通過調用listen,我們的套接字對象才能被客戶端連接。

    備注2:一個服務端套接字對象可以同時連接多個客戶端,與客戶端進行連接需要進行3次握手,且連接的過程需要一個一個來進行。比如說,同時有5個客戶端發(fā)起了連接請求,這時我們就會形成一個”先來后到”的隊列,對這5個客戶端的連接請求,一個一個進行處理。比如說,我們設置監(jiān)聽隊列大小為5,表示隊列最多容納5個客戶端等待處理。但有10個客戶端同時發(fā)起了連接請求,這時,服務端會先處理第1個發(fā)起請求的那個客戶端,前2~6個客戶端則在等待隊列中,最晚發(fā)起請求的4個客戶端,則會被拒絕。

  • 等待處理客戶端連接請求
  • connfd,addr = sockfd.accept() 功能: 阻塞等待處理客戶端請求(等待客戶端連接,啥時候有客戶端連接,才結束阻塞) 返回值:connfd : 客戶端連接套接字.客戶端連接后,新產生的專門與客戶端進行通信的套接字對象。則每當有一個客戶端連接時,都會有一個新的專用于連接的套接字對象產生。就像對每個客戶端提供一個專屬管家一樣,提升用戶端體驗。我們如果找到一個連接套接字,就可以知道它對應的客戶端是誰addr :連接的客戶端地址

    阻塞函數:當程序運行到這個函數時,則程序暫停執(zhí)行。程序在等待某種條件,當條件滿足后才繼續(xù)執(zhí)行,如:input()、sleep()。阻塞函數在IO操作中很常見。

  • 消息收發(fā)
  • data = connfd.recv(buffersize) 功能 : 接受客戶端消息 參數 :每次最多接收消息的大小(最多一次接受多少字節(jié)的消息) 返回值: 接收到的內容 n = connfd.send(data) 功能 : 發(fā)送消息 參數 :要發(fā)送的內容-bytes格式(字節(jié)串格式) 返回值: 發(fā)送的字節(jié)數

    備注1:所有和網絡相關的消息傳送都得是bytes格式(字節(jié)串格式).發(fā)送和接收都得是字節(jié)串。

    備注2:我們發(fā)送/接收的是字節(jié)串,但平時寫/讀的是字符串,所以我們需要用encode()/dencode()進行轉換。

  • 關閉套接字
  • sockfd.close() 功能:關閉套接字
    舉個例子

    服務端代碼:

    import socket#創(chuàng)建流式套接字 sockfd = socket.socket(socket.AF_INET, \socket.SOCK_STREAM) #綁定地址 sockfd.bind(('127.0.0.1', 8888))#設置監(jiān)聽 sockfd.listen(5)#等待處理客戶端鏈接 print("Waiting for connect....") connfd, addr = sockfd.accept()#收發(fā)消息 data = connfd.recv(1024) print("接收到的消息:", data.decode())n = connfd.send(b'Receive your message') print("發(fā)送了 %d 個字節(jié)數據" % n)#關閉套接字 connfd.close() sockfd.close()

    我們運行一下,得到以下結果:

    結果說明,程序阻塞在accept,等待客戶端連接。所以這時,我們就要找一個客戶端與之連接,下一節(jié),我們就學一下客戶端流程。

    客戶端流程

    先看看流程圖:

    代碼實現

  • 創(chuàng)建套接字(和客戶端代碼相同)
    注意:只有相同類型的套接字才能進行通信

  • 請求連接

  • sockfd.connect(server_addr) 功能:連接服務器 參數:元組 服務器地址
  • 收發(fā)消息(和客戶端代碼相同)
    注意: 為了防止兩端都阻塞,故recv和send要配合使用。比如,服務端是先send后recv,那么客戶端則需要先recv后send。否則,若兩端同時recv,則兩端都會阻塞。

  • 關閉套接字(和客戶端代碼相同)

  • 舉個例子

    客戶端代碼:

    服務端代碼:

    我們想要運行,但是若先運行客戶端,則會報錯。所以我們要先啟動服務端

    ①運行服務端

    查看服務端的控制臺Console 2/A輸出的結果:

    ②運行客戶端

    我們先看一下服務端的控制臺Console 2/A輸出的結果:

    我們再看一下客戶端的控制臺Console 3/A輸出的結果:

    消息收發(fā)成功了,很好~

    TCP套接字數據傳輸特點

    ①TCP連接中,當一端退出,另一端如果阻塞在recv,此時recv會立即返回一個空字串。
    ②TCP連接中,如果一端已經不存在,仍然試圖通過send發(fā)送信息,則會產生BrokenPipeError
    ③一個監(jiān)聽套接字可以同時連接多個客戶端,也能夠重復被連接。

    做個練習

    要求1:一個客戶端退出了,服務器不會退出,而是連接下一個客戶端
    要求2:客戶端可以不停的循環(huán)發(fā)送消息

    服務端代碼:

    #-*- coding: utf-8 -*-import socket#創(chuàng)建流式套接字 sockfd = socket.socket(socket.AF_INET, \socket.SOCK_STREAM)#綁定地址 sockfd.bind(('127.0.0.1', 8888))#設置監(jiān)聽 sockfd.listen(5)#等待處理客戶端鏈接 while True:print("Waiting for connect....")try:connfd, addr = sockfd.accept()print("Connect from:", addr)except KeyboardInterrupt:print("退出服務")break# 收發(fā)消息while True:data = connfd.recv(1024)# 得到空則退出循環(huán)if not data:breakprint("接收到的消息:", data.decode())n = connfd.send(b'Receive your message')print("發(fā)送了 %d 個字節(jié)數據" % n)connfd.close()#關閉套接字 sockfd.close()

    客戶端代碼:

    #-*- coding: utf-8 -*-from socket import *#創(chuàng)建tcp套接字 sockfd = socket()#發(fā)起連接 server_addr = ('127.0.0.1',8888) sockfd.connect(server_addr)#收發(fā)消息 while True:data = input("消息:")if not data:breaksockfd.send(data.encode())data = sockfd.recv(1024)print("From server:",data.decode())#關閉 sockfd.close()

    先運行服務端,再運行客戶端并發(fā)送消息。

    客戶端結果:

    服務端結果:

    網絡收發(fā)緩沖區(qū)

    ①網絡緩沖區(qū)有效的協(xié)調了消息的收發(fā)速度、緩解收發(fā)壓力。
    ②send和recv實際是向緩沖區(qū)發(fā)送接收消息,當緩沖區(qū)不為空recv就不會阻塞。

    網絡緩沖區(qū)完成消息傳遞示意圖:

    舉個例子

    還記得我們上面那個練習么?其中一段代碼是:


    它表示,每次最多可接收消息大小為1024個字節(jié).

    我們來更改一下最大可接受的消息字節(jié)數為5:

    用客戶端發(fā)送消息:

    服務端結果:

    客戶端結果:

    則說明,客戶端1次發(fā)送,服務端分3次接收了,同時返回給服務端3句’ Receive your message’也就是說,服務端的內層循環(huán)(如下圖所示)循環(huán)了3次

    備注1:所以我們一開始說【函數recv()是阻塞函數,當發(fā)送方不發(fā)消息,就會阻塞】。但其實更準確的來說,并不是發(fā)送方不發(fā)消息就會阻塞,接收方其實是先從緩沖區(qū)去拿消息,當緩沖區(qū)為空時,會阻塞,當緩沖區(qū)一直有消息時,則會一直獲取消息,一直不阻塞。

    注意! 運行上面的代碼時,客戶端有時候也會出現以下這種情況:

    按照上面的說法,當客戶端發(fā)送1條消息,客戶端會返回3條’ Receive your message’。但是也會出現,當第1條’ Receive your message’進入客戶端緩沖區(qū),客戶端就從客戶端緩沖區(qū)recv()了這條消息,導致剩下2條’ Receive your message’停留在緩沖區(qū)沒被接受。等到下一次,客戶端再從緩沖區(qū)中recv()時,才把剩下’ Receive your message’接收到。

    備注1:我們第一次運行代碼時,服務端快速的連續(xù)3次send()了1條’ Receive your message’,客戶端1次recv()了3條’ Receive your message’。接收端一次接收了多條發(fā)送端消息,我們稱這種情況叫:粘包

    TCP粘包

    • 原因
      TCP以字節(jié)流方式傳輸,沒有消息邊界。多次發(fā)送的消息被一次接收,此時就會形成粘包。

    • 影響(分情況,比如:傳一部電影/發(fā)送用戶名消息)
      ①如果發(fā)送的內容中,每個信息都有獨立的含義(比如:發(fā)送幾個用戶姓名),需要接收端獨立解析,此時,這時粘包會有影響。
      ②如果發(fā)送的是一個字節(jié)流文件,是一個連在一起的整體(比如:發(fā)送一部電影),接收端無需單獨解析,而是將所有接收到內容最終合成一個整體,這時粘包沒啥影響。

    • 處理方法
      ①人為的添加消息邊界,比如:每次發(fā)送一個姓名時,在姓名后加一個特殊符號。
      ②控制發(fā)送速度(因為粘包的產生是因為收發(fā)速度不協(xié)調),比如用sleep()函數調節(jié)。

    總結

    以上是生活随笔為你收集整理的网络编程(part9)--socket套接字编程之TCP套接字的全部內容,希望文章能夠幫你解決所遇到的問題。

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