SIP 3

本文介绍了SIP协议的基本概念和发展历程,详细阐述了SIP协议在网络中的应用,包括其网络结构、基本功能、特点以及网络中各种元素的角色和作用。

1.Overview:

image

2.SIP协议:

1)概述

SIP: Session Initiation Protocol,由IETF制定,最早由MMUSIC工作组提出,现在主要由SIP工作组负责维护和后期扩展,是一种轻量级的应用层通用信令协议,用于多媒体通信控制,可建立、修改和终止IP网上的语音和多媒体会话。

典型的SIP梯形网络结构:

image

SIP的Offer/Answer模型:

也称为会话协商模型,以在对等功能实体之间进行会话协商通过在SIP消息的消息体中包含SDP描述完成。、

Offer:包括媒体类型、媒体格式、地址
Answer:是否接收媒体类型、媒体格式、地址
在会话过程中,任何一方可以通过Offer/Answer模型修改会话属性。

2)发展和应用情况:

1999年,MMUSIC工作组推出SIP V1.0 (RFC2543)。
2003年,SIP工作组推出SIP V2.0 (RFC3261)目前已进行了许多扩展,并被3GPP/3GPP2、OMA、ETSI、ITU-T等标准化组织广泛采纳。
3)相关标准:

a)SIP核心标准:

image

b)SIP扩展标准:

IETF针对SIP的不同应用需求制定了上百篇SIP扩展RFC,

SIP相关RFC:
3261-32xx, 33xx, 34xx, 35xx, 36xx, 37xx, 38xx, 39xx, 40xx,
41xx, 42xx, 43xx, 44xx, 45xx, 46xx, 47xx, 48xx, 49xx, 50xx,
51xx, 52xx, 53xx, 54xx, before 3261

4)SIP基本功能:

用户定位:确定被叫用户通信所使用的终端系统的位置
用户能力协商:确定所用媒体类型和媒体参数
用户可用性判断:确定被叫用户是否空闲以及是否愿意加入会话
邀请用户加入会话(呼叫建立):邀请和提示被叫用户,在主被叫间传递控制参数是SIP的主要功能
呼叫处理:对呼叫进行终结和转移等
5)SIP的特点

基于文本的协议:简单灵活,便于扩展,易于用Java、Perl等面向对象的语言实现,易于调测排错
独立于底层传输协议:可工作在UDP、TCP、SCTP等协议之上,消息的格式及操作
过程与传送协议无关:推荐首选UDP,可以减少呼叫建立时延,便于应用采用多播机制
呼叫和媒体控制信息同时传送:在传送呼叫控制信令的同时,还可以在消息文本中通过SDP传送呼叫的媒体类型和格式等信息,以加快呼叫建立的速度便于增加新的应用或媒体
支持用户移动性
支持直接路由和代理路由
6)网络中元素:

SIP基本网络模型:客户-服务器协议,在语法和语义上与HTTP类似,SIP客户发出请求,SIP服务器接收请求并进行响应

image

a)SIP用户代理UA:

端系统中的SIP应用称作SIP用户代理(UA),UA = UAC(事务发起方) + UAS(事务最终接收方),UA最基本的功能是支持SIP请求和应答的正确发送和接收。UAC和UAS是基于transaction而言的。其余实体都是基于功能特征而言的。

用户代理客户(UAC) - 发送SIP请求
用户代理服务器(UAS)-侦听呼叫请求,提示用户或执行程序作出响应

B2BUA:在一次呼叫中既充当UAC又充当UAS角色,UAC根据UAS接收到的请求消息构造新的请求消息进行发送。

b)SIP代理服务器Proxy Server:

负责代表UA去转发请求, 到下一跳SIP代理服务器或重定向服务器或最终的UAS,也可能将请求分发到多个下一跳服务器。和B2BUA不同,Proxy只能允许对SIP头域中做非常小的改动,RFC3261中有描述(第16章)。

主要功能:寻址、路由、转发,可以解释、翻译、改写SIP请求。

分类:

