socket套接字-TCP \ UDP


socket套接字編程

  目標: 根據socket模塊提供的接口函數,進行組合使 用完成基於tcp或者udp的網路編程。

  套接字 : 完成上述目標的一種編程手段,編程方案

套接字分類:
  流式套接字(SOCK_STREAM): 傳輸層基於tcp協議 的套接字編程方案。
  數據報套接字(SOCK_DGRAM): 傳輸層基於udp協議的套接字編程方案。
  底層套接字(SOCK_RAM):訪問底層協議的套接字編程。

* 面向連接的傳輸--tcp協議--可靠地--流式套接字
* 面向無連接傳輸--udp協議--不可靠--數據報套接字

-------------------------------------------------------------------

本地套接字

linux 文件
  b(塊設備文件) c(字符設備文件) d(目錄)
  -(普通文件) l(鏈接) s(套接字) p(管道)

作用:用於本地不同的程序間進行通信

創建流程
  1.創建本地套接字
  sockfd = socket(AF_UNIX,SOCK_STREAM)
  2.綁定本地套接字文件
  * 選定文件位置和名稱
  * sockfd.bind(path)
  3. 監聽 listen()
  4. 消息收發, recv send

  
from socket import *
import os

#確定套接字文件
sock_file = './sock_file'

#判斷文件是否已經存在
if os.path.exists(sock_file):
    os.remove(sock_file)

#創建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)

#綁定套接字文件
sockfd.bind(sock_file)

#監聽
sockfd.listen(3)

#消息收發
while True:
    c,addr = sockfd.accept()
    while True:
        data = c.recv(1024)
        if data:
            print(data.decode())
            c.send(b"Receive")
        else:
            break 
    c.close()
sockfd.close()
unix_recv
  
from socket import * 

#確保通信兩端用的是同一個套接字文件
sock_file = "./sock_file"

#創建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)

#連接另一端
sockfd.connect(sock_file)

#收發消息
while True:
    msg = input(">>")
    if msg:
        sockfd.send(msg.encode())
        print(sockfd.recv(1024).decode())
    else:
        break 

sockfd.close()
unix_send

 

os.path.exists(path)
    功能 : 判斷一個文件是否存在
    參數:目標文件
    返回值 : 存在返回True 否則 False

os.remove() os.unlink()
    功能 : 刪除一個文件
    參數 : 目標文件

傳輸層-TCP

tcp協議的數據傳輸 (面向連接的傳輸服務)

  傳輸特征 : 提供可靠的數據傳輸,可靠性指數據傳輸過程中無丟失,無失序,無差錯,無重復。

  實現手段: 數據傳輸斷開前都需要進行傳輸和斷開的確認

三次握手 : tcp傳輸在數據傳輸前建立連接的過程 1. 客戶端向服務器發送連接請求 2. 服務器收到請求后,回復確認消息,表示允許連接 3. 客戶端收到服務器回復,進行最終標志發送確認連接 四次揮手 : tcp傳輸在連接斷開前進行斷開確認的過程 1. 主動發發送報文告知被動方要斷開連接 2. 被動方收到請求后立即返回報文告知已經准備斷開 3. 被動方准備就緒后再次發送報文告知可以斷開 4. 主動方發送消息,確認最終斷開

應用情況 : 適用於傳輸較大的文件,網絡情況良好,需要保證傳輸可靠性的情況。
   比如: 網頁的獲取,文件下載,郵件傳輸,登錄注冊

 

