Python进阶

文章目录


一、Python进阶:字符和编码

1、字符编码的前世今生

(1)、字符集概述

简单来说字符集就是一套文字符号及其编码的描述。从20世纪60年代美国标准化组织ANSI发布了第一个计算机字符集ASCII开始,为了处理不同的文字,各大计算机公司,各国政府,以及各种标准化组织发明了几百种字符集,例如我们熟悉的:ASUII、USC、GBK、BIG5…这些不同的字符集从收录到编码都各不相同,给软件的移植和信息的交换带来了很大的困惑。最严重的问题就是字符乱码,这也是到现在为止还影响着每个测试和开发人员的根本问题

(2)、几个基本概念

(1)、位:计算机的最小单位,二进制中的一位,用二进制的0/1表示

(2)、字节:八位组成一个字节

(3)、字符:我们肉眼可见的文字与符号

(4)、字符集:字符的集合

(5)、编码:将字符转换成计算机可识别的0/1代码

(5)、解码:将计算机表示的0/1编码转换成肉眼可见的字符

(3)、字符编码的起源:ASCLL

在第一代计算机发明的时候就确定了以二进制方式存储数据的基本逻辑,所以我们所见的所有信息,要想持久化就必须转换成计算机可识别的二进制编码。所以就必须建立一套字符对应二进制的对应关系。所以美国国家标准协会(American National Standard Institution)就收集了当时美国所使用的所有字符(一共128个)采用8个二进制位来建立了字符和对应的二进制编码的对应关系

0000 0000-0001 1111  # 共33种状态表示特殊的终端控制
0011 0000-0011 1010  # 共10个表示0-9十个字母
0100 0001-0101 1010  # 共26个表示大写字母
0110 0001-0111 1010  # 共26个表示小写字母
# 一个128个占7位,高位补0,所以共8位,也就是说一个字符占用一个字节

(4)、字符编码的发展:百家争鸣

计算机迅速发展并传入欧亚,所以每个国家都面临着字符以及字符对应的二进制转换问题。下面将分析在这个发展阶段,比较典型的两种字符

  • 1)EASCLL

计算机传入欧洲之后。当时欧洲国家的字符组成基本是:英文字母+本国特有字符,所以西欧的一些国家就联合起来,将ASCLL的最高位利用起来(128-255)之间的数字进行编码,用来表示本国特有字符,进行了统一编码。所以EASCLL还是单字节编码

  • 2)GBK

当计算机传出到中国之后,国家也面临字符编码相关的工作,所以当时就推动制定了一套双字节编码编码规范,他收录了常见的6000多个中文、东亚文字以及欧美文字,所有的字母采用两个字节进行编码,称为GBK,由于微软等公司的支持,所以GBK在中国很流行,以至于现在的很多小企业还是采用GBK编码

(5)、字符编码的现状:大一统

1)、UCS系列

随着信息交流越来越频繁。字符乱码的问题也就越来越突出,统一编码的需求也就越来越强烈。所以IOS组织就提出了一个统一的编码方案。大致的规则如下:

  • 所有的字符采用四个字节进行编码

  • 将这个编码空间划分为(组,面,行,列)四个部分,并且最高位固定为0,所以一共可以表达的字符个数为128255255*255总共可以表示2亿多个字符

  • 因为当时采用了四字节的编码方式,所以这种编码方式又称为UCS-4

2)unicode系列

UCS-4编码推出之后遭受到了美国的大公司的反对,当时由施乐公司牵头,联合了Google、MicroSoft、IBM、Oracle、SUN、Apple等公司成立了Unicode委员会,研究自己的编码和解码规范,然后推出了Unicode1.0编码规范。Unicode1.0采用双字节编码,收录了世界上大部分常用的字符。在这段时间就出现了两个不同的规范。ios和unicode组织也意识到了这个问题的严重性,于是两者开始对话。最终的结果是:将Unicode的双字节编码,纳入到UCS编码规范的0组0面

UCS-4的0组0面也称为基本多语言平面(Basic MultiLanguage Plain)也就是说UCS-4的高2字节都为的时候称为BMP,BMP和UCS-4的转换也很简单,在BMP的高位添加两个字节的0就称为UCS-4,将UCS-4的高二字节都变成0就变成了BMP

