1. 程式人生 > 實用技巧 >Python——socket網路程式設計

Python——socket網路程式設計


socket程式設計

應用程式架構的分類:
C/S 客戶端/伺服器
B/S 瀏覽器/伺服器

C/S架構和B/S架構的區別
C/S架構的優點:

  1. 個性化更容易實現
  2. 更安全
  3. 佔用網路資源少

B/S結構的優點:

  1. 更新方便
  2. 使用方便
  3. 幾乎不佔用本地資源

@

目錄


前言

  1. C/S架構與socket的關係:我們學習socket的目的是為了實現C/S架構;
  2. 還有一種架構是基於socket來實現的,寫一個伺服器端軟體,客戶端軟體不用寫,客戶端就是瀏覽器(瀏覽器的本質是套接字寫的客戶端);

一、什麼是網路

在一臺計算機上由硬體,硬體之上是作業系統,作業系統之上是應用程式(為客戶端軟體)
在另一臺計算機上硬體之上是作業系統,在作業系統之上是應用程式(伺服器端軟體),這兩臺計算機是基於網路通訊的

什麼是網路:

  1. 物理介質:網線;

  2. 協議(就像兩個人打電話,物理介質是電話線,兩個人處於不同的地區,兩個人說的話彼此聽不懂,怎麼解決?定義一個標準,稱之為網際網路協議)

總結:
在計算機通訊中,計算機除了物理連結介質外,想要通訊,計算機必須有一個標準,稱之為網際網路協議
即(網路就是物理連結介質和網際網路協議)
接下來寫一個客戶端軟體和伺服器軟體,兩者之間基於網路通訊(網路包含:物理連結介質,(是網路工程師乾的);我們要乾的事情是客戶端軟體給伺服器軟體發訊息,必須讓伺服器軟體和客戶端軟體遵循網際網路協議)

二、網際網路協議按照功能不同分為osi七層或tcp/ip五層或tcp/ip四層

(我們主要學習五層)

資料的封裝與解封裝詳情見:https://www.cnblogs.com/huoxc/p/13611394.html
在網路層:IP協議
傳輸層:TCP/UDP協議
資料鏈路層:乙太網協議
應用層:http協議,DNS協議,ftp協議

三:什麼是socket

  • 應用層軟體是我們寫的,想要發資料傳給傳輸層,應用層規定好的協議其他層控制不了,是客戶端和伺服器端規定好的,沒人管,但是往外發資料,必須按照UDP或者TCP協議;如果用TCP協議必須按照TCP的協議來,必須把TCP協議學會以及IP協議等;那麼效率比較低;
  • 怎麼解決:對於這種情況把TCP,IP協議等瘋漲起來,對於我來說TCP以下我都不打交道了,我只跟應用層打交道,socket就是在應用層和傳輸層之間的,傳輸層一下的都封裝到socket中,對於我來遵循套接字的標準,那麼我們基於socket來開發的;針對這種協議,python中有一個模組,socket

四:套接字的工作流程

(TCP協議的套接字)

首先:
1:伺服器端先拿到一個套接字物件
2:接下來繫結IP和埠
3:接下來監聽
4:等待客戶端的連結
其次:
1:客戶端拿到一個套接字物件
2:客戶端和伺服器建立連結(即3次握手,雙向連結)
3:客戶端向伺服器傳送資料
4:伺服器對請求進行處理,處理完畢後再響應給客戶端
5:關閉客戶端

先從伺服器端說起。伺服器端先初始化Socket,然後與埠繫結(bind),對埠進行監聽(listen),呼叫accept阻塞,等待客戶端連線。在這時如果有個客戶端初始化一個Socket,然後連線伺服器(connect),如果連線成功,這時客戶端與伺服器端的連線就建立了。客戶端傳送資料請求,伺服器端接收請求並處理請求,然後把迴應資料傳送給客戶端,客戶端讀取資料,最後關閉連線,一次互動結束

服務端

