基於TCP協議的socket套接字編程

看了上一篇的計算機網絡編程知識,應該對計算機網絡的知識有所瞭解了。不過,這都不重要。如今才進入正題。編程

1、什麼是Socket

socket:在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操做抽象爲幾個簡單的接口供應用層調用已實現進程在網絡中通訊。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。設計模式

因此,咱們無需深刻理解tcp/udp協議,socket已經爲咱們封裝好了,咱們只須要遵循socket的規定去編程,寫出的程序天然就是遵循tcp/udp標準的。緩存

2、套接字發展史及分類

套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 所以,有時人們也把套接字稱爲「伯克利套接字」或「BSD 套接字」。一開始,套接字被設計用在同 一臺主機上多個應用程序之間的通信。這也被稱進程間通信,或 IPC。套接字有兩種(或者稱爲有兩個種族),分別是基於文件型的和基於網絡型的。服務器

2.1 基於文件類型的套接字家族

套接字家族的名字:AF_UNIX網絡

unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,能夠經過訪問同一個文件系統間接完成通訊架構

2.2 基於網絡類型的套接字家族

套接字家族的名字:AF_INETsocket

(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)tcp

3、套接字工做流程

個生活中的場景。你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就創建起了鏈接,就能夠講話了。等交流結束,掛斷電話結束這次交談。 生活中的場景就解釋了這工做原理。函數

先從服務器端提及。服務器端先初始化Socket,而後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端鏈接。在這時若是有個客戶端初始化一個Socket,而後鏈接服務器(connect),若是鏈接成功,這時客戶端與服務器端的鏈接就創建了。客戶端發送數據請求,服務器端接收請求並處理請求,而後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉鏈接,一次交互結束,使用如下Python代碼實現:

import socket

# socket_family 能夠是 AF_UNIX 或 AF_INET。socket_type 能夠是 SOCK_STREAM 或 SOCK_DGRAM。protocol 通常不填,默認值爲 0
socket.socket(socket_family, socket_type, protocal=0)

# 獲取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 獲取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

3.1 服務端套接字函數

方法 用途
s.bind() 綁定(主機,端口號)到套接字
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的鏈接,(阻塞式)等待鏈接的到來

3.2 客戶端套接字函數

方法 用途
s.connect() 主動初始化TCP服務器鏈接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

3.3 公共用途的套接字函數

方法 用途
s.recv() 接收TCP數據
s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完)
s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完)
s.recvfrom() 接收UDP數據
s.sendto() 發送UDP數據
s.getpeername() 鏈接到當前套接字的遠端的地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回指定套接字的參數
s.setsockopt() 設置指定套接字的參數
s.close() 關閉套接字

3.4 面向鎖的套接字方法

方法 用途
s.setblocking() 設置套接字的阻塞與非阻塞模式
s.settimeout() 設置阻塞套接字操做的超時時間
s.gettimeout() 獲得阻塞套接字操做的超時時間

3.5 面向文件的套接字的函數

方法 用途
s.fileno() 套接字的文件描述符
s.makefile() 建立一個與該套接字相關的文件

4、基於套接字編寫一個簡單的c/s架構的程序

C:client 客戶機程序

HOST = "192.168.11.237"

import socket

# 1.建立一個socket對象
soc=socket.socket()

# 2.鏈接服務端
soc.connect((HOST,8080))
print(f"鏈接服務器{HOST}成功")

# 3.通信
while 1:
    flag = input("請輸入數據:")
    if flag == "q":
        break

    try:
        soc.send(flag.encode("utf8"))
        print(f"發送數據成功!\n")

        print("等待接收數據。。。")
        data = soc.recv(1024)
        print('來自服務器的數據:  ', data.decode("utf"))
    except Exception as a:
        print("出錯了:",a)
        break

# 4.關閉鏈接
soc.close()

S: Service 服務器端

HOST = "192.168.11.237"

import socket

# 1.建立socket對象 :socket.SOCK_STREAM 表示創建tcp鏈接 ,udp鏈接socket.SOCK_DGRAM
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 2.綁定ip地址:傳元組:(ip,端口號)
soc.bind((HOST,8080))  #若是寫本機ip,局域網外部能夠訪問
# soc.bind(('127.0.0.1',8080))    #若是寫127.0.0.1,只能本身訪問

# 3.監聽:等待鏈接 這個5 是半鏈接池的大小
soc.listen(5)

# 4.等待鏈接
print('等待鏈接中。。。')
conn,addr=soc.accept()
print(f'我被IP:{addr}鏈接了')

# 5.進行通信

while 1:
    try:
        # 接收
        print("準備接收。。。")
        rec_data = conn.recv(1024)  # 最大字節,# 會阻塞
        print("來自客戶端的數據:  ",rec_data.decode("utf8").upper(),"\n")
        # print('來自客戶端的數據:', rec_data.decode("utf"))

        send_data = input("請輸入數據:")
        if send_data == "q":
            break

        # 發送
        conn.send(send_data.encode("utf8"))

    except Exception as e:
        print("出錯了:",e)
        break

# 6.釋放鏈接
conn.close()

# 7.關閉socket通信
soc.close()

有客戶客戶機連不上服務器,多是由於防火牆的緣由

相關文章
相關標籤/搜索