11-什么是IPC,如何进行进程间通信、正向代理,反向代理、什么是粘包

1 什么是IPC,如何进行进程间通信
2 正向代理,反向代理
2.1 正向代理,反向代理详解
3 什么是粘包
3.1 黏包问题详解

1 什么是IPC,如何进行进程间通信

# 线程间通信:https://zhuanlan.zhihu.com/p/489305763
	-共享内存(共享变量)---》进程间变量是共享的---》lock---》临界区---》数据错乱
	 ---》互斥锁---》死锁问题---》递归锁(可重入锁)---》各种各样的锁

    -线程间变量共享(内存共享)----》多线程共享同一个变量---》数据错乱问题--》加锁
    
    
-IPC:Inter-Process Communication,进程间通信

进程间数据隔离
-两种情况:
    -同一台机器上的两个进程通信  python开启多进程
    -不同机器上的两个进程进行通信 
    
-如何通信:
    -python queue可以做进程间通信(队列,管道)
    -消息队列:redis就可以做消息队列,rabbitmq,kafka
    -socket套接字:(展现形式:1 服务和服务之间通过接口调用(http调用) 2 RPC调用:远程过程调用 3 socket客户端和服务端)
    -文件方式
IPC(Inter-Process Communication,进程间通信)是指两个或多个进程之间交换数据和信息的机制。
进程间通信是多进程系统中的关键概念,它允许不同进程之间进行协作,共享数据,以及实现更复杂的系统功能。

以下是一些常见的进程间通信的方式:
1. **管道(Pipe):**
   - 管道是一种单向通信机制,可以用于具有亲缘关系的进程间通信。
   - 有两种类型的管道:
     - 匿名管道:适用于具有父子关系的进程,通过 `pipe()` 系统调用创建。
     - 命名管道(FIFO):适用于无亲缘关系的进程,通过 `mkfifo()` 创建。
    
   - 管道是一种最基本的IPC机制。管道提供了一种单向通信机制,它是一个半双工的通信方式。
    通常情况下,一个管道用于连接两个相关进程。管道的基本操作包括创建管道、读写管道、关闭管道等。

2. **消息队列(Message Queue):**
   - 消息队列允许不同进程通过在队列中发送和接收消息来进行通信。
	   消息队列是有名字的,可以被无关进程访问。
	   在Linux中,使用 `msgget()`、`msgsnd()` 和 `msgrcv()` 等系统调用来操作消息队列。
	
	- 消息队列提供了一种进程间通信的方式,可以实现多个进程间的异步通信。
	消息队列支持多进程之间的通信,并提供了优先级和消息长度等额外的属性。

3. **共享内存(Shared Memory):**
   - 共享内存允许多个进程访问同一块内存区域,从而实现高效的数据共享。
   	 在Linux中,使用 `shmget()`、`shmat()` 和 `shmdt()` 等系统调用来操作共享内存。
   	
   - 共享内存是一种高效的进程间通信方式。它允许不同的进程在它们的虚拟地址空间共享同一个物理内存
   区域。共享内存的基本操作包括创建共享内存区域、映射共享内存、读写共享内存、删除共享内存等。

4. **信号(Signal):**
   - 信号是一种用于通知进程发生某事件的机制。
     例如,一个进程可以通过发送信号给另一个进程来通知它某个事件的发生。
   	 在Linux中,使用 `kill` 命令或 `signal()` 函数来发送信号。

   - 信号是一种异步通信方式,进程可以通过向其他进程发送信号来通知某个事件的发生。
   例如,进程可以通过发送SIGKILL信号杀死另一个进程。

5. **套接字(Socket):**
   - 套接字是一种用于实现网络通信的通用机制,但它也可以用于同一台机器上的进程间通信。
   在Linux中,套接字可以通过使用 `socket()`、`bind()`、`connect()` 等系统调用进行创建和操作。
   
6. **文件锁(File Lock):**
   - 进程可以使用文件锁来协调对文件的访问,实现进程间通信。
   	在Linux中,通过 `fcntl()` 等系统调用来实现文件锁。

选择何种IPC方式通常取决于具体的需求,不同的场景可能适合不同的通信机制。
例如,如果需要高效的数据共享,可以选择共享内存;
如果需要简单的消息传递,可以选择消息队列;
如果需要在远程机器上通信,可以选择套接字等。

2.1 匿名管道

在Python中,可以使用`os.pipe()`函数创建匿名管道。
以下是一个简单的例子,演示如何使用匿名管道进行父子进程通信:

import os

def parent_process(pipe):
    # 在父进程中关闭写端
    pipe[1].close()

    while True:
        # 从管道中读取数据
        data = pipe[0].readline().rstrip()
        if not data:
            break
        print(f"Parent received: {data}")

    # 在父进程中关闭读端
    pipe[0].close()