tcp套接字服務端編程

  import socket

  1. 創建套接字

  sockfd = socket.socket(socket_family = AF_INET,
  socket_type = SOCK_STREAM,
  proto = 0)
    功能 : 創建套接字
    參數 : socket_family : 選擇地址族類型 AF_INET
           socket_type : 套接字類型 SOCK_STREAM 流式
        SOCK_DGRAM 數據報
        proto : 選擇子協議類型 通常為0
    返回值 : 返回套接字對象


  2. 綁定服務端地址
  sockfd.bind(addr)
    功能 : 綁定IP地址
    參數 : 元組 (ip,port)

    localhost  或 127.0.0.1    可以被本機用 127.0.0.1
    192.168.205.127            可以被所有人用192.168.205.127訪問
    0.0.0.0                    可以被所有人用192.168.205.127訪問
    0.0.0.0                    也可被自己用127.0.0.1訪問

 

  3. 設置監聽套接字
  sockfd.listen(n)
    功能:將套接字設置為監聽套接字,創建監聽隊列
    參數:n 表示監聽隊列大小

    * 一個監聽套接字可以連接多個客戶端套接字

 

  4. 等待處理客戶端連接請求
  connfd,addr = sockfd.accept()
    功能 : 阻塞等待處理客戶端連接
    返回值: connfd 客戶端連接套接字
    addr 連接的客戶端地址

    * 阻塞函數 : 程序運行過程中遇到阻塞函數則暫停運行,直到某種阻塞條件達成再繼續運行。

 

  5. 消息收發

  data = connfd.recv(buffersize)
     功能 : 接收對應客戶端消息
     參數 : 一次最多接收多少字節
     返回值 : 接收到的內容

     * 如果沒有消息則會阻塞

  n = connfd.send(data)
    功能 : 發送消息給對應客戶端
    參數 : 要發送的內容,必須是bytes格式
    返回值 : 返回實際發送消息的大小

  6. 關閉套接字
  sockfd.close()
    功能: 關閉套接字

# -*- coding: utf-8 -*-

from socket import *


# 創建套接字
s = socket(AF_INET, SOCK_STREAM)

# 綁定服務端地址
s.bind(('127.0.0.1', 8888))

# 設置監聽套接字
s.listen(5)

# 循環 等待處理客戶端的連接請求
while True:
    print('Waiting for connect...')
    connfd, addr = s.accept()
    print('Connect from', addr)
    while True:
        # 接收客戶端消息
        data = connfd.recv(1024).decode()
        if not data:
            break
        print(data)
        # 向客戶端發送消息
        n = connfd.send(b'Rrceive your message')
        print("發送%d字節" % n)
    connfd.close()

# 關閉套接字
s.close()
TCP服務端的基本實現

-------------------------------------------------------------------

tcp客戶端
  1. 創建套接字
  * 必須相同類型的套接字才能通信
  2. 建立連接
  sockfd.connect(servr_addr)
    功能 : 建立連接
    參數 : 元組, 服務端地址
  3. 消息收發
  * 消息收發要和服務端配合,避免兩邊都出現recv阻塞
  4. 關閉套接字

 

# -*- coding: utf-8 -*-

from socket import *

# 創建套接字
s = socket(AF_INET, SOCK_STREAM)

# 發起連接
server_addr = ('127.0.0.1', 8888)
s.connect(server_addr)

while True:
    # 消息發送接收
    data = input('發送>>:')
    s.send(data.encode())
    if not data:
        break
    else:
        data = s.recv(1024)
        print('接收到>>:', data.decode())
TCP客戶端的基本實現

--------------------------------------------------------------------

套接字傳輸注意事項
1. 監聽套接字存在客戶端即可發起連接,但是最終連接的處理需要accept進行處理
2. 如果連接的另外一段退出,則recv會立即返回空子串不再阻塞。
3. 當連接的另一端退出時,再試圖send發送就會產生BrokenPipeError


網絡收發緩沖區

緩沖區作用 :協調收發(處理)速度
              減少交互次數

send和recv實際上是和緩沖區進行交互,發送緩沖區滿時就無法發送,接收緩沖區滿時recv才阻塞


TCP粘包

產生原因:
tcp套接字以字節流方式傳輸,沒有消息邊界
發送和接收並不能保證每次發送都及時的被接收

影響:如果每次發送內容表達一個獨立的含義此時可能需要處理粘包防止產生歧義

處理方法:
1. 每次發送的消息添加結尾標志 (人為增加消息邊界)
2. 發送數據結構體
3. 協調收發速度,每次發送后都預留接收時間
TCP粘包的基本處理

傳輸層-UDP

udp協議的傳輸 (面向無連接的傳輸服務)

  傳輸特點 : 不保證傳輸的可靠性,傳輸過程沒有連接和斷開的流程,數據收發自由。

  使用情況 : 網絡情況較差,對傳輸可靠性要求不高,需要提升傳輸效率。不便連接,需要靈活收發消息。
     比如:網絡視頻,群聊,廣播發送

 

