Python 學習筆記(七)--socket


1.網絡七層模型及主要協議

2.TCP的“三次握手”和四次揮手

三次握手

Step1:首先客戶端向服務器端發送一段TCP報文;

Step 2:服務器端接收到來自客戶端的TCP報文之后,結束LISTEN階段,並返回一段TCP報文;

Step 3:客戶端接收到來自服務器端的確認收到數據的TCP報文之后,明確了從客戶端到服務器的數據傳輸是正常的,結束SYN-SENT階段,並返回最后一段TCP報文。

此后客戶端和服務器端進行正常的數據傳輸。

四次揮手

 

Step 1:首先客戶端想要釋放連接,向服務器端發送一段TCP報文;

Step 2:服務器端接收到從客戶端發出的TCP報文之后,確認了客戶端想要釋放連接,隨后服務器端結束ESTABLISHED階段,進入CLOSE-WAIT階段(半關閉狀態)並返回一段TCP報文;

Step 3:服務器端自從發出ACK確認報文之后,經過CLOSED-WAIT階段,做好了釋放服務器端到客戶端方向上的連接准備,再次向客戶端發出一段TCP報文;

Step 4:客戶端收到從服務器端發出的TCP報文,確認了服務器端已做好釋放連接的准備,結束FIN-WAIT-2階段,進入TIME-WAIT階段,並向服務器端發送一段報文。

服務器端收到從客戶端發出的TCP報文之后結束LAST-ACK階段,進入CLOSED階段。由此正式確認關閉服務器端到客戶端方向上的連接。

3.socket

socket(簡稱 套接字)是進程間通信的一種方式,它與其它進程間通信的一個主要不同是:socket 可以實現不同機器間的進程通信。

下面是簡單的Case,幫助理解。

 客戶端

from socket import socket, AF_INET, SOCK_STREAM

##表示創建一個客戶端的socket

client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP;

##定義一個連接的目標

con_address = ('IP地址',端口號)

##告訴客戶端要連接的服務器的地址和端口號

client.connect(con_address)

##發送data

client.send('python學習筆記'.encode('utf-8'))

##關閉
socket.close()

 服務端,創建socket服務器

from socket import socket, AF_INET, SOCK_STREAM
##創建一個socket對象
server = socket(AF_INET, SOCK_STREAM)

##綁定端口號
server.bind('',端口號) ##第一個參數為空字符串時,表示本機的IP

##開啟監聽狀態
server.listen(5)  ###參數為整型,表示消息可堆積的數量。

while true:
  socket, addr_info = server.accept() ##阻塞的,表示沒有連接的時候,一直等待。返回值為socket 和 addr_info。wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For IP sockets ,the address info is a pair (hostaddr,port).
  
  ##打印下
  print(socket, addr_info)
  
  ##讀取接受到的信息
  recv_data = socket.recv(512).decode('utf-8')  ##512是我們定義的bufsize.這個方法返回的是bytes。
  print('{}發送過來的消息是:{}' .format(addr_info[0] , recv_data))

  ##關閉
  socket.close()

4.客戶端+服務器 交互通信(單信息交叉)

客戶端

from socket import socket, AF_INET, SOCK_STREAM

##表示創建一個客戶端的socket

client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP;

##定義一個連接的目標

con_address = ('IP地址',端口號)

##告訴客戶端要連接的服務器的地址和端口號

client.connect(con_address)
while True:
  msg = input('客戶端輸入:')
  client.send(msg.encode('utf-8'))
  if msg == 'byebye':
    break
  ##接受服務器端的msg
  recv_data = socket.recv(512).decode('utf-8')  ##512是我們定義的bufsize.這個方法返回的是bytes。
  print('服務器端發送過來的消息是:{}' .format(recv_data))
  if recv_data == 'byebye':
    break
##關閉
socket.close()

服務端

from socket import socket, AF_INET, SOCK_STREAM
##創建一個socket對象
server = socket(AF_INET, SOCK_STREAM)

##綁定端口號
server.bind('',端口號) ##第一個參數為空字符串時,表示本機的IP

##開啟監聽狀態
server.listen(5)  ###參數為整型,表示消息可堆積的數量。

while True:
  socket, addr_info = server.accept() ##阻塞的,
  while True: ###保證可以對多個客戶端,不能因為一個客戶端,關閉所有。
    ##讀取接受到的信息
    recv_data = socket.recv(512).decode('utf-8')  
    print('客戶端發送過來的消息是:{}' .format(recv_data))
    if recv_data == 'byebye':  ##如果客戶端說byebye,就退出
      break
    msg = input('服務器端輸入:')
    socket.send(msg.encode('utf-8'))
    if msg == 'byebye' : ##如果我們說了byebye,退出
      break  
  ##關閉
  socket.close()
  print(addr_info,'離開了!')