有状态代理服务器:作为虚拟的UAC/UAS,维持事务/对话状态机,需要记忆入请求和出请求
无状态代理服务器:接收请求,进行必要的翻译,发出请求,不需要记忆任何请求信息(除了这个以外其余实体都可以做UAC或者UAS)
分叉代理服务器:必须有状态记忆能力,以便将请求和应答进行匹配
非分叉代理服务器:可以无状态记忆能力
c)SIP重定向服务器Redirect Server:

通过响应告诉请求的发起方下一跳服务器的地址,然后由请求发起方根据此地址向下一跳服务器重新发送请求 。一个重定向服务器是一个SIP代理服务器或者SIP-registrar来对一个INVITE消息回复3XX响应的实体。这个回复带有Contact头域,提供应该去联络的设备的地址。

image

与Proxy Server的区别

重定向服务器的目的是提供可供选择的地址列表供用户定位SIP UA,代理服务器则是代替用户继续后面的定位尝试
重定向服务器只提供地址解析服务,类似于DNS
重定向服务器不主动发送SIP请求
重定向服务器不需要维持事务状态 (你问一个问题,它回答一个,之后什么都不记得了)
完整一个重定向服务器的流程如下图:

注意图中的注释:任何一个SIP代理服务器都可以对这个302响应进行处理,将INVITE请求转发到新的目的地去。

d)SIP注册服务器Register Server

通过注册过程接收客户当前的位置信息,并对位置服务器进行添加、修改、查询等操作。通常与代理服务器或重定向服务器放在一起。

image

功能:

接收用户的注册请求
记录用户的SIP地址和IP地址的绑定关系
提供注册认证功能,是实现用户移动性的基础
e)位置服务器

存储并向用户返回可能的位置信息,在SIP网络架构中起到重要作用的Internet公共服务器。位置服务器的信息可能来自SIP注册服务器,也可能通过其他渠道获取。位置服务器与SIP服务器之间通过使用LDAP协议 进行通信,位置服务器可能返回多个位置信息,重定向服务器和代理服务器可以采用不同的方式来处理这多个位置信息。

我们看一个呼叫的过程体会一下各个功能实体的作用:

image

更加实用的网络形式如下:

注意,除了SBC(Session Border Controller)以外,没有任何SIP服务器或者MGC自身会去处理数据流。与PSTN的互通需要软交换(MGC + MGW)的参与。PDN或者Internet与这个网络的互通要经过防火墙或者SBC。

7)SIP协议的结构及其位置

image

各层功能相对独立。层与层之间松散耦合,SIP独立于网络传输协议。

事务用户层

负责处理请求或响应、产生请求或响应
负责客户事务(CT)和服务器事务(ST)的创建和销毁

转载于:https://www.cnblogs.com/Dennis-mi/articles/8391473.html

