基於網絡編程 TCP協議 及 socket 基本語法

socket是什麼

Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。linux

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

也有人將socket說成ip+port,ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啓的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序

而程序的pid是同一臺機器上不一樣進程或者線程的標識

 

套接字分類:設計模式

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

套接字家族的名字:AF_UNIX服務器

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

套接字家族的名字:AF_INETsocket

 

六 套接字工做流程

 

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

 

 

 socket()模塊函數用法ide

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

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

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

因爲 socket 模塊中有太多的屬性。咱們在這裏破例使用了'from module import *'語句。使用 'from socket import *',咱們就把 socket 模塊裏的全部屬性都帶到咱們的命名空間裏了,這樣能 大幅減短咱們的代碼。
例如tcpSock = socket(AF_INET, SOCK_STREAM

 

實例: 基於TCP協議函數

服務端

import socket

# 建立socket對象  指定type參數爲socket.SOCK_DGRAM 表示使用UDP協議
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # datagram數據報的意思
# 綁定ip和端口
server.bind(("127.0.0.1",8888))

while True:
    # 接收數據 返回一個元祖  數據和 發送方的地址
    msg,c_addr = server.recvfrom(1024)
    print("收到來自%s: 說:%s" % (c_addr[0] ,msg.decode("utf-8")))
    # 發送數據到指定ip和端口
    server.sendto(msg.upper(),c_addr)
客戶端

import
socket client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # datagram數據報的意思 while True: msg = input(">>>:") client.sendto(msg.encode("utf-8"),("127.0.0.1",8888)) data,addr = client.recvfrom(1024) print(data.decode("utf-8"))

 

下面是 採用打電話的場景描述:

#服務器
# import socket
# #買電話  
# phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# #插卡
# phone.bind(('127.0.0.1',12012))   #端口範圍0-65535    0-1024 給系統用的
# #開機
# phone.listen(5)  # 5 表示同一時間 最大接受請求值爲5
# #等電話
# conn,client_addr = phone.accept()
# print('接受到',client_addr,'的電話')
# #收信息
# msg = conn.recv(1024) # 1024是一個最大值 限制
# print(msg.decode('utf-8'))
# #回信息
# conn.send('而後呢'.encode('utf-8'))
# #掛電話
# conn.close()
# #關機
# phone.close()
#客戶端 
# import socket
# # #買電話
# # phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# # #撥號
# # phone.connect(('127.0.0.1',12012))
# # #發信息
# # phone.send('哈哈哈哈哈哈哈哈'.encode('utf-8'))
# # #收信息
# # data = phone.recv(1024)
# # print(data.decode('utf-8'))
# # #掛電話
# # phone.close()

先運行服務器,在運行客戶端,否則會:

Traceback (most recent call last):
  File "F:/練習/客戶端.py", line 18, in <module>
    phone.connect(('127.0.0.1',2323))
ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。

 

tcp是基於連接的,必須先啓動服務端,而後再啓動客戶端去連接服務端

tcp服務端

 

 服務端套接字函數
s.bind()    綁定(主機,端口號)到套接字   接受一個元組參數
s.listen()  開始TCP監聽
s.accept()  被動接受TCP客戶的鏈接,(阻塞式)等待鏈接的到來

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

公共用途的套接字函數
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()           關閉套接字

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

面向文件的套接字的函數
s.fileno()          套接字的文件描述符
s.makefile()        建立一個與該套接字相關的文件

 

TCP服務端:

import socket
# 使用TCP 能夠直接默認
server = socket.socket()

# 指定端口 和 ip     端口 0 - 1023是系統保留的
server.bind(("127.0.0.1",65535))

# 監聽請求  參數爲最大半鏈接數(三次握手未完成的請求  多是服務器來不及 客戶端惡意攻擊)
server.listen(5)
# 爲了能夠不斷的接受客戶端鏈接請求
while True:
    # 接受鏈接請求
    c,addr = server.accept()
    # 爲了能夠重複收發數據
    while True:
        try:
            # 1024  程序的最大緩衝區容量   返回值類型爲bytes類型
            data = c.recv(1024).decode("utf-8")
            # 若是客戶端斷開鏈接(客戶端調用了close) recv 返回值爲kong 此時應該結束循環
            if not data:# 在linux中 客戶端異常關閉 服務器也會收空
                print("client closed!")
                c.close()
                break
            #解碼
            print(data)
            # 回覆數據 將原始數據轉爲大寫
            c.send(data.upper().encode("utf-8"))
        except ConnectionResetError:
            print("客戶端異常關閉!!")
            c.close()
            break
# 關閉資源
server.close()


# TCP斷開鏈接的正確姿式
# 客戶端調用close
# 服務器判斷若是接收數據爲空則相應的調用close
服務端
相關文章
相關標籤/搜索