Python进程间网络远程通讯方式:socket、pipe、RPC详解!

背景

最近在进行开发工作的时候,遇到了一个场景:

pc程序需要和安卓设备进行通讯和接口调用。

此时就需要进行远程调用方法。然而大学时代有关于远程过程调用的知识都还给了老师……所以在此进行一个复习,并进行实战演练!

网络远程过程调用

三种方式说明:

  • Socket:主要用于网络通信,它允许不同计算机上的进程通过网络进行数据交换。Socket提供了一个端到端的通信机制,无论是在同一台机器上还是跨网络的不同机器上。
  • Pipe:主要用于同一台机器上的进程间通信(IPC),它实现了一种简单的数据流机制,允许一个进程的输出直接作为另一个进程的输入。Pipe是单向的,数据只能从一个方向流动。
  • RPC:是一种远程过程调用的协议,它允许一个程序通过网络调用另一个地址空间(通常是另一台机器)上的过程(或函数),就像调用本地过程一样。RPC隐藏了网络通信的底层细节,使得远程调用看起来像本地调用一样简单。

相互关系说明:

  • socket和pipe:Socket主要用于跨设备场景,当然也可以用于实现同一台机器上的进程间通信,但是对于同一设备的进程通讯,Pipe更为常见和高效。
  • Socket和RPC:Socket是RPC实现中常用的底层通信机制之一。在RPC框架中,客户端和服务器之间的网络通信通常是通过Socket来完成的。Socket提供了数据传输的通道,而RPC则在这个通道上构建了一个更高层次的调用接口。

方式一:pipe

原理详解:

  • Pipe是一种在Unix和类Unix系统中常用的进程间通信机制。它通过创建一个管道文件来实现数据的单向流动。
  • 当一个进程创建了一个管道时,它会得到两个文件描述符:一个用于写入(写端),另一个用于读取(读端)。写入管道的数据会被存储在内核的缓冲区中,直到被另一个进程读取
  • Pipe的通信是同步的,即写进程在写入数据后会被阻塞,直到读进程读取了数据;同样,读进程在读取数据前也会被阻塞,直到写进程写入了数据

代码示例:

在Windows系统中,Python的multiprocessing模块提供了与Unix系统类似的管道(Pipe)功能,用于进程间通信(IPC)。

from multiprocessing import Process, Pipe  
  
def sender(conn):  
    conn.send("Hello from sender!")  
    conn.close()  
  
def receiver(conn):  
    print("Receiving...")  
    while True:  
        try:  
            data = conn.recv()  
            print(f"Received: {data}")  
        except EOFError:  
            print("No more data. Exiting.")  
            break  
    conn.close()  
  
if __name__ == '__main__':  
    # 创建一个管道  
    parent_conn, child_conn = Pipe()  
  
    # 创建子进程  
    p1 = Process(target=sender, args=(child_conn,))  
    p2 = Process(target=receiver, args=(parent_conn,))  
  
    # 启动子进程  
    p1.start()  
    p2.start()  
  
    # 等待子进程完成  
    p1.join()  
    p2.join()  
  

注意:在Windows上,如果接收者进程在发送者进程之后退出,可能会导致发送者进程中的管道连接在关闭时出现问题。  

方式二:socket

原理详解:

  • Socket是计算机网络编程中的一种抽象,它提供了在网络上进行通信的接口。Socket本质上是一种通信的端点,它在网络上标识了一个通信链路的两端,并提供了通信双方所需的接口和功能。
  • 在TCP/IP协议栈中,Socket位于传输层和应用层之间,它使用传输层提供的服务(如TCP或UDP)来实现网络通信。
  • TCP Socket基于TCP协议,提供可靠的、有序的数据传输服务。它通过三次握手建立连接,确保数据的可靠性和顺序性。
  • UDP Socket基于UDP协议,提供简单的数据传输服务,但不保证数据的可靠性和顺序性。它适用于一些实时性要求高、允许一定数据丢失的应用场景。

代码示例:

服务端:
import socket  
  
def tcp_server(host='127.0.0.1', port=12345):  
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
    server_socket.bind((host, port))  
    server_socket.listen(5)  # 最多可以挂起5个连接  
  
    print(f"Server is listening on {host}:{port}")  
  
    while True:  
        client_socket, addr = server_socket.accept()  
        print(f"Connected by {addr}")  
  
        try:  
            while True:  
                data = client_socket.recv(1024)  
                if not data:  
                    break  
                print(f"Received: {data.decode()}")  
                client_socket.sendall(data)  # Echo back the data  
        finally:  
            client_socket.close()  
  