import platform import shutil import os import re import threading import tkinter as tk import urllib.request import zipfile import json import time try: import winreg # Windows专用 except ImportError: pass import subprocess from tkinter import filedialog, messagebox, ttk from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.chrome.options import Options from selenium.common.exceptions import (WebDriverException, SessionNotCreatedException, NoSuchElementException) class WebScreenshotApp: def __init__(self, root): self.root = root root.title("网页截图工具 v2.0") root.geometry("800x600") root.resizable(True, True) # 创建主框架 main_frame = ttk.Frame(root, padding="20") main_frame.pack(fill=tk.BOTH, expand=True) # 创建输入区域 input_frame = ttk.LabelFrame(main_frame, text="截图参数", padding=10) input_frame.pack(fill=tk.X, pady=(0, 10)) # URL输入 ttk.Label(input_frame, text="目标网址:").grid(row=0, column=0, sticky="w", padx=5, pady=5) self.url_entry = ttk.Entry(input_frame, width=50) self.url_entry.grid(row=0, column=1, columnspan=2, sticky="we", padx=5, pady=5) self.url_entry.insert(0, "https://example.com") # 登录凭证(可选) auth_frame = ttk.Frame(input_frame) auth_frame.grid(row=1, column=0, columnspan=3, sticky="we", pady=5) ttk.Label(auth_frame, text="用户名:").pack(side=tk.LEFT, padx=(0, 5)) self.user_entry = ttk.Entry(auth_frame, width=20) self.user_entry.pack(side=tk.LEFT, padx=(0, 10)) ttk.Label(auth_frame, text="密码:").pack(side=tk.LEFT) self.pass_entry = ttk.Entry(auth_frame, width=20, show="*") self.pass_entry.pack(side=tk.LEFT, padx=(0, 10)) # 关键词搜索(可选) ttk.Label(input_frame, text="搜索关键词:").grid(row=2, column=0, sticky="w", padx=5, pady=5) self.search_entry = ttk.Entry(input_frame, width=50) self.search_entry.grid(row=2, column=1, columnspan=2, sticky="we", padx=5, pady=5) # 驱动设置区域 driver_frame = ttk.LabelFrame(main_frame, text="Chrome驱动设置", padding=10) driver_frame.pack(fill=tk.X, pady=(0, 10)) ttk.Label(driver_frame, text="驱动路径:").grid(row=0, column=0, sticky="w", padx=5, pady=5) self.driver_entry = ttk.Entry(driver_frame, width=50) self.driver_entry.grid(row=0, column=1, sticky="we", padx=5, pady=5) ttk.Button(driver_frame, text="浏览...", width=10, command=self.browse_driver).grid(row=0, column=2, padx=5) ttk.Button(driver_frame, text="自动检测", width=10, command=self.auto_detect_driver).grid(row=0, column=3, padx=5) self.driver_version_var = tk.StringVar(value="未检测") ttk.Label(driver_frame, text="驱动版本:").grid(row=1, column=0, sticky="w", padx=5) ttk.Label(driver_frame, textvariable=self.driver_version_var).grid(row=1, column=1, sticky="w") self.chrome_version_var = tk.StringVar(value="未检测") ttk.Label(driver_frame, text="Chrome版本:").grid(row=1, column=2, sticky="w", padx=5) ttk.Label(driver_frame, textvariable=self.chrome_version_var).grid(row=1, column=3, sticky="w") ttk.Button(driver_frame, text="更新驱动", command=self.update_driver).grid(row=2, column=3, padx=5, pady=5, sticky="e") # 输出设置 output_frame = ttk.LabelFrame(main_frame, text="输出设置", padding=10) output_frame.pack(fill=tk.X, pady=(0, 10)) ttk.Label(output_frame, text="保存目录:").grid(row=0, column=0, sticky="w", padx=5, pady=5) self.folder_entry = ttk.Entry(output_frame, width=50) self.folder_entry.grid(row=0, column=1, sticky="we", padx=5, pady=5) ttk.Button(output_frame, text="选择文件夹...", width=10, command=self.browse_folder).grid(row=0, column=2, padx=5) # 文件名前缀 ttk.Label(output_frame, text="文件名前缀:").grid(row=1, column=0, sticky="w", padx=5, pady=5) self.prefix_entry = ttk.Entry(output_frame, width=50) self.prefix_entry.grid(row=1, column=1, sticky="we", padx=5, pady=5) self.prefix_entry.insert(0, "screenshot") # 状态栏 status_frame = ttk.Frame(main_frame) status_frame.pack(fill=tk.X, pady=(10, 0)) self.status_var = tk.StringVar(value="就绪") ttk.Label(status_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W).pack(side=tk.BOTTOM, fill=tk.X) # 进度条 self.progress_var = tk.DoubleVar(value=0.0) self.progress = ttk.Progressbar(status_frame, variable=self.progress_var, maximum=100) self.progress.pack(side=tk.BOTTOM, fill=tk.X, pady=(0, 5)) # 操作按钮 button_frame = ttk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=(10, 0)) ttk.Button(button_frame, text="开始截图", command=self.start_capture).pack(side=tk.RIGHT, padx=5) ttk.Button(button_frame, text="退出", command=root.destroy).pack(side=tk.RIGHT) # 设置默认路径 self.set_default_paths() self.auto_detect_driver() def set_default_paths(self): """设置默认路径""" # 默认保存目录:用户桌面 desktop_path = os.path.join(os.path.expanduser("~"), "Desktop") self.folder_entry.delete(0, tk.END) self.folder_entry.insert(0, os.path.join(desktop_path, "网页截图")) # 尝试自动检测Chrome安装路径 chrome_path = self.detect_chrome_path() if chrome_path: driver_dir = os.path.dirname(chrome_path) driver_path = os.path.join(driver_dir, "chromedriver.exe") if os.path.exists(driver_path): self.driver_entry.delete(0, tk.END) self.driver_entry.insert(0, driver_path) def detect_chrome_path(self): """自动检测Chrome安装路径""" system = platform.system() try: if system == "Windows": # 通过注册表查找 key_path = r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe" with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key: path, _ = winreg.QueryValueEx(key, "") return path elif system == "Darwin": # macOS return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" elif system == "Linux": # 尝试常见安装路径 paths = [ "/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/snap/bin/chromium" ] for path in paths: if os.path.exists(path): return path except Exception: pass return None def browse_folder(self): """浏览选择文件夹""" folder = filedialog.askdirectory() if folder: self.folder_entry.delete(0, tk.END) self.folder_entry.insert(0, folder) def browse_driver(self): """浏览选择驱动文件""" filetypes = [("可执行文件", "*.exe" if platform.system() == "Windows" else "*")] driver_path = filedialog.askopenfilename( title="选择ChromeDriver", filetypes=filetypes ) if driver_path: self.driver_entry.delete(0, tk.END) self.driver_entry.insert(0, driver_path) self.check_driver_version() def auto_detect_driver(self): """自动检测驱动路径""" system = platform.system() common_paths = [] # Windows常见路径 if system == "Windows": common_paths = [ os.path.expandvars(r"%ProgramFiles%\Google\Chrome\Application\chromedriver.exe"), os.path.expandvars(r"%ProgramFiles(x86)%\Google\Chrome\Application\chromedriver.exe"), os.path.expandvars(r"%LOCALAPPDATA%\Google\Chrome\Application\chromedriver.exe") ] # macOS常见路径 elif system == "Darwin": common_paths = [ "/Applications/Google Chrome.app/Contents/MacOS/chromedriver", "/usr/local/bin/chromedriver", os.path.expanduser("~/bin/chromedriver") ] # Linux常见路径 elif system == "Linux": common_paths = [ "/usr/bin/chromedriver", "/usr/local/bin/chromedriver", "/snap/bin/chromedriver", os.path.expanduser("~/bin/chromedriver") ] # 检查常见路径 for path in common_paths: if os.path.exists(path): self.driver_entry.delete(0, tk.END) self.driver_entry.insert(0, path) self.check_driver_version() return # 尝试在PATH环境变量中查找 try: path = subprocess.check_output(["which", "chromedriver"], text=True).strip() if os.path.exists(path): self.driver_entry.delete(0, tk.END) self.driver_entry.insert(0, path) self.check_driver_version() return except Exception: pass messagebox.showinfo("提示", "未找到ChromeDriver, 请手动选择路径或点击'更新驱动'") def check_driver_version(self): """检查驱动和浏览器版本""" driver_path = self.driver_entry.get().strip() if not os.path.exists(driver_path): self.driver_version_var.set("无效路径") self.chrome_version_var.set("未检测") return try: # 获取驱动版本 result = subprocess.run( [driver_path, "--version"], capture_output=True, text=True, check=True ) driver_version = re.search(r"(\d+\.\d+\.\d+\.\d+)", result.stdout) if driver_version: self.driver_version_var.set(driver_version.group(1)) # 获取Chrome浏览器版本 chrome_path = self.detect_chrome_path() if chrome_path: result = subprocess.run( [chrome_path, "--version"], capture_output=True, text=True, check=True ) chrome_version = re.search(r"(\d+\.\d+\.\d+\.\d+)", result.stdout) if chrome_version: self.chrome_version_var.set(chrome_version.group(1)) except Exception as e: self.driver_version_var.set("检测失败") self.chrome_version_var.set("检测失败") def update_driver(self): """更新驱动版本""" # 获取当前Chrome主版本 chrome_version = self.chrome_version_var.get() if "未检测" in chrome_version or "失败" in chrome_version: messagebox.showerror("错误", "请先检测到有效的Chrome版本") return major_version = chrome_version.split(".")[0] threading.Thread( target=self.download_and_update_driver, args=(major_version,), daemon=True ).start() def start_capture(self): """启动截图流程""" # 验证输入 url = self.url_entry.get().strip() if not url: messagebox.showerror("错误", "请输入有效的网址") return driver_path = self.driver_entry.get().strip() if not os.path.exists(driver_path): messagebox.showerror("错误", "ChromeDriver路径无效") return # 在后台线程中执行截图 threading.Thread( target=self.capture_page, args=(url, driver_path), daemon=True ).start() def secure_capture(url): options = Options() # 关键修复配置 options.add_argument("--headless=new") options.add_argument("--disable-gpu") options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage") options.add_argument("--disable-software-rasterizer") # 防止反爬虫检测 options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) # 模拟真实浏览器 options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36") driver = webdriver.Chrome(options=options) try: driver.get(url) # 双重等待机制 WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, "//*[text()]")) ) WebDriverWait(driver, 5).until( lambda d: d.execute_script("return document.readyState === 'complete'") ) # 强制渲染动态内容 driver.execute_script(""" document.body.style.overflow = 'visible'; document.documentElement.style.height = '100%'; if(typeof echarts !== 'undefined') { echarts.getInstanceByDom(document.querySelector('.echarts')).resize(); } """) # 滚动触发渲染 driver.execute_script("window.scrollBy(0, 500)") time.sleep(0.3) # 截图前验证 if driver.execute_script("return document.body.clientHeight") < 100: driver.execute_script("document.body.style.minHeight = window.innerHeight + 'px'") # 使用PNG格式保存 return driver.get_screenshot_as_png() finally: driver.quit() def capture_page(self, url, driver_path): """执行实际截图操作""" try: self.status_var.set("正在初始化浏览器...") self.progress_var.set(10) # 配置浏览器选项 options = Options() options.add_argument("--headless=new") # 无头模式 options.add_argument("--disable-gpu") options.add_argument("--window-size=1920,1080") # 初始化WebDriver service = Service(executable_path=driver_path) driver = webdriver.Chrome(service=service, options=options) # 登录逻辑(如果有凭证) username = self.user_entry.get().strip() password = self.pass_entry.get().strip() if username and password: self.status_var.set("正在登录...") self.progress_var.set(30) driver.get(url) time.sleep(2) # 等待页面加载 # 尝试查找登录表单(实际实现需根据目标网站调整) try: driver.find_element(By.ID, "username").send_keys(username) driver.find_element(By.ID, "password").send_keys(password) driver.find_element(By.ID, "login-btn").click() time.sleep(3) # 等待登录完成 except NoSuchElementException: self.status_var.set("警告:未找到登录表单") # 搜索逻辑(如果有关键词) search_term = self.search_entry.get().strip() if search_term: self.status_var.set("正在执行搜索...") self.progress_var.set(50) try: search_box = driver.find_element(By.NAME, "q") search_box.send_keys(search_term) search_box.send_keys(Keys.RETURN) time.sleep(2) # 等待搜索结果 except NoSuchElementException: self.status_var.set("警告:未找到搜索框") # 执行截图 self.status_var.set("正在截图...") self.progress_var.set(80) timestamp = time.strftime("%Y%m%d-%H%M%S") prefix = self.prefix_entry.get().strip() or "screenshot" filename = f"{prefix}_{timestamp}.png" save_path = os.path.join(self.folder_entry.get(), filename) os.makedirs(os.path.dirname(save_path), exist_ok=True) driver.save_screenshot(save_path) self.progress_var.set(100) self.status_var.set(f"截图成功: {os.path.basename(save_path)}") except SessionNotCreatedException as e: self.status_var.set(f"驱动版本错误: {str(e)}") except WebDriverException as e: self.status_var.set(f"浏览器错误: {str(e)}") finally: try: driver.quit() except: pass # 主程序入口 if __name__ == "__main__": root = tk.Tk() app = WebScreenshotApp(root) root.mainloop() 这样的代码正确吗
08-30
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值