物理層功能:主要是基於電器特性發送高低電壓(電信號),高電壓對應數字1,低電壓對應數字0python
數據鏈路層的功能:定義了電信號的分組方式linux
早期的時候各個公司都有本身的分組方式,後來造成了統一的標準,即以太網協議ethernetgit
ethernet規定:github
一組電信號構成一個數據包,叫作‘幀’ 每一數據幀分紅:報頭head和數據data兩部分 head包含:(固定18個字節) 發送者/源地址,6個字節 接收者/目標地址,6個字節 數據類型,6個字節 data包含:(最短46字節,最長1500字節) 數據包的具體內容 head長度+data長度=最短64字節,最長1518字節,超過最大限制就分片發送
每塊網卡出廠時都被燒製上一個世界惟一的mac地址,長度爲48位2進制,一般由12位16進制數表示(前六位是廠商編號,後六位是流水線號)shell
有了mac地址,同一網絡內的兩臺主機就能夠通訊了(一臺主機經過arp協議獲取另一臺主機的mac地址)編程
ethernet採用最原始的方式,廣播的方式進行通訊,即計算機通訊基本靠吼json
網絡層功能:引入一套新的地址用來區分不一樣的廣播域/子網,這套地址即網絡地址windows
做用: ARP協議其主要用做將IP地址翻譯爲以太網的MAC地址設計模式
傳輸層功能:創建端口到端口的通訊緩存
TCP協議(傳輸控制協議)
tcp協議是面向連接的,面向流的,提供可靠性服務的,服務端和客戶端都要有成對的socket存在,必須先啓動服務端。
粘包
粘包是TCP協議是獨有的,發送端將間隔時間小並且數據量小的數據,合併到一塊兒發送給接收端。即面向流的通訊是無消息保護邊界的。
三次握手,四次揮手
3次握手
一、客戶端發送請求報文(SYN=1,並選擇一個seq=x) 二、服務端接收該報文,發送確認報文(SYN=1,ACK=1,並選擇一個seq = y,) 三、客戶端收到服務器的同步確認後,對服務器發送確認的確認(將ACK=1,確認號爲y+1,而報文首部的序號爲x+1,)
4次揮手
假設客戶端和服務端處於鏈接狀態,客戶端主動斷開鏈接 一、客戶端向服務端發送FIN報文:(FIN=1,序號seq=上一個最後傳輸的字節序號+1=u,發送後,客戶端進入FIN-WAIT-1狀態。) 二、服務端接收這個報文,發送一個確認報文:(令ACK=1,確認序號ack = u+1,本身的報文序號seq=v,發送後,服務器進入CLOSE-WAIT狀態。) 此時TCP鏈接進入鏈接半關閉狀態,服務器可能還會向客戶端發送一些數據。 三、若是服務端已經沒有要發送的數據,則發送釋放信號的報文 四、客戶端收到報文,發出確認報文,進入close狀態 服務端收到客戶端發來的確認報文後,也進入close狀態。
UDP(用戶數據報協議)
UDP協議是無鏈接的,面向消息的,因爲UDP支持的是一對多的模式,因此UDP協議不存在粘包現象,即面向消息的通訊是有消息保護邊界的。
總結:
TCP協議雖然安全性很高,可是網絡開銷大,而UDP協議雖然沒有提供安全機制,可是網絡開銷小。
Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有。
family(socket家族)
socket.AF_UNIX:用於本機進程間通信,爲了保證程序安全,兩個獨立的程序(進程)間是不能互相訪問彼此的內存的,但爲了實現進程間的通信,能夠經過建立一個本地的socket來完成
socket.AF_INET:(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)
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)
服務端套接字
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() 建立一個與該套接字相關的文件
地址重用
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
服務端
import socket import subprocess phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1', 9901)) #0-65535:0-1024給操做系統使用 phone.listen(5) print('starting...') while True: # 連接循環 conn, client_addr = phone.accept() print(client_addr) while True: # 通訊循環 try: # 一、收命令 cmd = conn.recv(1024) if not cmd: break # 適用於linux操做系統 # 、執行命令,拿到結果 obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() print("stdout:%s, stderr:%s" % (stdout, stderr)) # 三、把命令的結果返回給客戶端 print(len(stdout)+len(stderr)) conn.send(stdout+stderr) # +是一個能夠優化的點 except ConnectionResetError: # 適用於windows操做系統 break conn.close() phone.close()
客戶端
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input('>>: ').strip() if not msg:continue client.send(msg.encode('utf-8')) data=client.recv(1024) print(data.decode('utf-8'))
服務端
#!/usr/bin/env python # -*- coding: utf-8 -*- # __author__:JasonLIN import os import sys import json import struct import socket BASE_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(BASE_DIR) family_ip = "localhost" port = 9991 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((family_ip, port)) s.listen(5) def cmd_put(conn, file_name, file_size): file_path = os.path.join(BASE_DIR, "server_home", file_name) data_recv = 0 with open(file_path, "wb") as f: while file_size - data_recv > 1024: data = conn.recv(1024) f.write(data) data_recv += len(data) else: data = conn.recv(file_size - data_recv) f.write(data) data_recv += len(data) print("文件接收完成,一共", data_recv) def cmd_get(): pass def run(): while True: try: print("start.....") conn, addr = s.accept() head = conn.recv(4) header_len = struct.unpack("i", head)[0] data = json.loads(conn.recv(header_len)) print(data, type(data)) file_name = data["file_name"] func = data["func"] if func == "put": file_size = data["file_size"] cmd_put(conn, file_name, file_size) elif func == "get": cmd_get() except Exception as e: print(e) print("客戶端%s斷開" % conn) if __name__ == '__main__': run()
客戶端
#!/usr/bin/env python # -*- coding: utf-8 -*- # __author__:JasonLIN import os,sys import json import socket import struct BASE_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(BASE_DIR) ip_port = ("localhost", 9991) c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.connect(ip_port) def put(name): file_dir = os.path.join(BASE_DIR, "client_home", name) file_size = os.path.getsize(file_dir) data_header = { "func": "put", "file_name": name, "file_size": file_size } header_bytes = bytes(json.dumps(data_header), encoding="utf-8") head_len_bytes = struct.pack("i", len(header_bytes)) # 4個字節 c.send(head_len_bytes) c.send(header_bytes) i = 0 with open(file_dir, "rb")as f: for line in f: c.send(line) i += len(line) print("文件傳輸完成,一共:", i) def get(): pass def run(): while True: cmd = input(">>").strip() # put test.mp4 if cmd is None: continue if cmd == "q": break cmd_list = cmd.split(" ") func = cmd_list[0] file_name = cmd_list[1] if func == "put": put(file_name) elif func == "get": get(file_name) else: print("輸入有誤") continue if __name__ == '__main__': run()
服務端
import os import sys import socket import json import struct BASE_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(BASE_DIR) print(BASE_DIR) class MyFtpServer: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM server_dir = os.path.join(BASE_DIR, "server_home") max_packet_size = 8196 request_queue_size = 5 coding = "utf-8" allow_reuse_address = False def __init__(self, address, bind_and_active=True): self.server_address = address self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_active: try: self.connect() except: self.server_close() raise def connect(self): """創建鏈接""" if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() self.socket.listen(self.request_queue_size) def get_request(self): return self.socket.accept() def server_close(self): self.socket.close() def close_request(self): self.conn.close() def run(self): while True: try: self.conn, self.addr = self.get_request() print(self.conn, self.addr) while True: head_strut = self.conn.recv(4) if not head_strut: break head_len = struct.unpack("i", head_strut)[0] head_dic = json.loads(self.conn.recv(head_len)) print(head_dic, type(head_dic)) func = head_dic["func"] if hasattr(self, func): func = getattr(self, func) func(head_dic) except Exception: self.close_request() break def put(self, head_dic): file_name = head_dic["file_name"] file_size = head_dic["file_size"] file_path = os.path.join(self.server_dir, file_name) data_recv = 0 with open(file_path, "wb") as f: while file_size - data_recv > 1024: data = self.conn.recv(1024) f.write(data) data_recv += len(data) else: data = self.conn.recv(file_size - data_recv) f.write(data) data_recv += len(data) print("文件接收完成,一共", data_recv) s = MyFtpServer(("127.0.0.1", 9999)) s.run()
客戶端
import os import sys import socket import json import struct BASE_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(BASE_DIR) print(BASE_DIR) class Myclient: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM client_dir = os.path.join(BASE_DIR, "client_home") max_packet_size = 8196 request_queue_size = 5 coding = "utf-8" allow_reuse_address = False def __init__(self, server_address, connect=True): self.server_address = server_address self.socket = socket.socket(self.address_family, self.socket_type) if connect: try: self.client_connect() except: self.client_close() raise def client_connect(self): self.socket.connect(self.server_address) def client_close(self): self.socket.close() def run(self): while True: cmd = input(">>").strip() # put test.mp3 if cmd is None: continue if cmd == "q": break cmd_list = cmd.split(" ") func = cmd_list[0] if hasattr(self, func): func = getattr(self, func) func(cmd_list) def put(self, cmd_list): file_name = cmd_list[1] file_dir = os.path.join(self.client_dir, file_name) file_size = os.path.getsize(file_dir) data_header = { "func": "put", "file_name": file_name, "file_size": file_size } header_bytes = bytes(json.dumps(data_header), encoding="utf-8") head_len_bytes = struct.pack("i", len(header_bytes)) # 4個字節 self.socket.send(head_len_bytes) self.socket.send(header_bytes) i = 0 with open(file_dir, "rb")as f: for line in f: self.socket.send(line) i += len(line) print("文件傳輸完成,一共:", i) client = Myclient(("127.0.0.1", 9999)) client.run()
https://github.com/Jasonlincoln/FTP
服務端
from multiprocessing import Process import socket def task(conn): while True: try: data = conn.recv(1024).decode('utf-8') print(data) conn.send(data.upper().encode('utf-8')) except ConnectionRefusedError as e: print(e) def server(ip, port): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((ip, port)) server.listen(5) while True: conn, addr = server.accept() p = Process(target=task, args=(conn,)) p.start() server.close() if __name__ == '__main__': server("127.0.0.1", 9998)
客戶端
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 9998)) while True: msg = input(">>").strip() if not msg: continue client.send(msg.encode("utf-8")) data = client.recv(1024) print(data.decode("utf-8"))