【Python】进阶 - 网络编程(TCP开发基础、进程和线程)

系列篇章🎉
No.文章
1【Python】基础知识(详细)🚀
2【Python】基础 - 循环、容器类型🚀
3【Python】基础 - 推导式、函数🚀
4【Python】基础 - 文件、异常、模块🚀
5【Python】进阶 - 面向对象(详细)🚀
6【Python】进阶 - 闭包、装饰器、深浅拷贝🚀

目录

系列篇章🎉

一、计算机网络概述

1.1 网络编程三要素

1.2 编码转换

 二、TCP开发基础

2.1 socket类

 2.2 服务器端

2.3 客户端

三、进程和线程基础

3.1 进程简介

3.2 线程


一、计算机网络概述

1.1 网络编程三要素

IP地址(Internet Protocol Address)

  • IP 地址是用来标识网络中唯一一台设备的地址。在 TCP/IP 网络中,每台设备必须有一个唯一的 IP 地址才能进行通信
  • IPv4:32位地址,通常以点分十进制表示(如 192.168.1.1
  • IPv6:128位地址,用于解决 IPv4 地址不足的问题(如2001:0db8:85a3::8a2e:0370:7334

端口号(Port Number)

  • 端口号用来标识运行在目标设备上的特定应用程序或服务。它是一个 16 位整数(范围为 0~65535)
  • 常见服务有固定端口,例如:HTTP:80、HTTPS:443、FTP:21、SSH:22
  • 一般程序员开发应用程序使用的端口号成为动态端口号范围是1024至65535,不过建议使用8000以后的

协议(Protocol)

协议定义了数据在网络中的传输规则和格式,常见的网络协议包括:

  • TCP(Transmission Control Protocol):面向连接、可靠传输、按序交付,适合对数据准确性要求高的场景(如网页浏览、文件传输)
  • UDP(User Datagram Protocol):无连接、不可靠传输、速度快,适合实时性要求高的场景(如音视频传输、游戏)

1.2 编码转换

在计算机网络中,数据是以二进制进行传输的。所以在网络传输数据的过程中,数据需要先转化为二进制(bytes)数据类型。

方法类型转换方向作用
encode()strbytes字符串编码成字节
decode()bytesstr字节解码成字符串

字符串编码:

        str.encode([encoding='utf-8'])

字节解码:

        bytes.decode([encoding='utf-8'])

encoding:指定解码格式,默认是 'utf-8' 。

 二、TCP开发基础

2.1 socket类

socket 类是进行网络编程的核心工具之一,它封装了底层的 TCP/IP 协议栈,使用socket可以方便地进行客户端-服务器通信。

# 1. 导入socket模块
import socket

# 2. 创建客户端socket对象使用socket类
socket.socket(AddressFamily, Type)
参数含义
AddressFamily地址族(Address Family),常用 socket.AF_INET(IPv4)或 socket.AF_INET6(IPv6)
Type套接字类型,常用 socket.SOCK_STREAM(TCP)或 socket.SOCK_DGRAM(UDP)

 2.2 服务器端

主动发起建立连接请求的是客户端程序,等待接受连接请求的是服务端程序。

方法名功能说明
bind(address)将 socket 绑定到指定地址(IP 和端口)
listen(backlog)开始监听连接请求(仅用于 TCP)
accept()接受客户端连接,返回一个新的 socket 和客户端地址
scend()发送数据
recv()接收数据
import socket

if __name__ == '__main__':
    # 1. 创建服务器端套接字对象
    tcp_server_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2. 绑定IP地址和端口号(ip不写代表本机)
    tcp_server_client.bind(('', 8888))
    # 3. 开启监听(listen(10) 的作用就是告诉操作系统:最多允许有 10 个连接请求在排队。)
    tcp_server_client.listen(10)
    # 4. 接收客户端连接请求(利用拆包)
    conn_socket,ip_port = tcp_server_client.accept()
    print(f'客户端IP地址和端口号:{ip_port}')
    # 5. 接收数据
    recv_data = conn_socket.recv(1024).decode()
    print(f'接收到的数据:{recv_data}')
    # 6. 发送数据(.encode()将字符串转为bytes,默认编码方式就是utf-8)
    conn_socket.send(f'客户端的数据已经收到了'.encode())
    # 7. 关闭套接字
    conn_socket.close()
    tcp_server_client.close()

2.3 客户端

客户端常用方法:

方法名功能说明
connect(address)连接到服务器(适用于 TCP)
send(bytes)发送数据(TCP)
recv(bufsize)接收数据(TCP)
sendto(bytes, address)发送数据(UDP)
recvfrom(bufsize)接收数据和发送方地址(UDP)
import json
import socket

if __name__ == '__main__':
    # 1. 创建客户端套接字对象
    tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 2. 和服务器套接字建立连接(参数必须是一个元组)
    tcp_client_socket.connect(('localhost',8888))
    # 3. 发送数据
    str1 = input('请输入传输信息:\n')
    tcp_client_socket.send(str1.encode(encoding='utf-8'))
    # 4. 接收数据
    recv_data = tcp_client_socket.recv(1024).decode('utf-8')
    print(recv_data)
    # 5. 关闭客户端套接字
    tcp_client_socket.close()

三、进程和线程基础

进程(Process)线程(Thread) 是实现并发编程的两种主要方式。它们都可以用来提高程序的执行效率,但适用于不同的场景。

特性进程(Process)线程(Thread)
内存是否共享
资源占用
切换开销
是否受 GIL 限制否(每个进程有自己的 GIL)
并行能力支持多核并行(CPU 密集型推荐)不支持真正的并行(IO 密集型推荐)
稳定性高(一个进程崩溃不影响其他)低(共享资源可能互相影响)
编程复杂度相对较高较低

并发:指多个任务在逻辑上同时进行,但在物理层面可能是交替执行的。比如对于单核cpu处理多任务,操作系统轮流让各个任务交替执行 。

 并行:指多个任务真正地在同一时刻执行,通常依赖于多核 CPU 或多台机器。

对比项并发(Concurrency)并行(Parallelism)
是否真正同时执行❌ 不一定,通常是交替执行✅ 真正同时执行
主要用途IO 密集型任务(如网络请求、文件读写)CPU 密集型任务(如图像处理、计算)
实现方式单线程/多线程、事件循环多进程、多线程(多核)、GPU 加速
Python 示例threading, asynciomultiprocessing, ProcessPoolExecutor
资源占用较低较高
稳定性可能因资源共享产生竞争条件进程间隔离,更稳定

3.1 进程简介

进程是操作系统中资源分配的基本单位,通俗理解一个正在运行的程序就是一个进程,每个进程都有自己独立的内存空间和系统资源。由 multiprocessing 模块来创建和管理进程。

特性描述
内存隔离进程之间内存不共享,数据更安全
稳定性高一个进程崩溃不影响其他进程
启动开销大创建和销毁进程比线程慢
利用多核可以利用多核 CPU 并行计算,适合 CPU 密集型任务

程序运行会默认创建一个进程,这个默认创建的进程我们称之为主进程

使用多进程之后会程序又创建的进程成为子进程。

基本用法:创建进程

Process(target=函数):指定进程执行的目标函数。
start():启动进程。

import multiprocessing
import time
def music():
    for i in range(2):
        print('听音乐...')
        time.sleep(0.1)

def play():
    for i in range(2):
        print('打篮球...')
        time.sleep(0.1)

if __name__ == '__main__':
    music_prosess = multiprocessing.Process(target=music)
    play_process = multiprocessing.Process(target=play)
    music_prosess.start()
    play_process.start()

带参数的进程: args以元组的方式给执行任务传参,kwargs以字典方式给执行任务传参。

import multiprocessing
import time

def play(num,sta_str):
    print(sta_str)
    for i in range(num):
        print('打篮球...')
        time.sleep(0.2)

def music(num):
    for i in range(num):
        print('rapper...')
        time.sleep(0.2)

if __name__ == '__main__':
    play_process = multiprocessing.Process(target=play,kwargs={'num':2,'sta_str':'多任务要开始了'})
    music_prosess = multiprocessing.Process(target=music,args=(2,))
    play_process.start()
    music_prosess.start()

获取进程编号:当程序中进程的数量越来越多时 , 如果没有办法区分主进程和子进程还有不同的子进程 , 那么就无法进行有效的进程管理 , 为了方便管理实际上每个进程都是有自己编号的。

import multiprocessing
import time
import os

def play():
    # getpid()获取当前进程(子进程)的编号
    print(f'play   >> {os.getpid()}')
    # getppid()获取父进程的编号
    print(f'play-p >> {os.getppid()}')
    time.sleep(0.2)
def rap():
    print(f'rap   >> {os.getpid()}')
    print(f'rap-p >> {os.getppid()}')
    time.sleep(0.2)

if __name__ == '__main__':
    print(os.getpid())
    play_process = multiprocessing.Process(target=play)
    play_rap = multiprocessing.Process(target=rap)
    play_process.start()
    play_rap.start()

 

 进程间不共享全局变量:

import multiprocessing
import time

my_list = []
def write():
    for i in range(3):
        my_list.append(i)
def read():
    print(my_list)

if __name__ == '__main__':
    write_process = multiprocessing.Process(target=write)
    read_process = multiprocessing.Process(target=read)
    write_process.start()
    time.sleep(1)
    read_process.start()    # []
    print(my_list)    # []

守护进程:保证主进程退出后子进程自动终止

import multiprocessing
import time

def work():
    for i in range(10):
        print('子进程...')
        time.sleep(0.3)

if __name__ == '__main__':
    work_process = multiprocessing.Process(target=work)
    # 方式一
    # 设置守护主进程,主进程退出后直接销毁子进程,不再执行子进程中的代码
    work_process.daemon = True
    work_process.start()
    time.sleep(1)
    # 方式二(不推荐,容易产生僵尸进程)
    # 在主进程退出之前,让所有的子进程全部直接销毁掉
    # work_process.terminate()
    print('主进程执行完毕')

3.2 线程

线程是程序执行的最小单位, 实际上进程只负责分配资源 , 而利用这些资源执行程序的是线程 , 也就说进程是线程的容器 , 一个进程中最少有一个线程来负责执行程序 。使用 threading 模块来创建和管理线程。

特性描述
共享内存多个线程可以访问相同的数据
启动快线程比进程轻量,启动速度快
GIL限制Python 的全局解释器锁(GIL)使多线程不能真正并行
适合IO密集型如文件读写、网络请求等

基本用法:创建线程

import threading
import time

def play(num):
    for i in range(num):
        print('打篮球...')
        time.sleep(0.2)

def rap(num,str1):
    print(str1)
    for i in range(num):
        print('rapper...')
        time.sleep(0.2)

if __name__ == '__main__':
    play_thread = threading.Thread(target=play,args=(3,))
    rap_thread = threading.Thread(target=rap,kwargs={'num':3,'str1':'开始rap'})
    play_thread.start()
    rap_thread.start()

线程守护

import threading
import time
""" 主线程默认等子线程运行结束后才终止 """
def work():
    for i in range(10):
        print('工作中...')
        time.sleep(0.2)

if __name__ == '__main__':
    # 方式一,创建子线程并设置守护主线程
    # work_thread = threading.Thread(target=work,daemon=True)
    work_thread = threading.Thread(target=work)
    # 方式二,设置守护主线程
    work_thread.daemon = True
    work_thread.start()
    time.sleep(1)
    print('主线程运行结束')

线程执行顺序:线程之间的执行是无序的,由CPU调度决定某个线程先执行

import threading
import time

def work():
    time.sleep(0.5)
    current_thread = threading.current_thread()
    print(current_thread)

if __name__ == '__main__':
    for i in range(10):
        work_thread = threading.Thread(target=work)
        work_thread.start()

线程间共享全局变量:多个线程都是在同一个进程中 , 多个线程使用的资源都是同一个进程中的资源 ,因此多线程间是共享全局变量

import threading
import time

list1 = []
def write():
    for i in range(3):
        list1.append(i)
def read():
    print(list1)

if __name__ == '__main__':
    write_thread = threading.Thread(target=write)
    read_thread = threading.Thread(target=read)

    write_thread.start()
    time.sleep(1)
    read_thread.start()     # [0, 1, 2]
    print(list1)    # [0, 1, 2]

 互斥锁

1. 创建互斥锁

lock = threading.Lock()

2. 上锁

lock.acquire()

3. 释放锁

lock.release()

        注意:如果加锁之后没有在合适的地方释放锁会形成死锁。

import threading

lock = threading.Lock()  # 创建一个锁对象,用于线程同步

sum = 0# 全局变量,用于累加计数
def play(num):
    global sum
    for i in range(num):
        lock.acquire()    # 加锁,防止多个线程同时修改sum
        sum += 1    # 修改共享资源(sum)的代码段
        lock.release()    # 释放锁
    print(f"{sum}---------打篮球----------")

def rap(num):
    global sum
    for i in range(num):
        lock.acquire()
        sum += 1
        lock.release()
    print(f"{sum}----------rapper----------")

if __name__ == '__main__':
    thread_s1 = threading.Thread(target=play,args=(1000000,))
    thread_Stu = threading.Thread(target=rap,args=(1000000,))

    thread_s1.start()
    thread_Stu.start()

上面代码如果不加锁的话低版本Python解释器会出现争抢资源的问题。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰糖猕猴桃

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

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

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

打赏作者

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

抵扣说明:

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

余额充值