python socket--"套接字"

一:TCP連接之三次握手與四次揮手

    TCP是一種面向連接的、可靠的、基於字節流的傳輸層通信協議。所以,兩臺遵循TCP的主機在彼此交換數據包之前必須先建立一個TCP連接。

    TCP通過三次握手建立連接:

    1,客戶端發送SYN(SEQ=x)報文給服務器端,進入SYN_SEND狀態。
    2,服務器端收到SYN報文,迴應一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態。
    3,客戶端收到服務器端的SYN報文,迴應一個ACK(ACK=y+1)報文,進入Established狀態。

    三次握手完成,TCP客戶端和服務器端成功地建立連接,可以開始傳輸數據了。



    TCP終止一個連接要經過四次揮手,這是由TCP的半關閉(half-close)造成的:

    1,某個應用進程首先調用close,稱該端執行“主動關閉”(active close)。該端的TCP於是發送一個FIN分節,表示數據發送完畢。
    2, 接收到這個FIN的對端執行 “被動關閉”(passive close),這個FIN由TCP確認。
          FIN的接收也作爲一個文件結束符(end-of-file)傳遞給接收端應用進程,放在已排隊等候該應用進程接收的任何其他數據之後
    3,一段時間後,接收到這個文件結束符的應用進程將調用close關閉它的套接字。這導致它的TCP也發送一個FIN
    4,接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。

    在步驟2與步驟3之間,從執行被動關閉一端到執行主動關閉一端流動數據是可能的,這稱爲“半關閉”(half-close)。




二:何謂 "套接字"--socket

    伯克利套接字(Internet Berkeley sockets) ,又稱爲BSD 套接字(BSD sockets)是一種應用程序接口(API),用於網際套接字( socket)與Unix域套接字,包括了一個用C語言寫成的應用程序開發庫,主要用於實現進程間通訊,在計算機網絡通訊方面被廣泛使用。

    Berkeley套接字應用程序接口形成了事實上的網絡套接字的標準精髓。這套應用程序接口也被用於Unix域套接字(Unix domain sockets),後者可以在單機上爲進程間通訊(IPC)的接口。

    本篇主要講的是網際套接字socket,它大多源自Berkeley套接字標準。
    socket是一種操作系統提供的進程間通信機制,也是操作系統爲應用程序提供的一種應用程序接口(API)
    在套接字接口中,以IP地址及通信端口組成套接字地址(socket address)。遠程的套接字地址,以及本地的套接字地址完成連接後,再加上使用的協議(protocol),這個五元組(five-element tuple),作爲套接字對(socket pairs),之後就可以彼此交換數據

     socket本質是編程接口(API),是對TCP/IP協議族傳輸層及其以下層的封裝

     通過socket這個編程接口,可以實現端口到端口的通信,即能實現任何應用程序之間的通信



三:python中socket的實現--socket模塊

    TCP是一種面向連接的、可靠的、基於字節流的傳輸層通信協議。

#創建socket對象最常用方法(還有很多其他創建方法)
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    family(不全):
    socket.AF_INET        IPv4(默認)
    socket.AF_INET6      IPv6
    socket.AF_UNIX       只能夠用於單一的Unix系統進程間通信

    type(不全):
    socket.SOCK_STREAM    流式socket , for TCP (默認)
    socket.SOCK_DGRAM     數據報式socket , for UDP

    socket.SOCK_RAW              原始套接字

    常用方法:

socket.bind(address)
Bind the socket to address.

socket.listen([backlog])
Enable a server to accept connections.
If not specified, a default reasonable value is chosen--for backlog

socket.accept()
Accept a connection;The return value is a pair (conn, address) where conn is a new socket object
usable to send and receive data on the connection, and address is the address bound to the socket
on the other end of the connection

socket.connect(address)
Connect to a remote socket at address.

socket.send(bytes[, flags])    TCP method
Send data to the socket. The socket must be connected to a remote socket
Returns the number of bytes sent. Applications are responsible for checking that all data has been sent;
if only some of the data was transmitted, the application needs to attempt delivery of the remaining data

socket.sendall(bytes[, flags])
Send data to the socket. The socket must be connected to a remote socket.
this method continues to send data from bytes until either all data has been sent or an error occurs. 
None is returned on success.

socket.sendto(bytes[, flags], address)   UDP method
Send data to the socket. The socket should not be connected to a remote socket, since the destination socket
is specified by address.Return the number of bytes sent

socket.recv(bufsize[, flags])
Receive data from the socket. The return value is a bytes object representing the data received.
Note: For best match with hardware and network realities, the value of bufsize should be a relatively
small power of 2, for example, 4096

socket.recvfrom(bufsize[, flags])
Receive data from the socket. The return value is a pair (bytes, address)

socket.close()
Mark the socket closed
Note: close() releases the resource associated with a connection but does not necessarily close the
connection immediately. If you want to close the connection in a timely fashion, call shutdown() before close().




#服務器端 Sever

import socket
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))    # 服務器需要綁定地址
server.listen(5)    # 可以掛起的最大連接請求數量,不傳則自動選擇一個合理的默認值

while True:
    connection,client_addr = server.accept()    # 建立連接
    print("客戶端地址:",client_addr)
    while True:
        try:
            msg = connection.recv(1024)  # 1024表示一次從內存中取1024字節的數據
            if not msg:break   # 針對linux系統
            connection.send(msg.upper())
        except ConnectionError:  # 捕捉客戶端斷開連接異常
            break
    connection.close()   # 斷開連接
server.close()   # 關閉socket對象
# 客戶端 Client

import socket

client = socket.socket()   # 默認family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None
client.connect(('127.0.0.1',8080))    # 與服務器建連接

while True:
    msg = input('>>:').strip()
    if not msg:continue
    if msg == 'q':
        break
    client.send(msg.encode('utf-8'))   #只能發送bytes類型的數據,所有這裏需要先編碼
    data = client.recv(1024)        # 接收到的是bytes類型
    print(data.decode('utf-8'))
client.close()
    下圖模擬了5臺客戶端依次訪問服務器的過程:


    UDP是一種無連接的,不可靠的,面向報文的傳輸層通信協議

# UDP Server

import socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))

while True:
    msg,client_addr= server.recvfrom(1024)
    print(msg.decode('utf-8'),client_addr)      # hello ('127.0.0.1', 51705)
    server.sendto(msg.upper(),client_addr)
# UDP Client

import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    msg = input('>>:').strip()
    client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
    data,server_addr = client.recvfrom(1024)
    print(data.decode('utf-8'),server_addr)   # HELLO ('127.0.0.1', 8080)

四:TCP粘包現象及其處理方法

    TCP粘包是指發送方發送的若干包數據到接收方接收時粘成一包,從接收緩衝區看,後一包數據的頭緊接着前一包數據的尾,其本質原因是TCP是基於字節流的,數據之間沒有相應分界

    會造成粘包:
    1發送端需要等緩衝區滿才發送出去,造成粘包

    2接收方不及時接收緩衝區的包,造成多個包一起接收


    爲TCP數據加上保護邊界,可預防粘包:
    (1)發送固定長度的數據
    (2)把數據的大小與數據一塊發送
    (3)使用特殊標記來區分數據間隔,如給每個數據包加上一個定製的包頭


發佈了53 篇原創文章 · 獲贊 34 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章