網絡編程

第1章 互聯網常見架構:

C/S:客戶端和服務端

常見:wechat/qq

B/S:瀏覽器和服務器

常見:所有瀏覽器都是BS架構

第2章 什麼是socket

Socket就是一系列接口,把傳輸層一下的協議都封裝成了簡單的接口

2.1爲什麼要用套接字?

目的是要編寫一個CS架構的軟件

server端必須具備的特點:

1.     一直對外服務

2.     必須綁定一個固定的地址

3.     支持併發

2.2套接字的類型:

1.     基於文件類型的套接字:AF_UNIX

兩個文件同時位於一個機器上,則可以共用一個文件系統來進行通信

2.     基於網絡類型的套接字:AF_INET

2.3套接字的工作流程:

先從服務端說起,服務端先初始化socket,然後與端口綁定,對端口進行監聽,調用accept阻塞,等待客戶端連接,在這時如果有個客戶端初始化一個socket,然後連接服務器connect,如果連接成功,這時客戶端與服務端的連接就建立了,客戶端發送數據請求,服務端接受請求並處理請求,然後把數據發送給客戶端,客戶端讀取數據,最後關閉連接,一次交互結束

2.4關於套接字的方法:

服務端套接字函數:

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() 
得到阻塞套接字操作的超時時間

2.5基於tcpsocket通信

服務端:

import socket

server=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)
server.bind((
'127.0.0.1',8080))
server.listen(
3)
10

print('來自客戶端的請求')
print(addr)
data=conn.recv(
1024)
print('來自客戶端的消息:',data)
conn.send(data.upper())
conn.close()

客戶端:

import socket
client=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)
client.connect((
'127.0.0.1',8080))

client.send(
bytes('nihao',encoding='utf-8'))
data=client.recv(
1024)
print('來自服務端的數據:',data)
client.close()

2.6通信循環問題

服務端:

import socket

server = socket.socket(socket.AF_INET
,socket.SOCK_STREAM)
server.bind((
'127.0.0.1',8080))
server.listen(
3)

conn
,addr=server.accept()
print(addr)
    
while True:
            data=conn.recv(
1024)
            
iflen(data) == break
            
print('來自客戶端的消息:',data)
            conn.send(data.upper())
    conn.close()

客戶端:

import socket

client=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)

client.connect((
'127.0.0.1',8080))

while True:
    msg=
input('>>: ').strip()
    
iflen(msg) == :continue
    
client.send(bytes(msg,encoding='utf-8'))
    data=client.recv(
1024)
    
print(data)

client.close()

2.7循環鏈接問題

import socket

server=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)

server.bind((
'127.0.0.1',8080))

server.listen(
5)

while True:
    conn
,addr=server.accept()

    
while True:
        
try:
            data=conn.recv(
1024)
            
iflen(data) == 0:break
            
print(data)
            conn.send(data.upper())
        
exceptConnectionRefusedError as e:
            
break
    
conn.close()

2.8tcpssh客戶端

服務端:

import socket

import subprocess

server=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)

server.bind((
'127.0.0.1',8080))

server.listen(
5)

while True:
    conn
,addr=server.accept()

    
while True:
        
try:
            data=conn.recv(
1024)
            
iflen(data) == 0:break
            
obj=subprocess.Popen(data.decode('utf-8'),
            
shell=True,
            
stderr=subprocess.PIPE,
            
stdout=subprocess.PIPE
            )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            conn.send(stdout+stderr)
        
exceptConnectionRefusedError as e:
            
break
    
conn.close()
server.close()

客戶端:

import socket

client=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)

client.connect((
'127.0.0.1',8080))

while True:
    msg=
input('>>: ').strip()
    
iflen(msg) == :continue
    
client.send(bytes(msg,encoding='utf-8'))
    data=client.recv(
1024)
    
print(data.decode('utf-8'))

client.close()

第3章 粘包問題

要知道:只有tcp有粘包現象,UDP則永遠沒有

3.1什麼是粘包?

就是接受方不知道消息之間的界限,不知道一次性提取多少字節所造成的

3.2解決辦法:

問題的根源在於,接受端不知大發送端將要傳送的字節流的長度,所以解決粘包的方法就是圍繞,如何讓發送端在發送數據前把自己將要發送的字節流總大小讓接收端知曉,然後接收端來一個死循環接受所有數據即可

解決粘包問題服務端:

import socket
import struct

import subprocess

server=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)

server.bind((
'127.0.0.1',8080))

server.listen(
5)

while True:
    conn
,addr=server.accept()

    
while True:
        
try:
            data=conn.recv(
1024)
            
iflen(data) == 0:break
            
obj=subprocess.Popen(data.decode('utf-8'),
            
shell=True,
            
stderr=subprocess.PIPE,
            
stdout=subprocess.PIPE
            )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            
#發送固定長度的報頭
            
total_size=len(stdout) + len(stderr)
            conn.send(struct.pack(
'i',total_size))

            
#真實數據
            
conn.send(stdout+stderr)
        
exceptConnectionRefusedError as e:
            
break
    
conn.close()
server.close()

客戶端:

import socket
import struct

client=socket.socket(socket.AF_INET
,socket.SOCK_STREAM)

client.connect((
'127.0.0.1',8080))

while True:
    msg=
input('>>: ').strip()
    
iflen(msg) == :continue
    
client.send(bytes(msg,encoding='utf-8'))
    
#接受數據長度
    
header=client.recv(4)
    total_size=struct.unpack(
'i',header)[0]
    recv_size=
0
    
res=b''
    
whilerecv_size < total_size:
        recv_data=client.recv(
1024)
        res+=recv_data
        recv_size+=
len(recv_data)
    
print(res.decode('utf-8'))

client.close()

第4章 一個簡單的基於UDPsocket客戶端和服務端

服務端:

import socket

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

while True:
    data
,client_addr=server.recvfrom(1024)
    
print('===>',data,client_addr)
    server.sendto(data.upper()
,client_addr)
server.close()

客戶端:

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)
client.close()

第5章 socketserver實現併發的效果

5.1TCP服務端:

 

5.2UDP服務端:

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章