ASCII、GB2312、GBK、GB18030、Unicode、UTF-8

2、Python中的字符编码

(1)、Python的字符串

1)、在Python3中字符串是以Unicode编码的,也就是说,Python的字符串默认支持多语言

2)、对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符

print(ord("王"))
# print(ord("王者"))  # 报错,单个字符

print(chr(32774))
# print(chr(89562))  # 报错,每个这个整数表示的字符

在这里插入图片描述

(2)、bytes和str

a = "abc"
b = b"abc"

print(a)
print(b)

print(type(a))
print(type(b))

在这里插入图片描述

# str 与 pytes 互转
a = "自君之出矣,不复理残机"
print(a.encode("UTF-8"))

c = b'\xe8\x87\xaa\xe5\x90\x9b\xe4\xb9\x8b\xe5\x87\xba\xe7\x9f\xa3\xef\xbc\x8c\xe4\xb8\x8d\xe5\xa4\x8d\xe7\x90\x86\xe6\xae\x8b\xe6\x9c\xba'
print(c.decode("UTF-8"))  # 以 encoding 指定的编码格式解码字符串,默认编码为字符串编码

在这里插入图片描述

(3)、数据库查询操作

(4)、数据库跟新操作

(5)、删除操作

Python进阶:Python对中文的处理

with open("./a.txt", "w+", encoding="gb2312") as file1:
    file1.write("红豆生南国,春来发几枝!")

with open("./a.txt", "r+", encoding="gbk") as file1:  # gbk和gb2312中这几个字符的编码一致,可以读取成功
    print(file1.read())

在这里插入图片描述

with open("./b.txt", "wb") as file1:  # wb 二进制方式读写
    file1.write("美人卷珠帘,".encode("big5"))  # big5 台湾编码格式

with open("./b.txt", "rb") as file1:
    print(file1.read().decode("big5"))  # 以 encoding 指定的编码格式解码字符串,默认编码为字符串编码

在这里插入图片描述

pngData = b""

# 以二进制模式读,存磁盘
with open("./截图.png", "rb") as file1:
    pngData = file1.read()

# 以二进制模式写,从磁盘
with open("./a.png", "wb") as file1:
    file1.write(pngData)

在这里插入图片描述

import chardet  # chardet模块获取文件编码类型


def get_encoding(file):
    with open(file, "rb") as file1:  # 以二进制方式读取,获取字节数据,检测类型
        data = file1.read(1024)
        return chardet.detect(data)  # ["encoding"]


file_name = './a.txt'
print(get_encoding(
    file_name))  # {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} encoding:编码 confidence:相似度 language:语言

在这里插入图片描述

import requests

url = 'https://www.baidu.com'
response = requests.get(url)

# encoding 表示的是响应当前的编码方式,而 apparent_encoding 则是你所获取的网页要求的编码方式,通过将 apparent_encoding 赋值给 encoding 就可以保证网页不乱码
response.encoding = response.apparent_encoding

# 对响应的操作---text属性
print(response.text)  # 这行代码会打印出 html 页面的源代码

# 对响应的操作---content属性
print(response.content)  # content 是网页内容进行二进制编码后的结果

在这里插入图片描述

Python进阶:Python对外部程序的调用

为什么叫“胶水语言”

  • 现成的工具软件,它们都可以完成一些功能(wget,ffmpeg)

  • 有时需要扩展一下,添加一点功能(free)

  • 有时需要把工具软件组合起来(free,gnuplot)

粘合各种外部程序和各种语言的库,实现功能

1、os.system

(1)、os库里面的system函数,等于打开操作系统的shell,敲入一串命令

import os

os.system("ipconfig")  # 阻塞

os.system("cmd.exe")  # 控制台中运行cmd程序

os.system("mspaint")  # 打开windows画板,相当于打开操作系统的shell执行命令

在这里插入图片描述

(2)、阻塞式的调用

import os

os.system("cmd.exe")  # 控制台中运行cmd程序

os.system("mspaint")  # 打开windows画板,相当于打开操作系统的shell执行命令

print("after call")

1)调用的外部程序没有退出时,Python程序会一直停在那里

  • 直到外部程序退出了,代码才接着往下执行

2)上面的最后一行代码里面打印的“after call”

  • 直到os.system(“cmd.exe”)/os.system(“mspaint”)执行完成才会接着往下执行

