Socket or RPC 网络编程方法对比
Socket编程
流程
- 服务端和客户端初始化socket,得到文件描述符;
- 服务端调用bind,将socket绑定在指定的IP地址和端口;
- 服务端调用listen,进行监听;
- 服务端调用accept,等待客户端连接;
- 客户端调用connect,向服务端的地址和端口发起连接请求;
- 服务端accept返回用于传输的socket的文件描述符;
- 客户端调用writer写入数据;服务端调用read读取数据;
- 客户端断开连接时,调用close,服务端read读取数据,读取到EOF,待处理完数据后,服务端调用close,表示连接关闭。
服务端建立连接
服务端首先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。
socket()->bind()->listen()->accept()
客户端建立连接
客户端首先初始化socket,然后与服务端连接,服务端监听成功则连接建立完成
socket()->connect()
常见问题
connect,accept发生在三次握手的哪一步?
客户端connect成功返回在第二次握手,服务端accept成功返回时在三次握手成功之后。
没有accept,能建立TCP连接吗?
可以,accept()系统调用并不参与TCP三次握手的过程,执行accept()只是为了从全连接队列中取出一条连接。
没有listen,能建立TCP连接吗?
如果服务端只bind了ip和port,没有调用listen的话,客户端对服务端发起连接建立请求,服务端会回RST报文,无法建立TCP连接。
如果两个客户端同时向对方发出请求建立连接,或者客户端自连接,可以建立TCO连接。
C++ Demo
Server
1 |
|
Client
1 |
|
RPC
RPC(Remote Procedure Call)远程调用协议,目标是让远程调用服务更加简单、透明。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。
grpc
Google发布的开源RPC框架,是基于HTTP2.0协议的,目前已经成为最主流的RPC框架之一。
特点
- 跨语言使用,支持C++、Java、Go、Python等编程语言。
- 基于IDL文件定义服务,通过proto3工具生成指定语言的数据结构、服务端接口以及客户端Stub。
- 通信协议基于标准的HTTP/2设计,支持双向流、消息头压缩、单TCP的多路复用、服务端推送等特性。
- 序列化支持Protocol Buffer和JSON。
- 安装简单,扩展方便。
grpc的四种服务方法
- 简单RPC:客户端向服务器发送请求并等待响应返回,类似于正常的函数调用。
1
rpc GetFeature(Point) returns (Feature) {}
- 客户端流式RPC:客户端向服务器发送请求并获取流以读回一系列消息。客户端从返回的流中读取,直到没有更多消息为止。
- 服务端流式RPC:客户端写入一系列消息并将它们发送到服务器,同样使用提供的流。一旦客户端完成消息写入,它就会等待服务器读取所有消息并返回响应。
- 双端流式RPC:双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照它们喜欢的任何顺序读取和写入。
C++ GRPC Demo
helloworld.proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
/*
定义Greeter服务和SayHello方法
SayHello接收HelloRequest参数,返回HelloReply结果
*/
service Greeter {
rpc SayHello (HelloRequest) return (HelloReply) {}
}
/*定义HelloRequest消息类型*/
message HelloRequest {
string name = 1;
}
/*定义HelloReply消息类型*/
message HelloReply {
string message = 1;
}Server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class GreeterServiceImpl final : public Greeter:Service
{
Status SayHello(ServerContext* context, const HelloRequest* request,HelloReply* reply) override
{
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
// 启动GRPC的默认健康检查服务
grpc::EnableDefaultHealthCheckService(true);
// 注册反射机制
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
std::string server_address("127.0.0.1:50051");
// 创建GreeterServiceImpl实例和ServerBuilder实例
GreeterServiceImpl service;
ServerBuilder builder;
// 监听端口,使用grpc::InsecureServerCredentials()进行非安全通信
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// 将实例注册为可处理请求的服务
builder.RegisterService(&service);
// 组装并启动服务器
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// 等待和关闭
server->Wait();
}Server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel) : stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user)
{
// 创建一个HelloRequest对象request,并设置name值
HelloRequest request;
request.set_name(user);
// 存储Server返回值
HelloReply reply;
// 客户端上下文
ClientContext context;
// 执行RPC
Status status = stub_->SayHello(&context, request, &reply);
// 返回值处理
if (status.ok()) {
return reply.message();
} else {
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
void RunClient() {
std::string server_address = "127.0.0.1:50051";
/*
实例化客户端,需要传递一个channel,用于创建RPC
grpc::InsecureChannelCredentials()表示未经身份验证
*/
Client greeter(
grpc::CreateChannel(server_address, grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
}
为什么有HTTP还要使用RPC?
- HTTP是协议,RPC是框架或者说调用方式。
- 二者的服务发现存在区别。
- HTTP基于TCP,TCP发送的数据包包括header和body,内容冗余;RPC定制化程度更高,性能更好。
参考文章
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Writer-X!