if __name__ == '__main__':  
    tcp_server()
客户端:
import socket  
  
def tcp_client(host='127.0.0.1', port=12345):  
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
    client_socket.connect((host, port))  
  
    try:  
        while True:  
            message = input("Enter message: ")  
            if message == 'quit':  
                break  
            client_socket.sendall(message.encode())  
            data = client_socket.recv(1024)  
            print(f"Received: {data.decode()}")  
    finally:  
        client_socket.close()  
  
if __name__ == '__main__':  
    tcp_client()

方式三:RPC

原理详解:

  • RPC是一种远程过程调用的协议,它允许程序通过网络调用远程地址空间上的过程。RPC隐藏了网络通信的底层细节,使得远程调用看起来像本地调用一样简单。
  • RPC的实现通常包括客户端和服务器两部分。客户端负责发起远程调用请求,并接收服务器返回的调用结果;服务器则负责接收客户端的请求,执行相应的过程,并将结果返回给客户端。
  • RPC框架通常会在客户端和服务器之间建立一条或多条Socket连接,用于传输远程调用的请求和响应。这些Socket连接可以是持久的(长连接),也可以是临时的(短连接)。
  • RPC框架还需要处理一些额外的任务,如参数和结果的序列化与反序列化、网络异常的处理、服务调用的负载均衡等。这些任务通常是由RPC框架本身来完成的,以减轻应用程序的负担。

 

代码示例:

1. .proto 文件(helloworld.proto

这个文件定义了gRPC服务的接口,使用Protocol Buffers语法。

// 使用proto3语法  
syntax = "proto3";  
  
// 定义包名,防止命名冲突  
package helloworld;  
  
// 定义Greeter服务  
service Greeter {  
  // 定义一个RPC方法SayHello,它接收HelloRequest并返回HelloReply  
  rpc SayHello (HelloRequest) returns (HelloReply) {}  
}  
  
// 定义HelloRequest消息,包含一个string类型的name字段  
message HelloRequest {  
  string name = 1; // 字段编号为1  
}  
  
// 定义HelloReply消息,包含一个string类型的message字段  
message HelloReply {  
  string message = 1; // 字段编号为1  
}
2. Java 服务端实现

这里假设你已经使用protoc编译器和gRPC Java插件生成了GreeterGrpc.javaHelloworld.java等自动生成的代码。

import io.grpc.Server;  
import io.grpc.ServerBuilder;  
import io.grpc.stub.StreamObserver;  
import helloworld.GreeterGrpc;  
import helloworld.GreeterOuterClass;  
  
// 实现GreeterGrpc.GreeterImplBase,提供SayHello方法的具体实现  
public class HelloWorldServer {  
  static class HelloWorldImpl extends GreeterGrpc.GreeterImplBase {  
    @Override  
    public void sayHello(GreeterOuterClass.HelloRequest req, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {  
      // 构造回复消息  
      GreeterOuterClass.HelloReply reply = GreeterOuterClass.HelloReply.newBuilder()  
          .setMessage("Hello " + req.getName())  
          .build();  
      // 发送回复并标记RPC调用完成  
      responseObserver.onNext(reply);  
      responseObserver.onCompleted();  
    }  
  }  
  
  public static void main(String[] args) throws Exception {  
    // 在指定端口上创建并启动gRPC服务器  
    Server server = ServerBuilder.forPort(50051)  
        .addService(new HelloWorldImpl())  
        .build()  
        .start();  
    System.out.println("Server started, listening on 50051");  
    // 等待服务器终止(通常是通过某种方式发送的关闭信号)  
    server.awaitTermination();  
  }  
}
3. Python 客户端实现

这里假设你已经使用protoc编译器和gRPC Python插件生成了__init__.pyhelloworld_pb2.pyhelloworld_pb2_grpc.py等自动生成的代码。

import grpc  
  
import helloworld_pb2  
import helloworld_pb2_grpc  
  
def run():  
    # 创建一个不安全的通道连接到服务器  
    with grpc.insecure_channel('localhost:50051') as channel:  
        # 创建Greeter服务的存根(stub)  
        stub = helloworld_pb2_grpc.GreeterStub(channel)  
        # 构造请求消息  
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))  
        # 打印响应消息  
        print("Greeter client received: " + response.message)  
  
if __name__ == '__main__':  
    run()

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

千天夜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值