(3)、Python在调用这些程序时,退出码作为返回值返回

retCode = os.system("mspaint")
# retCode = os.system("mspaint3")  # 返回1

print("retCode=", retCode)

在这里插入图片描述

2、subprocess

(1)、subprocess.check_output 需要等到被调用程序退出,才能返回

import subprocess

# 执行命令,将结果以字节形式返回  bytes类型
output_pytes = subprocess.check_output("ipconfig")

print(output_pytes)
print(output_pytes.decode("gbk"))  # 以 encoding 指定的编码格式解码字符串,默认编码为字符串编码

在这里插入图片描述

(2)、subprocess库里面的Popen类:

  • 被调动程序运行时候,就获取其输出的信息

  • 运行时,输入一些信息给被调用程序

(3)、非阻塞式调用外部程序,调用外部程序后,Python程序继续执行

import subprocess  # 非阻塞,重新启动一个进程

subprocess.Popen("mspaint")
print("after")

在这里插入图片描述

一、Python进阶:装饰器

1、装饰器前戏

(1)、在Python这个国家里,装饰器一个重点、也是一个难点,是一个十二分重要的高级函数

(2)、如果将装饰器比作通过Python之路上的一个大boss,那么想刷这个副本就必须学会三个克制技能

  • 人阶功法 作用域:LEGB

  • 地阶功法 高阶函数:函数即对象

  • 天阶功法 闭包

1.1、人阶功法 作用域:LEGB

1.1.1、L(local)局部作用域

局部变量:包含在def关键字定义的语句块中,即在函数中定义的变量

def foo():
    a = 100  # 局部变量
    print(a)

foo()

# print(a)  # 全局作用域,找不到 a

在这里插入图片描述

1.1.2、E(enclosing)嵌套作用域

1)、E也包含在def关键字中,E和L是相对的,E相对于更上层的函数而言也是L

2)、在函数foo内部定义一个函数bar,foo中的变量就属于E,bar中的变量就属于L

b = 99  # 全局变量


def foo():
    a = 100  # 局部变量
    b = 200  # 局部变量 b
    print(a)
    print("打印最近值:", b)

    # print(c)  # 找不到

    def bar():
        c = 299  # 嵌套作用域
        print(a, b, c)

    bar()


foo()

print("打印本层值:", b)

在这里插入图片描述

1.1.3、G(global)全局作用域

1)即在模块层次中定义的变量,每一个模块都是一个全局作用域

2)一个.py文件就是一个模块

3)注意:全局作用域的作用范围仅限于单个模块文件内

b = 99  # 全局变量


def foo():
    print(b)


foo()

print(b)  # 全局作用域

在这里插入图片描述

1.1.4、B(built-in)内置作用域

系统内固定模式里定义的变量。如预定义在builtin模块内的变量

print("内置作用域:", __name__)  # Python预先定义的

在这里插入图片描述

1.2、地阶功法 高阶函数:函数即对象

(1)、在Python的世界里,函数和我们之前的[1,2,3],‘abc’,8等一样都是对象,而且函数是最高级的对象

(2)、函数对象的调用仅仅比其它对象多了一个()而已,foo与a、b一样都是个变量名

b = '外挂'
a = b
print(a)


def foo():
    print("Python!!!")


a = foo  # foo是一个函数名

print(a)

a()

在这里插入图片描述

(3)、所以:

  • 函数名可作为参数传递,可以被赋给其他变量
def foo(func):
    func()
    

def bar():
    print("Python!!!")


foo(bar)

在这里插入图片描述

  • 函数名可作为返回值
def foo():
    def bar():
        print("过年啦!!!")

    return bar


a = foo()
a()

在这里插入图片描述

注意:这里说的函数都是指函数名,比如foo;而foo()已经执行函数了,foo()是什么类型取决于return的内容是什么类型!!!

1.3、天阶功法 闭包

在一个内部函数里边,对在外部作用域(但不是全局作用域)的变量进行引用那么这个内部函数就被认为是闭包

def outer():
    x = 10

    def inner():  # 内部函数
        print(x)  # 引用了外部函数的变量,但不是全局作用域的变量

    return inner


# outer()()
a = outer()  # 返回 inner,相当于 a = inner
a()  # 相当于 inner()