udp套接字的服務端

  1. 創建數據報套接字
  sockfd = socket(AF_INET,SOCK_DGRAM)

  2. 綁定地址
  sockfd.bind(addr)

  3. 消息的收發

  data,addr = sockfd.recvfrom(buffersize)
    功能 : 接收UDP消息
    參數 : 每次最多接收多大的消息
    返回值:data 接收到的數據
    addr 消息發送端的地址

    * 一次接收一個數據報,如果數據報大小大於buffersize則會丟失部分消息

  sockfd.sendto(data,addr)
    功能 : 發送udp消息
    參數 : data 發送的消息 bytes格式
    addr 目標地址
    返回值: 發送的字節數

  4. 關閉套接字
  sockfd.close()

# -*- coding: utf-8 -*-

from socket import *

# 創建套接字
s = socket(AF_INET, SOCK_DGRAM)

# 綁定地址
server_addr = ('127.0.0.1', 8888)
s.bind(server_addr)

while True:
    print('Waiting for connect...')
    # 消息接收,
    # 注意一次接收一個數據報,如果數據報大小大於參數,則會丟失部分數據
    data, addr = s.recvfrom(1024)
    print(data.decode())

    # 發送消息
    s.sendto('Recevie your message'.encode(), addr)
s.close()
UDP服務器的基本實現

補充函數
  sendall(data)
    功能 : 發送tcp消息
    參數 : 要發送的內容,bytes格式
    返回值 : 成功返回 None 失敗產生異常

--------------------------------------------------------------------------

udp客戶端
  1. 創建套接字
  socket(AF_INET,SOCK_DGRAM)
  2. 消息收發
  recvfrom/sendto
  3. 關閉套接字
  close()

# -*- coding: utf-8 -*-

from socket import *
import sys

if len(sys.argv) < 3:
    print('''
        argv is error!
        run as
        python udp_client.py 127.0.0.1 8888''')

# 從命令行輸入IP,端口
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST, PORT)

# 創建套接字
s = socket(AF_INET, SOCK_DGRAM)

while True:
    data = input('發送>>:').encode()
    if not data:
        break
    s.sendto(data, ADDR)
    data1, addr = s.recvfrom(1024)
    print(data1.decode())
s.close()
UDP客戶端的基本實現

 

1. sys.argv 屬性
    功能 : 獲取命令行參數,得到一個列表

    命令本身是 argv[0]
    后面的參數從argv[1]開始,默認以空格分隔
    使用引號引起來的內容算作一個整體
    命令行參數都以字符串放入列表

2. Linux系統下:
    在程序的第一行加
    #!/usr/bin/env python3
    添加程序的執行權限
    chmod  755  file.py
    修改后即可通過 ./file.py  運行程序
-----------------------------------------------------------------

套接字對象 

s代表一個套接字

s.family  : 獲取套接字地址族類型
s.type : 獲取套接字類型
s.getsockname() : 獲取套接字的綁定地址

s.fileno() : 獲取套接字的文件描述符
文件描述符 : 每一個IO事件操作系統都會分配一個不同的正整數作為編號,改正整數即為這個IO的文件描述符。

* 文件描述符是操作系統識別IO的唯一標志
stdin ---> 0
stdout --> 1
stderr --> 2

s.getpeername() : 獲取客戶端連接套接字的對應地址

s.setsockopt(level,option,value)
功能:設置套接字選項,豐富或者修改套接字屬性功能
參數: level 選項類別   SOL_SOCKET
       option 具體選項
       value  選項值

s.getsockopt(level,option)
功能:獲取套接字選項值
參數: level 選項類別   SOL_SOCKET
       option 具體選項
返回值:選項值

* 如果要設置套接字選項,最好在創建套接字之后立即設置
普及套接字的對象
tcp套接字編程和udp套接字編程區別 1. 流式套接字使用字節流的方式傳輸,數據報套接字以數據報形式傳輸數據 2. tcp會有粘包現象,udp有消息邊界不會形成粘包 3. tcp 可以保障數據傳輸完整性,udp則不保證 4. tcp 需要進行listen accept 操作,udp不需要 5. tcp收發消息使用新的套接字,recv send。udp使用recvfrom,sendto

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM