开篇介绍:
============================================================================================================
在这篇文章中主要介绍的是Protocol的使用方法,和步骤 。
在后一篇文章中将会介绍的是Thrift的使用方法,和步骤。
在第三篇文章中将会介绍的是 Protocol 和 Thrift 在实现同一个类和其中相同的数据类型的时候,性能的对比分析,在这里LZ并没有对其中所支持的
数据类型或是变量的表示方法进行对比,这些信息LZ会给出官方权威的相关连接,供大家查阅。
LZ对比Protocol和Thrift的性能主要分为以下的几点:
1. 压缩/解压同一个大型的数组变量所消耗的时间
2.压缩对象之后所占空间和压缩之前的压缩比
3.对于同一个数据对象(即占用同样大小空间的对象) 使用同样的定义方法,即对于相同的形式说明书,分别使用protobuf 和 thrift 两种中间语言
对其进行描述生成的一套完整的代码,对其中的同一个数组这个数据对象进行传输,分别需要消耗的时间进行对比。
4.对比分析,在一种形式说明书中分别生成两种( protobuf , thrift ) 的中间代码,分别编写Server 端实现代码和 Client端实现代码, 还有相应接口
定义代码和 接口实现代码的不同,对比哪一个实现步骤更加的清晰。
==============================================================================================
本文将主要分为三个部分:
第一部分: 介绍protobuf 的主要功能和使用安装方法
第二部分: 介绍protobuf 形式说明书中代码定义方法以及如何通过protobuf的中间文件 *.proto 生成java 代码
第三部分: 介绍protobuf形式说明书中定义的方法在生成的java代码中的对应关系,以及在何处编写Client端和Server端,Interface和Implementation
代码的实现。
-----------------------------------------------------------let 's begin ~ --------------------------------------------------------------------------------------------
第一部分:
Protobuf 是Protocol Buffer的缩写,简称是PB,是google公司开发的一种数据交换格式,是一种独立于语言,独立于平台,在各种语言中处于中立地位的一种开发语言。
google针对protobuf 的开发提供了三种常用的语言实现: Java, C++ 和 Python, 每一种语言的实现中都包含了相关语言的编译器及它的库文件。
由于Protobuf是一种二进制的格式来进行数据表示和传输,所以比使用XML文件来进行数据交换要快得多。
以此优点而常被用在分布式系统设计中担任系统中多个节点之间的数据传输工作。也常常用于异构的环境下进行数据的交换以及RPC底层框架的搭建工作。
Protobuf作为一种效率和兼容性很优秀的二进制数据传输格式,广泛的使用在例如网络传输、文件配置、数据存储等等相关诸多领域。
PB源代码分层结构大致如下所示:
PB 源码
{
PB基础库
{
Message抽象层
Descriptor抽象层
IO子系统
}//PB基础库
PB编译器---源码生成器
}//PB源码
其中的IO子系统是最为简单的,它与其他系统部分没有依赖关系,实现了统一的输入输出接口。
Message抽象层: 对于Message抽象层主要由一个抽象的Message抽象类和从Message类里面单独分离出来的Reflection类构成。
GeneratedMessageReflection是Reflection的派生类, 它实现了抽象Message的方法,这里的关键之处在于一个静态的offsets的数组
,在这个数组里面存储了任何Message派生类对象里面的各个字段相对于对象本身的偏移量。通过偏移量的指示,GeneratedMessageReflection可以在不知道确切名字
和类型的情况下实现Reflection里面所定义的Message的方法。
Descriptor抽象层: 对于Descriptor抽象层是整个系统的核心,它是由平行的两组解释器组成的。一组是一系列递归下降的Descriptor类群
,另一组是一系列递归下降的DescriptorProto的类群。Descriptor类群描述的是抽象的任意的消息。
DescriptorProto类型是对消息格式本身进行描述的消息,其中FileDescriptorProto类群描述了 .proto 文件的结构,任何.proto 文件都是可以映射到一个FileDescriptorProto 对象上的。
FileDescriptor对象与FileDescriptorProto 对象之间是相互转换的。
好了,理论简单介绍了一下,下面我们来看一下如何使用:
首先,如果想要通过 编译.proto 文件之后生成代码的话,是需要有 protobuf的编译程序的,下面是官网地址:
Protocol-buffer 包下载地址
当然,在这里需要知道的是,在官方提供的protobuf 包中仅仅支持的是对数据进行序列化和反序列化的方式,如果想要构架一个RPC底层框架,
这些还是不够的,我们还需要可以提供RPC通信的软件包:protobuf-socket-rpc 才可以,下面是它的下载地址:
protobuf-socket-rpc
对于,protobuf 包下载之后,根据不同的版本使用方法也是不同的,目前总共有两种版本,一个是用于windowns下面的版本,是以zip格式压缩的,
下载之后,直接解压缩既可以使用里面的exe 文件了。
另一个版本是,tar.gz格式的,需要自己对其进行编译之后才可以使用,对于如何编译不是本篇文章的重点,所以在这里不对其进行介绍了。
在下载成功之后,我们可以试着写一个.proto 文件,来对其进行编译一下。
在这里仅仅举一个简单的例子,比如说我像要生成一个 cat类,和一个 fish 类,在类cat中定义一个eat的方法, eat所传输的参数是fish类的对象。
定义的文件包名称为 animal , 对外显示的类名称为 myCat ,且使用的语言是 Java。
package animal ;
option java_package = "com.animal" ;
option java_outer_classname = "CatProtos" ;
option java_generic_services = true ;
message Fish
{
required string fishName =1 ;
required int32 fishNum = 2 ;
}
message Cat
{
required string name = 1 ;
required int32 catAge =2 ;
repeated Fish fish =3 ;
}
service CatService
{
rpc EatFish ( Fish ) returns ( Fish ) ;
}
定义好文件之后,将其扩展名称修改为 .proto 文件格式,然后打开"命令提示符" 窗口, 通常的做法就是,将你所编写的 .proto 文件先存放到 你的protobuf exe的同一个目录下面,
如下面的截图所示:
上面代码文件的名称是test3.proto ,在这里可以肯定的是,文件的名称与你所需要生成的类的名称是没有关系的,
之所以将代码文件存放在,或是临时的存放在protoc.exe 所在的同一个路径的下面,是因为这样在命令行中输入无需指定各自所在的不同文件的麻烦。
编译proto 文件生成 java 文件 的命令格式是:
protoc = --java_out = . test3.proto
protoc = --java_out = . test3.proto 这个命令总共分为三个部分,
第一部分是 --java_out 这个参数指定的是,该proto代码文件中或许会定义多个 生成文件,
就是这个形式说明书中生成的代码可以 有 java , c++ 或是 Python , 但是其中 java 代码的路径是由 java_out 这个参数所指定的。
第二部分是 " . " 这个点,代表的意思就是 生成java 文件(c++ / python ) 所在的目录路径是当前的目录下面。
第三部分, 而后面的 test3.proto 所指定的是所需要编译的形式说明书是哪个。
其中, 在编译 java 代码的时候是需要导入相应的 jar 数据包的,
一个是 com.google.protobuf 的数据包下载地址:
com.google.protobuf jar 文件包下载处
另一个是 protobuf-rpc 的数据包下载地址:
protobuf-socket-rpc , 即刚刚已经介绍过了。
<-----
其中,形式说明书是用来在分布式系统中描述用来描述服务器端和客户端交互
的方法的格式的其中包括方法名称以及其中传递的参数以及返回值的类型等等, 编译器根据形式说明书中所描绘的格式生成各自属于客户端和服务器端的程序段,其中这个程序段通常被称作是 客户存根和服务器存根,在通信的过程中根据本地的程序调用会触发到存根,存根会将远程发送的数据传递到系统的内核中,内核会将其发送到远程机器上。
------>
通过上述的编译命令,就可以在相应的目录下面找到一个 com.animal 的文件夹了, 在文件夹下面静静躺着一个名为 CatProtos.java 的java 文件,其中的类的结构如下图:
---------------------------------------------------------section 1 end --------------------------------------------------------------------------------------------
----------------------------------------------------------section 2 --------------------------------------------------------------------------------------------------
好了,现在我们来看一下第二部分,在这一部分中,将会实现一个复杂一些的Protobuf 的proto 文件,然后详细介绍一下该proto 文件中的各个字段的用途和生成java 文件之后对应的java 类中的什么部分。
由于LZ主要想要测试Protobuf 和Thrift 二者在数据传输方面的性能,和数据压缩比,数据压缩速度的性能,所以LZ决定在proto文件中定义的数据对象类型和所描述的方法将和这个目标有关。
首先测试的就是 protobuf 的传输数据的速率,可以在文件中定义一个大型的数组,然后定义一个 sender 的service 来实现 传入参数和传出参数对应的都是该数组对应的message类型,
然后在客户端调用这个方法的时候,在客户端调用方法之间,启动计时器方法, 然后调用该sender 方法,将固定大小的数组对应的数据块作为方法调用的参数传入sender 这个方法中,
同样这个sender 方法的返回值仅仅就是将传入的参数进行返回。 但是与本地调用不同,sender 的实现代码是不在本地机器上的,如果实在同一台机器上的话,虽然是在同一台机器上,
但是并不是在同一个进程地址空间内,这样的话,消息会发送出去然后经过一个网络回路再次到达本台机器的服务器的进程上, 服务器进程调用本地sender方法实体对其进行返回,
返回的数据被客户端接收到之后,此次远程访问 即 RPC访问调用结束,计时器记录时间, 这样两次时间之差即为数据通过Protobuf 传递得到的时间。
根据上面的描述,我决定将 proto 文件定义为:
package testProtobuf ;
option java_package = "com.test.protobuf" ;
option java_outer_classname = "ProtobufTest" ;
option java_generic_services = true ;
//first we define the array as the object transmitting data
message Data
{
required string data1 = 1;
required string data2 = 2;
required string data3 = 3;
required string data4 = 4;
required string data5 = 5 ;
required string data6 = 6;
required int32 length =7;
}
message RequestBlock
{
repeated Data dataBlock =1 ;
}
message ResponseBlock
{
repeated Data dataBlock = 1 ;
}
service MyService
{
rpc sender( RequestBlock ) returns ( ResponseBlock ) ;
}
1. package testProtobuf ; 这个语句是用来说明,在编译该proto文件之后所生成的文件将会存放到哪一个文件夹的下面
2. option java_package ="com.test.protobuf"; 这条语句是用来说明,生成文件所在项目中的文件包层次是如何分布的。
3. option java_outer_classname = "ProtobufTest" ; 这条语句的作用是为了说明,根据该proto文件生成的java 代码中类的名称是什么,
这也就是LZ在前面所说的, 生成的类名与proto文件的名称是没有关系的,而是与里面命令java_outer_classname 之后所定义的名称有关。
4. option java_generic_service = true ; 这句话是用来说明,在这个 proto文件中所定义的service 在相关的类中会有对应的 方法生成,而对于
protoc ( protobuf compiler) 来说,默认的是false 即 ,即便在proto文件中对service 进行声明但是在生成的代码中仍然不会产生service所描述
的相关方法的代码。 所以如果想要使用protobuf 的生成方法的这一功能的话,需要将对应字段的值置为 true .
}
这一段代码的功能很简单,即定义一个数据对象,该数据对象共有7 个字段,LZ想让该数据对象用来作为在client端和server端互相传递数据的一个
信息的载体,但是在protobuf中并没有支持大型数据传输的(或者是暂时还没有找到)数据结构,所以暂时先使用这种表示方法。在Data所要描述
的数据类型中,前6个字段是用来存放所要在client端和server端传递的数据,而最后一个变量是用来对其中数据量大小的记载。
在这里需要说明的就是,在protobuf 的字段修饰符中有: { required , optional , repeated } , 其中required是静态分配的空间,也就是在编译的时候
就已经在空间中开辟好的地址空间, 而optional则是为一个变量是否分配相应的空间取决于发送请求的一方是否发送了该变量,如果发送了该变量,
则在接收方为其开辟相应的空间对其进行接收,如果发送方没有发送该变量,则在接收端(往往是服务器端)不为其开辟空间存放变量。
repeated 类似于我们常使用的struct,就LZ目前的使用情况可知,LZ仅仅在一个messageA 中将另一个messageC 作为messageA的一个成员
变量定义在其中的时候,才会使用repeated 的关键词来对其进行修饰,如下面的定义可以看出, Data 是自定义的message 类型的, 那么将其以成员
变量的方式定义于RequestBlock 和ResponseBlock这两个 message之中的时候时 , 就要在Data 的前面使用repeated 进行修饰。 当然更加官方
一点的说法就是, 使用repeated修饰的对象的空间划分是动态分配的,其实这么理解也是没有错误的,即在proto被编译之前,是不可能预先知道
Data对应的字段将会存放需要空间多大的数据或是信息,一切都是需要等到对其进行编译的时候才会知道,所以是动态分配的。
message RequestBlock
{
repeated Data dataBlock =1 ;
}
message ResponseBlock
{
repeated Data dataBlock = 1 ;
}
上面两端代码分别定义的是,在service中定义的远程调用方法中所传递参数的类型,在其message中的对象也是message 的类型,故使用
repeated来对其进行修饰。
service MyService
{
rpc sender ( RequestBlock) returns ( ResponseBlock ) ;
}
上面的这段代码用于定义一个远程调用方法中的传递参数类型: RequestBlock 类型的, 返回值类型 ResponseBlock 类型的。
其中,这个service 中所描述的方法,将会在生成代码中的Server 代码段中, Client代码段中,以及接口定义和接口实现中都有所表现,
大致的使用过程是这样的:
虽然在proto文件中对该远程调用方法给予了描述,但是这仅仅是一个Client与Server 端所达成的一个通信的接口标准,其中实现方法的实体在
这里,或是在自动生成的代码中是不存在的,是需要用户根据自己的需要来对其进行实现的。
在实现的过程中,用户需要在接口实现方法中 即 Impl 相关类中给出该接口的实现的代码表述,然后在Server 端的Server启动,注册,加载的某一个
部分将改Impl方法以参数的方式传入其中。等到Server端启动之后,就会作为服务器为远程访问的Client端提供传入接口的实现方法实体的Impl该
方法的远程调用了,在Client端只要根据接口协议中的协议描述语句传入参数即可实现远程调用接口的实现实体的代码了。
好了,下面我们来看一下,根据上述的proto 文件生成的java 代码的简略图:
在这里值得注意的地方就是,在service中所定义的方法, 即,sender 它的返回值是void, 你一定认为这个是因为proto文件出错了,而造成的返回值为void
而不是像想象的是ResponseBlock的类型, 不过仔细看的话,返回值是作为一个参数的形式在sender方法中,这个地方类似于我们常使用的指针的类型,
即服务器在远程运行该方法之后,将返回结果并不是以方法的返回值返回给调用一端的,而是通过参数的方式进行返回的。在这个地方前面的论述并非完全正确,
在后续的程序编写过程中,定义一个SenderImpl 的类来实现Service 中所定义的sender 接口在代码中是以BlockingInterface 接口形式所同意表现的,并且在其中
提供的sender 方法的实现方式是完全按照我们在 .proto 文件中定义的方式体现的,即传入参数中对应的是RequestBlock 类型实体,而在返回值对应的是ResponseBlock
的类型实体。
这个地方的官方文档是: protobuf official document
---------------------------------------------------------------------section 2 end ~ -------------------------------------------------------------------------------
-----------------------------------------------------------------------section 3 begin ~ ---------------------------------------------------------------------------
好了,现在中间生成的java 代码已经生成了,下面我们来根据其中的远程调用方法来书写实现文件中定义的接口的实现类、Server端和Client端的代码吧。
构架整个底层框架的方法同样也是可以分为如下的五步:
首先,分别创建三个类文件 分别命名为 SenderImpl,java Client.java 和 Server.java
然后,我们最先开始编写实现SenderImpl.java 这个文件,在这里需要注意的就是,关于如何构架一个RPC框架的问题,就是根据.proto 文件生成的中间
自动生成的文件中有很多用不到的代码,总之就是很多的代码,其中有一个名称为 我们在 proto 文件中在service定义中的接口的名字,找到它既可。
SenderImpl.java 文件中的内容如下:
package com.test.proto;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.text.html.ListView;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException ;
import com.google.protobuf.UnknownFieldSet;
import com.test.proto.ProtobufTest;
import com.test.proto.ProtobufTest.DataOrBuilder;
import com.test.proto.ProtobufTest.MyService.BlockingInterface;
import com.test.proto.ProtobufTest.RequestBlock;
import com.test.proto.ProtobufTest.ResponseBlock;
import com.test.proto.ProtobufTest.Data;
import com.test.proto.ProtobufTest.RequestBlockOrBuilder;
import com.test.proto.ProtobufTest.ResponseBlock.Builder;
import com.test.proto.ProtobufTest.ResponseBlockOrBuilder;
public class SenderImpl implements BlockingInterface
{
@Override
public ResponseBlock sender(RpcController controller, RequestBlock request)
throws ServiceException {
public ResponseBlock sender(RpcController controller, RequestBlock request)
throws ServiceException {
ResponseBlock.Builder resBuilder = ResponseBlock.newBuilder() ;
Data data = request.getDataBlock(0) ;
resBuilder.addDataBlock(data) ;
ResponseBlock rb = resBuilder.build() ;
return rb ;
}
}
Server:
package com.test.proto;
import java.util.concurrent.Executors;
import com.googlecode.protobuf.socketrpc.RpcServer;
import com.googlecode.protobuf.socketrpc.ServerRpcConnectionFactory ;
import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories;
import com.test.proto.ProtobufTest.MyService;
public class Server {
private int port ;
private int threadPoolSize ;
public Server ( int port , int threadPoolSize )
{
this.port = port ;
this.threadPoolSize = threadPoolSize ;
}
public void run ()
{
//start server
ServerRpcConnectionFactory rpcConnectionFactory =
SocketRpcConnectionFactories.createServerRpcConnectionFactory( port ) ;
RpcServer server = new RpcServer ( rpcConnectionFactory,
Executors.newFixedThreadPool(threadPoolSize), true ) ;
server.registerBlockingService(
MyService.newReflectiveBlockingService(new SenderImpl())) ;
server.run () ;
}
public static void main ( String [] args )
{
int port = 9003 ;
int size = 5 ;
new Server( port , size ).run () ;
}
}
Client 端代码:
package com.test.proto;
import com.google.protobuf.BlockingRpcChannel;
import com.google.protobuf.ByteString ;
import com.google.protobuf.RpcController ;
import com.google.protobuf.ServiceException ;
import com.googlecode.protobuf.socketrpc.RpcChannels ;
import com.googlecode.protobuf.socketrpc.RpcConnectionFactory ;
import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories ;
import com.googlecode.protobuf.socketrpc.SocketRpcController ;
import com.test.proto.ProtobufTest.MyService;
import com.test.proto.ProtobufTest.RequestBlock;
import com.test.proto.ProtobufTest.ResponseBlock;
import com.test.proto.ProtobufTest.MyService.BlockingInterface;
import com.test.proto.ProtobufTest.Data;
public class Client {
private int port ;
private String host ;
private int size ;
private int count ;
public Client ( int port , String host , int size , int count )
{
super() ;
this.port = port ;
this.host = host ;
this.size = size ;
this.count = count ;
}
public long run ()
{
//here we gonna to create a channel
RpcConnectionFactory connectionFactory =
SocketRpcConnectionFactories.createRpcConnectionFactory( host, port) ;
BlockingRpcChannel channel = RpcChannels.newBlockingRpcChannel(connectionFactory) ;
//here we call the service
BlockingInterface service = MyService.newBlockingStub(channel) ;
RpcController controller = new SocketRpcController () ;
Data.Builder dataBuilder = Data.newBuilder();
RequestBlock.Builder reqBuilder = RequestBlock.newBuilder() ;
dataBuilder.setData1("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
dataBuilder.setData2("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
dataBuilder.setData3("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") ;
dataBuilder.setData4("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") ;
dataBuilder.setData5("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") ;
dataBuilder.setData6("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") ;
dataBuilder.setLength(6000) ;
reqBuilder.addDataBlock(dataBuilder.build()) ;
long start = 0 ;
long end = 0 ;
try
{
start = System.currentTimeMillis() ;
service.sender(controller, reqBuilder.build()) ;
end = System.currentTimeMillis() ;
System.out.println("total sending time costs : "+(end - start ));
}
catch ( ServiceException e )
{
e.printStackTrace();
}
return end - start ;
}
public static void main ( String [] args )
{
String host = "acer-PC" ; //set your own hostname
int port = 9003 ;
int size = 1 ;
int count = 1 ;
new Client( port , host , size , count ).run() ;
}
}
在这里,LZ其实是想通过Client端来发送一个6000B 的数据包,但是暂时没有找到如何进行大数据段的表示方法,所以采用了比较脑残的方法,还请多多原谅,看在LZ在笔记本上一个个1 的输入的份上,,,,,
好了,今天先写到这里,中的来说框架是这样,但是在运行的时候多多少少存在些问题,需要进一步的修改一下才能够实现最终测试的目的,新的一天加油吧~
截止到目前为止,程序的代码还存在一些问题,Server端可以正常的启动,但是client端的访问还存在一些问题,还有值得注意的就是client端上设置的port和Server端上的port是一致的,因为在Server端上设置的端口号是Server所监听的发来消息的 port 号码,而在Client 端上设置的port号码是向哪一个 IP地址的 port 发送信息的设定。
在这里所对应的本质是,我们都知道,在网络中如果想要实现两个进程之间的通信的话,Client端 进程 在知道远程通信进程所在主机的IP地址还是远远不够的,最多也只能够实现点对点的通信方法,而不能实现进程之间的端对端的进程之间的通信,所以 这个端口号就是远程进程访问过程中所需要指定的运行在远程主机上面的进程的PID号码。 Client端设置的是想要访问的PID号码,通过IP+PID 的方式向服务器进程提出远程访问。前面所说的Client所要访问的PID号码并非一定是直接相应Client端请求的进程的ID号码, 当然这个PID所指定的是什么是和该服务器构架的模型有关的, 我们都知道在比较主流的服务器端通常是有一个协调者,这个协调者对应的实质上也是运行在服务器端的一个进程,即也是可以通过一个进程来对其进行表示。 如果Server是以协调者的方式受理来自客户端的请求,然后将该请求发送至各个工作者即实际调用服务器端的方法来完成Client的请求的线程的话,那么我们就称此服务器模型为 协调者/工作者模型,处于这种工作模型下面的协调进程也成为监听进程。 那么它的PID 就为该服务器上面的监听端口号,客户端只要将请求发送到该端口号的上面的话,就会被服务器的监听程序 即 协调者进程所受理,然后分发到实际的工作者的上面。
这是因为这个原因,我们在代码中要将双方的端口号设置为一样的。