在这里插入图片描述

如上实例:inner就是内部函数,inner里引用了外部作用域的变量x(x在外部作用域outer里边但不是全局作用域)

2、装饰器高潮

装饰器概念

(1)、装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额为的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲:装饰器的作用就是为已经存在的对象添加额外的功能

(2)、装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内

装饰器的应用

现有自动化测试平台代码,老板要求:在测试用例代码执行测试用例的时候,把服务器的响应快慢记录下来

import time

def foo():
    print("执行测试用例:")
    time.sleep(1)

方法一:直接修改源代码----------老板决定扣工资

  • 开放封闭原则:允许扩展已有功能代码、禁止修改已有功能代码

方法二:重写代码----------所有同事一致决定打死你

  • 工作量太大

  • 重复代码

  • 重复代码怎么解决

import time


def show_time(func):  # 装饰函数,也就是装饰器
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print("执行测试用例花费时间:", end_time - start_time)

    return inner


@show_time  # 相当于 foo = show_time(foo)
def foo():  # 被装饰函数
    print("执行测试用例!!!")
    time.sleep(1)


# foo = show_time(foo)  # 执行 show_time 会返回 inner,并且我们用 foo 接受了 inner
foo()

在这里插入图片描述

Python进阶:多线程原理

举个栗子、并发的概念

(1)、当我们烧水的时候我们往往不是在旁边等着它烧水,而是在它烧开之前我们也许会做些其它的事情比如看书、看会儿电视,一旦水烧开了我们就去处理烧开的水(比如泡茶)。这也体现出我们做事的效率,计算机也是如此

在这里插入图片描述

(2)、在单核CPU的计算机上,从微观层面上来说永远只有一个线程在执行,也就是说,一台计算机同时只能做一件事,那么、它是如何满足你一边看电影一边听音乐的需求的呢???

并发与并行

(1)、并发:逻辑上具备同时处理多个任务的能力

(2)、并行:物理上在同一时刻执行多个并发任务

在这里插入图片描述

什么是线程、什么是进程

(1)、开个QQ,开了一个进程;开了迅雷,开了一个进程

(2)、在QQ的这个进程里,传输文字开一个线程、传输语言开了一个线程、弹出对话框又开了一个线程

(3)、所以运行某个软件,相当于开了一个进程。在这个软件运行的过程里(在这个进程里),多个工作同时运转,完成了QQ的运行,那么这“多个工作”分别有一个线程,所以一个进程管着多个线程,一个进程有且至少有一个线程

线程与进程的区别

(1)、线程共享创建它的进程的地址空间;进程有自己的地址空间

(2)、线程可以直接访问其进程的数据段;进程有它们自己的父进程的数据段副本

(3)、线程可以直接与进程中的其他线程通信;进程必须使用进程间通信来与兄弟进程通信

(4)、容易创建新线程;新进程需要父进程的复制

(5)、线程可以对同一进程的线程进行相当大的控制;流程只能对子流程进行控制

(6)、对主线程的更改(取消、优先级更改等)可能会影响进程中其他线程的行为;对父进程的更改不会影响子进程

import threading, time


def foo(something):
    for i in range(5):
        time.sleep(1)
        print(something)


# 创建线程
t1 = threading.Thread(target=foo, args=("看电影",))  # target指向函数名foo,args传参为"看电影"
t2 = threading.Thread(target=foo, args=("听音乐~~~",))

# 启动线程
t1.start()
t2.start()

在这里插入图片描述

join

import threading, time


def foo(something):
    for i in range(5):
        time.sleep(1)
        print(something)


# 创建线程
t1 = threading.Thread(target=foo, args=("数据逻辑一",))  # target指向函数名foo,args传参为"看电影"
t2 = threading.Thread(target=foo, args=("数据逻辑二",))

# 启动线程
t1.start()
t2.start()
# join 在子线程完成运行之前,这个子线程的父线程将一直被阻塞
t1.join()
t2.join()

print("启动数据检查...")

在这里插入图片描述

守护线程

import threading, time


def foo(something):
    for i in range(5):
        time.sleep(1)
        print(something)


# 创建线程
t1 = threading.Thread(target=foo, args=("生产数据1",))  # target指向函数名foo,args传参为"看电影"
t2 = threading.Thread(target=foo, args=("生产数据2",))

# 声明守护线程,必须在 start 方法调用之前声明守护线程
t1.setDaemon(True)
t2.setDaemon(True)

# 启动线程
t1.start()
t2.start()

for i in range(3):
    time.sleep(1)
    print("消费数据")

print("消费需求已经满足了")  # 已满足后,退出主线程

在这里插入图片描述

Python 并发实战: 为什么需要多线程

要把100M的数据写入磁盘,CPU输出100M的数据只需要0.01秒,可以磁盘要接收这100M数据可能需要10秒,怎么办?有两种办法

第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行

第二种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”

不安全的并发

小明有一张银行卡余额500元,这张银行卡绑定了小明妻子的微信和支付宝手中。小明今天发工资了,收入人民币10000元,在银行汇款的同时小明的妻子通过支付宝消费口红一只300元

  • 两件事都需要操作一个变量–>账户余额

  • 两件事几乎同时发生,所以同时取到余额这个变量 balance ,都是500元

  • 线程 A 计算工资收入: 500+10000 = 10500元 balance = 10500

  • 线程 B 计算消费支出: 500-300 = 200元 balance = 200

  • 显然,并发操作数据是不安全的

同步锁

import threading, time

account_balabce = 500  # 银行卡账户余额
r = threading.Lock()  # 创建一把锁,同步锁


def foo(num):  # 操作账户余额函数
    r.acquire()  # 上锁
    global account_balabce  # 声明对全局变量的引用,当需要修改全局变量时,需要声明
    balabce = account_balabce  # 通过接口调用取到账户余额
    balabce = balabce + num
    time.sleep(2)
    account_balabce = balabce  # 计算结果,将结果存回数据库或通过接口返回新的余额
    r.release()  # 解锁,数据操作完需要解锁


t1 = threading.Thread(target=foo, args=(10000,))  # 银行转账
t2 = threading.Thread(target=foo, args=(-300,))  # 口红消费

t1.start()
t2.start()
t1.join()  # 阻塞父线程
t2.join()

print("最终账户余额:", account_balabce)

在这里插入图片描述

如代码,需要操作账户余额的时候,我们给它上一把锁,同一时间只允许一个线程进行操作,等操作完毕再把锁打开,如此就避免了数据安全的问题

死锁

在线程间共享多个资源的时候,如果两个线程方别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去

import threading, time

lockA = threading.Lock()  # 面试官的锁
lockB = threading.Lock()  # 求职者的锁


def foo1():  # 面试官
    lockA.acquire()
    print("面试官:请解释什么是死锁")
    time.sleep(1)

    lockB.acquire()
    print("给求职者发offer")

    lockA.release()
    lockB.release()


def foo2():  # 求职者
    lockB.acquire()
    print("求职者:给我发offer")
    time.sleep(1)

    lockA.acquire()
    print("给面试官解释什么是死锁")
    time.sleep(1)


t1 = threading.Thread(target=foo1)
t2 = threading.Thread(target=foo2)

t1.start()
t2.start()

在这里插入图片描述

递归锁(可重入锁)

(1)、为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.Rlock

(2)、Rlock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获取资源

import threading, time

lock = threading.RLock()  # 递归锁,也叫可重入锁;内部存在一个计算器,上锁时,计算器加1,解锁时,计数器减1;计算器不能小于0


def foo1():  # 面试官
    lock.acquire()
    print("面试官:请解释什么是死锁")
    time.sleep(1)

    lock.acquire()
    print("给求职者发offer")

    lock.release()
    lock.release()


def foo2():  # 求职者
    lock.acquire()
    print("求职者:给我发offer")
    time.sleep(1)

    lock.acquire()
    print("给面试官解释什么是死锁")
    time.sleep(1)


t1 = threading.Thread(target=foo1)
t2 = threading.Thread(target=foo2)

t1.start()  # t1先执行,当计数器为0时,在运行其他线程
t2.start()

在这里插入图片描述

Python进阶:socket服务器和客户端开发

1、计算机网络

1.1、OSI模型

OSI中的层功能TCP/IP协议族交换单元名称
交换层一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类TFTP、HTTP、SNMP、FTP、SMTP、DNS、Telnet数据报文(Message)
表示层主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩没有协议数据报文(Message)
会话层通过传输层(端口号:传输端口与接收端口)建立数据传输的通路没有协议数据报文(Message)
传输层定义了一些传输数据的协议和端口号(WWW端口80等)TCP(面向连接)、UDP(面向无连接)数据报文(Message)
网络层主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装IP、ICMP、RIP、OSPF、BGP、IGMP数据包(Packet)
数据链路层主要将从物理层接收的数据进行MAC地址的封装与解封装SLIP、CSLIP、PPP、ARP、RARP、MTU数据帧(Frame)
物理层主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等ISO2110、IEEE802.1、EEE802.2数据比特流(Bits)

1.2、网络通信要素

1.2.1、IP地址

(1)、用来标识网络上一台独立的主机

(2)、IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段。主机号:用于识别该网络中的主机)

(3)、特殊的IP地址:127.0.0.1(本地回环地址、保留地址、点分十进制)可用于简单的测试网卡是否故障。表示本机

1.2.2、端口号

(1)、用于标识进程的逻辑地址。不同的进程都有不同的端口标识

(2)、端口:要将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这些数字,则将这些数字称为端口(此端口是一个逻辑端口)

1.2.3、传输协议

(1)、通讯的规则。例如:TCP、UDP协议(好比两个人得用同一种语言进行交流)

(2)、UDP:User Datagram Protocol用户数据报协议

特点:

  • 面向无连接:传输数据之前源端和目的端不需要建立连接

  • 每个数据报的大小都限制在64K(8个字节)以内

  • 面向报文的不可靠协议(即:发送出去的数据不一定会接收得到)

  • 传输效率快,效率高

  • 现实生活实例:邮局寄件、实时在线聊天、视频会议…等

(3)、TCP:Transmission Control Protocol传输控制协议

特点:

  • 面向连接:传输数据之前需要建立连接

  • 在连接过程中进行大量数据传输

  • 通过“三次握手”的方式完成连接,是安全可靠协议

  • 传输速度慢,效率低

1.3、socket编程

(1)、想要理解socket,就要先来理解TCP,UDP协议

(2)、TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中

  • 应用层:TFTP、HTTP、SNMP、FTP、SMTP、DNS、Telnet等等

  • 传输层:TCP、UDP

  • 网络层:IP、ICMP、OSPF、EIGRP、IGMP

  • 数据链路层:SLIP、CSLIP、PPP、MTU

(3)、每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的利用ip地址 + 协议 + 端口号唯一标识网络中的一个进程。能够唯一标识网络中的进程后,它们就可以利用socket进行通信了,我们经常把socket翻译为套接字,socket是在应用层和传输层(TCP/IP协议族通信)之间的一个抽象层,是一组接口,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信

(4)、应用程序两端通过“套接字”向网络发出请求或者应答网络请求。可以把socket理解为通信的把手(hand)

(5)、socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种“打开——读/写——关闭”,模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通信结束时关闭文件。socket的英文原义是“插槽”或“插座”,就像我们家里座机一样,如果没有网线的那个插口,电话是无法通信的

(6)、socket是实现TCP、UDP协议的接口,便于使用TCP、UDP

1.4、socket通信流程

在这里插入图片描述

(1)、服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket

(2)、服务器为socket绑定ip地址和端口号

(3)、服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开

(4)、客户端创建socket

(5)、客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket

(6)、服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直等到客户端返回连接信息后才返回,开始接收下一个客户端连接请求

(7)、客户端连接成功,向服务器发送连接状态信息

(8)、服务器accepyt方法返回,连接成功

(9)、客户端向socket写入信息(或服务端向socket写入信息)

(10)、服务器读取信息(客户端读取信息)

(11)、客户端关闭

(12)、服务器端关闭

2、socket实战

2.1、socket实战

服务端

import socket

# 创建 socket 对象(套接字)
sk = socket.socket()

# 将 socket 与 ip 地址和端口进行绑定
sk.bind(("127.0.0.1", 1300))  # 使用元组进行传参

# 监听绑定的端口,有没有请求发过来
sk.listen()
print("服务器已经启动...")

# 等待传入连接,在连接成功之前,保持阻塞状态
# 连接成功之后,解除阻塞状态,会返回一个新的套接字(socket对象:conn)和客户端的ip地址和端口
conn, addr = sk.accept()
print("客户端的ip地址和端口是:", addr)

