socket基本用法


socket介紹

1、什么是socket

  socket是應用層與傳輸層中間的一個軟件抽象層,它是一組接口。它把TCP/IP這些復雜的協議統一封裝起來

  這樣我們只要知道如何使用socket就好,就已經符合了傳輸層往下的一大串協議

2、為什么要使用socket

  如果沒有socket而我們寫的代碼又要讓別人能正確解析,就需要一層層往下研究協議,寫出符合協議的代碼

  而我們大家傳輸層往下的代碼基本一樣,所以把這些代碼封裝成一個模塊,方便大家使用,不用去考慮傳輸層

  以下的東西

3、socket發展

  套接字起源於20世紀70年代,一開始,套接字被設計用在同一台主機上多個應用程序的通訊,這也被稱為

  進程間通訊或者IPC。后來網絡發展,又被應用於網絡協議上。

  套接字有兩種(或者稱為有兩個種族):

    基於文件類型的套接字家族:名字  --> AF_UNIX

    基於網絡類型的套接字家族:名字  --> AF_INET被用於ipv4       AF_INET6被用於ipv6

socket用法

# 需要明確的是:

  關於網絡協議和socket相關概念,對於所有編程語言都是一致的,區別僅僅是個編程語言的函數名稱不同

1、服務器端

import socket
# 獲得socket對象 AF_INET表示基於網絡的套接字
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# SOCK_STREAM表示的是TCP協議  SOCK_DGRAM表示的是UDP協議
# 將socket對象綁定ip地址和端口號
sk.bind(('127.0.0.1',8080))     # 參數需要傳入一個元組
# 相當於電話的開機 括號里的參數表示可以同時接收5個請求
sk.listen(5)

# 一般服務器都不需要關閉,所以加個while循環
while True:
# 進入監聽狀態,等待別人鏈接過來,有兩個返回值,一個是對方的socket對象,一個是對方的ip以及端口
    client,addr = sk.accept()
    # 收發消息一般都需要多次循環發送,也加個while循環
    while True:
        try: # 如果對面強行關閉,為了程序不崩,就需要異常處理
            msg = client.recv(1024)    # recv表示接收,括號里是最大接收字節
            if not msg: # 如果接收到的消息是空字符串,就表示對面正常退出,需要打斷循環
                break
            # send表示發送數據,發送的數據必須是二進制數據
            client.send('二進制數據'.encode('utf-8'))
        except Exception as e:
            print(e)
            break
    client.close()  # 關閉對面傳過來的對象
sk.close()  # 關閉服務器的socket對象

2、客戶端

import socket
# 獲取socket對象,括號里默認是AF_INET  和 SOCK_STREAM  所以可以不寫
sk = socket.socket()
# 鏈接到服務器端 括號里也是一個元組,包含ip地址以及對方的端口號
sk.connect(('127.0.0.1',8080))      # 自己的端口號系統會隨機分配,不需要設置

while True: # 收發信息也需要循環
    try: # 為了防止服務器非正常下線而導致客戶端直接崩潰,需要加入異常處理
        cmd = input('>>(q:退出)')
        if cmd == 'q':  # 給客戶端一個可以正常退出的方法
            break
        if not cmd: # 如果直接敲回車,那么cmd就是空字符串,TCP協議對此進行優化
            continue    # 正常收發消息過程中,你傳入空字符串,它不會幫你發送
        sk.send(cmd.encode('utf-8'))
# 如果上面沒有做空判斷,輸入空會直接跳過send這步,這樣客戶端和服務器端都處於接收狀態,卡住不動
        sk.recv(1024) 
    except Exception as e:
        print(e)
        break
sk.close() # 關閉socket對象

3、基於TCP的socket通訊流程圖

socket常見問題

1、端口占用

報錯信息

Traceback (most recent call last):
File "F:/pyprogram/day31/homework/topic2/test.py", line 6, in <module>
sk.bind(('127.0.0.1',8080))
OSError: [WinError 10048] 通常每個套接字地址(協議/網絡地址/端口)只允許使用一次。

問題發生的原因

  1.可能是由於你已經啟動了服務器程序,卻又再次啟動了服務器程序,同一個端口不能被多個進程使用導致

  2.三次握手或四次揮手時,發生了異常導致對方程序已經結束而服務器任然處於time_wait狀態導致

  3.在高並發的場景下,由於連接的客戶端太多,也會產生大量處於time_wait狀態連接

解決的方案

  第一種重新選擇端口后就可以,或者把之前的占用端口的進程關閉掉

  第二三種以后再說

2、強行關閉鏈接

報錯信息

Traceback (most recent call last):
File "F:/pyprogram/day31/part2/客戶端.py", line 12, in <module>
client.send(msg.encode('utf-8'))
ConnectionResetError: [WinError 10054] 遠程主機強迫關閉了一個現有的連接。

問題發生的原因

  當客戶端與服務器鏈接成功后,如果一方沒有執行close,而是直接強行終止程序(或是遇到異常被迫終止)

  都會導致另一方發生問題

  在windows下,接收數據的一方在recv函數處將拋出異常

解決的方案

  用try...except語法抓取異常,抓到對方強退異常后並處理

3、客戶端正常結束,服務器端無限循環空字符

報錯信息

  不會產生報錯信息,但是服務器端會一直循環空字符串

問題發生的原因

  客戶端正常close()之后,會給服務器發送一個空字符串,

  如果服務器端沒有發送數據的話,就會一直循環接收這個空字符串,占用CPU資源

解決的方案

  在服務器端接收的地方加上空字符串判斷,如果是空字符串,就表示對方客戶端退出了

  那么判斷空成功,讓自己也退出循環


免責聲明!

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



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