5.借助線程,一方可以發送多個消息

即客戶端與服務端之間的通信不必要限制為一來一回,一問一答

服務端

##服務器端,創建socket服務器

from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
##創建一個socket對象
server = socket(AF_INET, SOCK_STREAM)

##綁定端口號
server.bind('',端口號)##第一個參數為空字符串時,表示本機的IP

##開啟監聽狀態
server.listen(5)  ###參數為整型,表示消息可堆積的數量

##任務
def send_msg(socket)
  while True:
    msg = input('輸入要發送的消息:')
    socket.send(msg.encode('utf-8'))

def recv_msg(socket)
  while Ture:##可以持續收消息
    data=socket.recv(512).decode('utf-8')
    if len(data)==0:
      break
    print('收到客戶端的消息是',data)

while True:
  socket, addr_info = server.accept() ##阻塞的
  t_send = Thread(target=send_msg, args=(socket,))
  t_recv = Thread(target=recv_msg, args=(socket,))
  t_send.start()
  t_recv.start()

客戶端

from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
##表示創建一個客戶端的socket

client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP;

##定義一個連接的目標

con_address = ('IP地址',端口號)

##告訴客戶端要連接的服務器的地址和端口號

client.connect(con_address)


##任務
def send_msg(socket)
  while True:
    msg = input('輸入要發送的消息:')
    socket.send(msg.encode('utf-8'))

def recv_msg(socket)
  while Ture:##可以持續收消息
    data=socket.recv(512).decode('utf-8')
    if len(data)==0:
      break
    print('收到服務器端的消息是',data)


t_send = Thread(target=send_msg, args=(client,))
t_recv = Thread(target=recv_msg, args=(client,))
t_send.start()
t_recv.start()

 6.web客戶端訪問

不寫專門的客戶端,借助web進行訪問,並且支持多個web同時訪問(通過協程實現)。

web Server 的代碼(服務端)

'''
cilent:瀏覽器客戶端
瀏覽器發出請求(request),Server端返回響應(response)。
request 包含:request 行(里面有請求方法--get,協議--HTTP/1.1)、請求頭【鍵值對】、請求體(POST請求時的數據)。
response 包含:response 行(里面有協議--HTTP/1.1,狀態--例如200 ok)、響應頭【鍵值對】、響應體(數據)。
'''


##服務器端,創建socket服務器
import gevent   ##導入協程的包
import gevent import monkey
##注意此時的socket 一定要來自gevent 的包,進行了繼承和封裝
##from socket import socket, AF_INET, SOCK_STREAM
from gevent import socket
from threading import Thread

monkey.patch_all()  

##創建一個socket對象
server = socket.socket()

##綁定端口號
server.bind('',端口號)---第一個參數為空字符串時,表示本機的IP

##開啟監聽狀態
server.listen(5)  ###參數為整型,表示消息可堆積的數量

##處理客戶端的訪問
def handle_client(socket)
  recv_data = socket.recv(512).decode('utf-8')
  print(recv_data)

  ##每次訪問返回的信息是
  
  msg ='歡迎來此訪問!'
  ##格式化response,需要有響應行、響應頭
  resp_line = 'HTTP/1.1 200 OK\r\n' ##\r\n格式有換行要求
  resp_header = 'Content-Type:text/html\r\ncharset=utf-8\r\nServer:testServer\r\n' 

  resp=  resp_line + resp_header +'\r\n'+msg  ##拼湊完整的返回體。注意返回體結束結尾需是兩個換行,添加一個

  socket.send(resp.encode('utf-8'))
  socket.colse() ##聊天結束

while True:
  socket, addr_info = server.accept()  
  print(addr_info,'請求訪問!')
  gevent.spawn(handle_cilent,socket)

 7.socket 常用函數和方法的梳理

 

類型 函數或方法 解釋

服務端套接字函數

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() 創建一個與該套接字相關的文件

 

參考

1.TCP/IP協議(一)網絡基礎知識 網絡七層協議

https://www.cnblogs.com/wanghuaijun/p/10092930.html

2.計算機各層網絡協議 

https://www.cnblogs.com/weiliuyby/p/8030175.html

3.Python3之socket編程

https://www.cnblogs.com/zhangyingai/p/7097922.html

4.詳解 TCP 連接的“ 三次握手 ”與“ 四次揮手 ”

https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc


免責聲明!

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



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