# 接收客户端返回信息
data = conn.recv(1024)  # 不知道接收的数据量有多大,需要指定大小,每次接收1024个字节,超出循环接收
print("客户端:", data.decode("utf8"))  # 字符串不可以在网络间进行传输;以 encoding 指定的编码格式解码字符串,默认编码为字符串编码

# 发送数据
server_input = input("请输入>>>")
conn.sendall(server_input.encode("utf-8"))  # 字符串不可以在网络间进行传输

# 关闭socket
conn.close()
sk.close()

在这里插入图片描述

客户端

import socket

# 创建 socket 对象(套接字)
sk = socket.socket()

# 连接指定计算机端口
sk.connect(("127.0.0.1", 1300))  # 使用元组进行传参

# 发送数据
client_input = input("请输入:")
sk.sendall(client_input.encode("utf8"))

# 接收服务器数据
data = sk.recv(1024)  # 不知道接收的数据量有多大,需要指定大小,每次接收1024个字节,超出循环接收
print("服务器:", data.decode("utf8"))

# 关闭socket
sk.close()

在这里插入图片描述

2.2、文件上传

2.3、屌丝追女神的故事:远程聊天

2.4、坚持的屌丝:不间断聊天和退出处理

2.5、一大波屌丝正在接近:并发处理

Python进阶:远程控制Linux

安装Paramiko

cmd执行下面的命令

pip install paramiko --default-timeout=60
pip install paramiko -i https://pypi.douban.com/simple/--trusted-host pypi.douban.com

Linux主机SSH安装

(1)、保证有一台Linux主机

  • 自己搭建虚拟机

  • 如果没有 临时使用 云主机

(2)、保证SSH服务开启

在Linux机器上执行:

  • 1):sudo apt-get install ssh 或者 sudo apt-get install openssh-client

  • 2):ssh-keygen 中间会让确认,一路回车即可

  • 3):sudo apt-get update

  • 4):sudo apt-get install openssh-server

  • 5):上边就完成了ssh服务的安装,接下来检查是否启动 ps -e|grep ssh

  • 6):如果只有ssh-agent那ssh-server还没有启动,需要/etc/init.d/ssh start,如果看到sshd那说明ssh-server已经启动了

  • 7):如果没有则可以输入:sudo /etc/init.d/ssh start

可参考:https://blog.csdn.net/study_crazy_dog/article/details/115049145

Linux主机SSH配置

(1)、ssh-server配置文件位于/etc/ssh/sshd_config,在这里可以定义SSH的服务端口,默认端口是22,可以自己定义成其他端口号,如22333。然后重启SSH服务

(2)、通过修改配置文件/etc/ssh/sshd_config,可以改ssh登录端口和禁止root登录。改端口可以防止被端口扫描:

  • sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.original

  • sudo chmod a-w /etc/ssh/sshd_config.original

(3)、编辑配置文件:gedit /etc/ssh/sshd_config

  • 找到 #Port 22 修改为: Port 22333

  • 找到 #PermitRootLogin,在下边添加一行:PermitRootLogin no

(4)、配置完成后重启:sudo /etc/init.d/ssh restart

Linux示例操作

创建目录、文件、列出目录中文件、查看内容

  • 登录

  • mkdir

  • touch file; echo ‘abcd’ > file

  • ls

  • cat

import paramiko

# 创建 ssh 对象
ssh = paramiko.SSHClient()

# 连接方法
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 发起连接
ssh.connect("192.168.118.137", 22, "liuzhaoquan", "123456")

"""
# 在远程Linux执行命令
stdin, stdout, stderr = ssh.exec_command("ip addr show")  # stdin 标准输入文件;stdout 标准输出文件;stderr 错误信息文件

# 将执行结果 stdout 打印出来
print(stdout.read().decode("utf8"))
"""

sftp = ssh.open_sftp()

### 从运程 Linux 下载文件到本地
# sftp.get("/abc/passwd.soft", "./passwd.txt")  # PermissionError: [Errno 13] Permission denied:

### 将本地文件传送到远程Linux
sftp.put("./passwd.txt", "/abc/passwd.txt")  # 需指定一个文件名称

# 关闭 ssh 连接
ssh.close()

在这里插入图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鹿快跑~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值