def child_process(pipe):
    # 在子进程中关闭读端
    pipe[0].close()
    messages = ["Message 1", "Message 2", "Message 3"]
    for message in messages:
        # 向管道中写入数据
        pipe[1].write(message + "\n")
        pipe[1].flush()
        print(f"Child sent: {message}")
    # 在子进程中关闭写端
    pipe[1].close()

if __name__ == "__main__":
    # 创建匿名管道
    pipe = os.pipe()
    # 创建子进程
    pid = os.fork()
    if pid > 0:
        # 在父进程中
        parent_process(pipe)
    elif pid == 0:
        # 在子进程中
        child_process(pipe)
    else:
        print("Fork failed.")


这个例子创建了一个父进程和一个子进程,它们之间通过匿名管道进行通信。父进程读取子进程发送的消息,子进程发送一些消息到父进程。在实际应用中,可以使用更高级的模块,如`multiprocessing`,来更方便地实现进程间通信。

2.1 命名管道

在Python中,可以使用`os.mkfifo()`函数创建命名管道(FIFO)。

以下是一个简单的例子,演示如何使用命名管道进行两个无亲缘关系的进程通信:
import os
fifo_path = "myfifo"

def writer_process():
    # 打开命名管道进行写操作
    with open(fifo_path, "w") as fifo:
        messages = ["Message 1", "Message 2", "Message 3"]
        for message in messages:
            fifo.write(message + "\n")
            fifo.flush()
            print(f"Writer sent: {message}")

def reader_process():
    # 打开命名管道进行读操作
    with open(fifo_path, "r") as fifo:
        while True:
            data = fifo.readline().rstrip()
            if not data:
                break
            print(f"Reader received: {data}")

if __name__ == "__main__":
    # 创建命名管道
    os.mkfifo(fifo_path)
    # 创建写进程
    writer_pid = os.fork()
    if writer_pid > 0:
        # 在父进程中创建读进程
        reader_process()
    elif writer_pid == 0:
        # 在子进程中创建写进程
        writer_process()
    else:
        print("Fork failed.")


在这个例子中,父进程创建了一个读进程和一个写进程,它们之间通过命名管道进行通信。
请注意,这个例子中使用了`os.fork()`创建进程,但在实际应用中,你可能会使用更高级的模块,
如`multiprocessing`,以便更方便地实现进程间通信。

1.2 消息队列

在Python中,可以使用 `multiprocessing` 模块中的 `Queue` 类实现消息队列。

以下是一个简单的例子,演示如何使用消息队列进行不同进程间的通信:
from multiprocessing import Process, Queue

def producer(queue):
    messages = ["Message 1", "Message 2", "Message 3"]
    for message in messages:
        # 将消息放入队列
        queue.put(message)
        print(f"Producer put: {message}")

def consumer(queue):
    while True:
        # 从队列中获取消息
        message = queue.get()
        if message == "STOP":
            break
        print(f"Consumer got: {message}")

if __name__ == "__main__":
    # 创建消息队列
    message_queue = Queue()
    # 创建生产者进程
    producer_process = Process(target=producer, args=(message_queue,))
    # 创建消费者进程
    consumer_process = Process(target=consumer, args=(message_queue,))
    # 启动进程
    producer_process.start()
    consumer_process.start()
    # 等待生产者生产完消息
    producer_process.join()
    # 向队列发送停止信号
    message_queue.put("STOP")
    # 等待消费者处理完消息
    consumer_process.join()

在这个例子中,`producer` 函数负责向队列中放入消息,而 `consumer` 函数负责从队列中取出消息。
通过 `multiprocessing.Queue` 实现了两个进程之间的通信。
请注意,这只是一个基本的示例,实际应用中可能需要更复杂的逻辑来处理不同进程之间的通信。

1.3 共享内存

在Python中,可以使用 `multiprocessing` 模块中的 `Value` 或 `Array` 来创建共享内存。

以下是一个简单的例子,演示如何使用共享内存实现多个进程之间的数据共享:
from multiprocessing import Process, Value, Array

def writer(shared_value, shared_array):
    # 在共享内存中写入数据
    shared_value.value = 10
    for i in range(len(shared_array)):
        shared_array[i] = i * 2

def reader(shared_value, shared_array):
    # 从共享内存中读取数据
    print(f"Shared Value: {shared_value.value}")
    print(f"Shared Array: {list(shared_array)}")

if __name__ == "__main__":
    # 创建共享内存
    shared_value = Value("i", 0)  # 'i'表示整数类型
    shared_array = Array("i", range(5))  # 'i'表示整数类型,初始值为0, 2, 4, 6, 8
    # 创建写进程
    writer_process = Process(target=writer, args=(shared_value, shared_array))
    # 创建读进程
    reader_process = Process(target=reader, args=(shared_value, shared_array))
    # 启动进程
    writer_process.start()
    writer_process.join()  # 等待写进程结束
    
    reader_process.start()
    reader_process.join()  # 等待读进程结束


在这个例子中,`Value` 用于创建一个共享的整数,`Array` 用于创建一个共享的数组。
两个进程通过这两个共享内存对象进行数据的读写操作。请注意,共享内存的使用需要注意同步问题,
以确保数据的一致性。在实际应用中,可能需要使用 `multiprocessing.Lock` 来进行进程间的同步。

1.4 信号

在Python中,可以使用 `signal` 模块来处理信号。

以下是一个简单的例子,演示如何使用信号通知一个进程发生某个事件:
import os
import signal
import time

def signal_handler(signum, frame):
    print(f"Received signal {signum}. Event occurred!")

def main_process():
    # 注册信号处理函数
    signal.signal(signal.SIGUSR1, signal_handler)
    print(f"Main process (PID {os.getpid()}) is waiting for the event.")
    while True:
        time.sleep(1)

if __name__ == "__main__":
    # 创建子进程
    pid = os.fork()
    if pid > 0:
        # 在父进程中
        print(f"Parent process (PID {os.getpid()}) created child process (PID {pid}).")
        time.sleep(2)  # 等待子进程注册信号处理函数
        # 向子进程发送自定义信号(SIGUSR1)
        os.kill(pid, signal.SIGUSR1)
        os.waitpid(pid, 0)  # 等待子进程结束
    elif pid == 0:
        # 在子进程中
        main_process()
    else:
        print("Fork failed.")

在这个例子中,父进程创建了一个子进程,然后通过 `os.kill()` 向子进程发送 `SIGUSR1` 信号,
用于通知子进程发生了某个事件。子进程通过 `signal.signal()` 注册了一个信号处理函数 
`signal_handler`,当收到 `SIGUSR1` 信号时,会执行这个处理函数。
这样,子进程就能够在接收到信号时执行相应的操作。

1.5 套接字

在Python中,可以使用 `socket` 模块实现套接字通信。

以下是一个简单的例子,演示如何使用套接字在同一台机器上的两个进程之间进行通信:
import socket
import os

def server():
    # 创建套接字
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 绑定地址和端口
    server_socket.bind(('127.0.0.1', 8888))
    # 监听连接
    server_socket.listen(1)
    print("Server is waiting for connection...")
    # 接受连接
    client_socket, client_address = server_socket.accept()
    print(f"Connected by {client_address}")
    # 接收数据
    data = client_socket.recv(1024)
    print(f"Received data: {data.decode()}")
    # 关闭连接
    client_socket.close()
    server_socket.close()

def client():
    # 创建套接字
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 连接服务器
    client_socket.connect(('127.0.0.1', 8888))
    print("Connected to the server.")
    # 发送数据
    message = "Hello, server!"
    client_socket.sendall(message.encode())
    print(f"Sent data: {message}")
    # 关闭连接
    client_socket.close()

if __name__ == "__main__":
    # 创建子进程
    pid = os.fork()
    if pid > 0:
        # 在父进程中作为服务器
        server()
    elif pid == 0:
        # 在子进程中作为客户端
        client()
    else:
        print("Fork failed.")


在这个例子中,父进程作为服务器,子进程作为客户端,它们通过套接字在同一台机器上进行通信。
请注意,在实际应用中,可能需要更复杂的逻辑来处理套接字通信,例如异常处理、数据传输协议等。
此外,Python 3 中的 `socket` 模块提供了更多高级的功能和选项,可以根据实际需求进行定制。

1.6 文件锁

在Python中,可以使用 `fcntl` 模块来实现文件锁。

以下是一个简单的例子,演示如何使用文件锁进行两个进程之间的通信:
import fcntl
import os
import time

lock_file_path = "lock_file.txt"

def process_with_lock():
    with open(lock_file_path, "w") as lock_file:
        try:
            # 尝试获取文件锁
            fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
            print(f"Process {os.getpid()} acquired the lock.")
            # 模拟进程执行任务
            time.sleep(5)
        except BlockingIOError:
            # 获取文件锁失败
            print(f"Process {os.getpid()} couldn't acquire the lock. Another process is holding the lock.")
        finally:
            # 释放文件锁
            fcntl.flock(lock_file, fcntl.LOCK_UN)
            print(f"Process {os.getpid()} released the lock.")

def main():
    # 创建子进程
    pid = os.fork()
    if pid > 0:
        # 在父进程中
        process_with_lock()
        os.waitpid(pid, 0)  # 等待子进程结束
    elif pid == 0:
        # 在子进程中
        process_with_lock()
    else:
        print("Fork failed.")

if __name__ == "__main__":
    main()


在这个例子中,父进程和子进程都尝试获取同一个文件的文件锁。如果一个进程成功获取锁,
那么它可以执行一些任务,然后释放锁。如果另一个进程在尝试获取锁时发现锁已经被持有,
那么它会得到一个 `BlockingIOError` 异常,表示获取锁失败。

请注意,使用文件锁时要确保所有进程都遵守相同的锁协议,以防止死锁等问题。此外,文件锁通常
是在同一台机器上的进程间通信的一种简单而有效的方法,但在分布式环境中可能需要考虑其他机制。

2 正向代理,反向代理

代理其实就是一个中介,A和B本来可以直连,中间插入一个C,C就是中介就是代理

-正向代理:代理的是客户端   VPN  爬虫代理池
-反向代理:代理的是服务端   nginx

#看一下:https://blog.csdn.net/lsc_2019/article/details/124630025

正向代理(Forward Proxy)和反向代理(Reverse Proxy)是两种代理服务器的工作模式,
它们在网络中有不同的应用场景和功能。

### 正向代理(Forward Proxy):
1. **工作原理:**
   - 在正向代理中,客户端通过代理服务器来访问互联网资源。
   	客户端向代理发送请求,代理服务器代表客户端去获取资源,并将资源返回给客户端。

2. **应用场景:**
   - **访问控制:** 用于控制对特定资源的访问,可以实现访问过滤和访问授权。
   - **匿名访问:** 用户可以通过正向代理来隐藏其真实的网络标识,实现匿名访问。
   - **加速访问:** 通过缓存等机制,提高对特定资源的访问速度。

3. **示例:**
   - 用户通过公司的正向代理访问互联网,公司的代理服务器代表用户去获取互联网资源。

### 反向代理(Reverse Proxy):
1. **工作原理:**
   - 在反向代理中,客户端向代理服务器发送请求,而代理服务器会代表服务端来响应客户端的请求。
   	客户端并不直接与目标服务器通信,而是与代理服务器通信。

2. **应用场景:**
   - **负载均衡:** 可以将请求分发到多个后端服务器,以实现负载均衡,提高系统性能和可靠性。
   - **安全性:** 可以隐藏真实的服务器信息,提高系统的安全性,防止直接暴露后端服务器的细节。
   - **SSL终结:** 可以在代理服务器上进行SSL终结,减轻后端服务器的负担。

3. **示例:**
   - 客户端通过公司的反向代理访问内部应用,反向代理服务器根据负载均衡策略将请求转发到多个内部服务器。

总体而言,正向代理位于客户端和互联网之间,代表客户端访问资源;而反向代理位于服务端和互联网之间,
代表服务端对外提供服务。这两种代理模式在实际应用中都有重要作用,用于提高安全性、性能和可维护性。

3 什么是粘包

-tcp协议的现象
-因为TCP是流式协议,tcp客户端发送的多个数据包就会像水流一样流向TCP服务端,
 多个数据包就会'粘'在一起,区分不开是几个数据包,造成了粘包现象
    -1 每个包设置结束标志    http协议采用这种  /r/n/r/n
    -2 每个包设置固定大小的头,头中包含包的大小 

粘包(Packet Stitching)是在计算机网络通信中常见的一种现象,
指的是发送方发送的数据包被接收方一次性接收,而不是按照发送方发送的时候分包接收。
这可能导致接收方难以区分不同的数据包边界,从而引发数据解析错误。

粘包的发生通常涉及到数据的发送和接收速率不一致、TCP协议的特性以及底层网络设备的缓冲等因素。

在TCP协议中,数据是以字节流的形式传输的,没有明显的分包边界。当发送方迅速发送多个小数据包时,
接收方可能会将这些小数据包合并成一个更大的数据块,这就形成了粘包。

应对粘包的常见方法包括:
1. **消息长度标识:**
   - 在消息的开头添加消息长度的标识,接收方根据标识来划分消息的边界。

2. **固定长度消息:**
   - 规定每个消息的固定长度,接收方按照固定长度来解析消息。

3. **消息分隔符:**
   - 在消息的结束处添加特殊的分隔符,接收方根据分隔符来划分消息。

4. **使用消息头部信息:**
   - 在消息头部包含额外的信息,如消息类型、消息长度等,接收方根据头部信息来解析消息。

5. **流水号或消息序列号:**
   - 在消息中加入序列号,接收方根据序列号来判断消息的顺序和边界。

选择哪种方法取决于具体的应用场景和需求。在实际应用中,消息长度标识和消息分隔符比较常见。
注意在实现时,需要保证发送方和接收方的协议一致,以确保正确解析数据。
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值