import socket
ip_port=('127.0.0.1',9000)  
BUFSIZE=1024                #收發訊息的大小
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #其中socket.AF_INET:基於網路通訊的套接字;
#socket.SOCK_STREAM:流式套接字又稱之為TCP協議
s.bind(ip_port) #繫結的IP和埠(IP為伺服器的IP)
s.listen(5)     #監聽,如打電話,在跟別人通話的時候,還可能來多個電話,這個連結處於掛起的狀態,如果當前的連結以結束,立馬就跟剛剛掛起的電話通訊,在TCP協議中被稱之為連線池的大小


conn,addr=s.accept()            #手機接電話(accept是一種阻塞操作,什麼時候變成非阻塞狀態,有人鏈接就會變成非阻塞狀態)返回一個連結物件,客戶端的IP地址和埠
# print(conn)
# print(addr)
print('接到來自%s的電話' %addr[0])

msg=conn.recv(BUFSIZE)             #接受訊息
print(msg,type(msg))

conn.send(msg.upper())          #發訊息,

conn.close()                    #關閉連結

s.close()                       #關閉套接字

客戶端:

import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(ip_port)           #建立連結

s.send('lheiehei '.encode('utf-8'))         #發訊息(基於網路傳送的是二進位制格式)

feedback=s.recv(BUFSIZE)                           #收訊息
print(feedback.decode('utf-8'))

s.close()                                       #關閉連結

有可能還會出現以下的情況:

怎麼解決:
這個是由於你的服務端仍然存在四次揮手的time_wait狀態在佔用地址(如果不懂,請深入研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.伺服器高併發情況下會有大量的time_wait狀態的優化方法)

#加入一條socket配置,重用ip和埠  或者重新寫一個非著名埠

phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8080))

如果在Linux中解決方法如下:

發現系統存在大量TIME_WAIT狀態的連線,通過調整linux核心引數解決,
vi /etc/sysctl.conf

編輯檔案,加入以下內容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
 
然後執行 /sbin/sysctl -p 讓引數生效。
 
net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當出現SYN等待佇列溢位時,啟用cookies來處理,可防範少量SYN攻擊,預設為0,表示關閉;

net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉;

net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉。

net.ipv4.tcp_fin_timeout 修改系統預設的 TIMEOUT 時間

加上鍊接迴圈與通訊迴圈:

服務端:
import socket
ip_port=('127.0.0.1',9000)  #電話卡
BUFSIZE=1024                #收發訊息的尺寸
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機
print(s)      #s是套接字物件:這個套接字物件主要用來建立三次握手
s.bind(ip_port) #手機插卡
s.listen(5)     #手機待機


conn,addr=s.accept()            #手機接電話
# print(conn)
# print(addr)
while True:
	msg=conn.recv(BUFSIZE)             #聽訊息,聽話(這種套接字主要用來收發訊息,跟一個客戶端收發訊息,因為跟一個客戶端建號的三次握手)
	conn.send(msg.upper())          #發訊息,說話

conn.close()                    #掛電話

s.close()
客戶端:
import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(ip_port)           #撥電話
while True:
	s.send('linhaifeng nb'.encode('utf-8'))         #發訊息,說話(只能傳送位元組型別)
	feedback=s.recv(BUFSIZE)                           #收訊息,聽話
	print(feedback.decode('utf-8'))

s.close()                                       #掛電話

總結:

服務端套接字有幾種形式:

  1. conn這種套接字(這種套接字主要用來收發訊息,跟一個客戶端收發訊息,因為跟一個客戶端建號的三次握手)
    如果再換一個客戶端連結,就是一個新的conn物件;
  2. 剛開始的時候,拿到的s套接字物件,這個套接字物件主要用來建立三次握手;

如果客戶端發的是空,則直接卡住了,在客戶端沒有卡住,在伺服器端卡住了,服務端一直在等著,怎麼解決?,在客戶端解決;

import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(ip_port)           #撥電話
while True:
	msg=input('>>>:').strip()
	if not msg:continue   #當為空的時候,繼續讓使用者輸入
		

s.close()                                       #掛電話

粘包:

須知:只有TCP有粘包現象,UDP永遠不會粘包,首先需要掌握一個socket收發訊息的原理

解決粘包:https://www.cnblogs.com/huoxc/p/13999288.html