2007年11月21日星期三

RPC: Remote Procedure Call


RFC1057 - RPC: Remote Procedure Call Protocol Specification Version 2 (Sun version)

http://www.ietf.org/rfc/rfc1057.txt?number=1057

RFC1831 - RPC: Remote Procedure Call Protocol Specification Version 2 (ONC version)
http://www.ietf.org/rfc/rfc1831.txt?number=1831

RFC1832 - XDR: External Data Representation Standard (Obsoletes)
http://www.ietf.org/rfc/rfc1832.txt?number=1832

RFC4506 - XDR: External Data Representation Standard
http://www.ietf.org/rfc/rfc4506.txt?number=4506


远程过程调用(RPC) 是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI 网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式程序在内的应用程序更加容易。

RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用过程接收答复信息,获得进程结果,然后调用执行继续进行。

目前有多种RPC模式和执行。最初由Sun公司提出;IETF ONC 重新修订了Sun版本,使得 ONC PRC 协议成为 IETF 标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。


协议结构

远程过程调用(RPC)信息协议由两个不同结构组成:调用信息和答复信息。信息流程如下所示:

远程过程调用流程

RPC:远程过程调用流程

struct rpc_msg {
         unsigned int xid;
union switch (msg_type mtype) {
case CALL:
call_body cbody;
case REPLY:
reply_body rbody;
} body;
};
RPC调用信息:每条远程过程调用信息包括以下无符号整数字段,以独立识别远程过程:
  • 程序号(Program number)
  • 程序版本号(Program version number)
  • 过程号(Procedure number)

RPC 调用信息主体形式如下:

struct call_body {
unsigned int rpcvers; /* must be equal to two (2) */
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque_auth cred;
opaque_auth verf;
/* procedure specific parameters start here */
};
RPC答复信息:RPC 协议的答复信息的改变取决于网络服务器对调用信息是接收还是拒绝。答复信息请求包括区别以下情形的各种信息:
  • RPC 成功执行调用信息。.
  • RPC 的远程实现不是协议第二版,返回 RPC 支持的最低和最高版本号。
  • 在远程系统中,远程程序不可用。
  • 远程程序不支持被请求的版本号。返回远程程序所支持的最低和最高版本号。
  • 请求的过程号不存在。通常是呼叫方协议或程序差错。

RPC答复信息形式如下:

union reply_body switch (reply_stat stat) {
case MSG_ACCEPTED:
accepted_reply areply;
case MSG_DENIED:
rejected_reply rreply;
} reply;

其他消息定义参见RFC。

Callback function

/* 注意:函数指针在只声明了变量类型还未赋值(创建指针变量)前就使用,会导致空指针错误 */

一篇不错的关于回调机制的文章:
http://www.ibm.com/developerworks/cn/linux/l-callback/index.html


(转载)

什么是回调函数?

简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。

为什么要使用回调函数?

因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。

如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell 排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、 float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。

回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此 时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。

另一个使用回调机制的API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程 序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什 么,它只关心返回值,因为基于返回值,它将继续执行或退出。

不管怎么说,回调函数是继承自C语言的,因而,在C++中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法函数符(functor),而不是回调函数

一个简单的回调函数实现

声明和定义时应注意:回调函数由系统调用,不要把它当作你的某个类的成员函数。)

void f();// 函数原型

void (*) (); //函数指针的声明

注意此时还没有创建指针变量,只是声明了变量类型。
目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

// 获得函数指针的大小
unsigned psize = sizeof (void (*) ());

// 为函数指针声明类型定义
typedef void (*pfv) ();

pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法。


void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函数 */
}

void func();

int main()
{
p = func;
caller(p); /* 传递函数地址到调用者 */
}

如果赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。

-----------------------------------------------------------------------
(转载)
C++中回调函数(callback)和函数符(functor)的比较

回调函数(callback)与函数符(functor)很多时候从用途上来看很相似,以致于我们经常将它们相提并论。例如:

inline bool compare(int a, int b)
{
return a > b;
}

struct comparer {
bool operator()(int a, int b) const {
return a > b;
}
};

void main()
{
std::vector
<int> vec, vec2;
std::sort(vec.begin(), vec.end(), compare);
std::sort(vec2.begin(), vec2.end(), comparer());
}

之所以称为函数符,是因为这是一种利用某些类对象支持operator()的特性,来达到模拟函数调用效果的技术。

如果这里vec, vec2这两个vector的内容一样,那么从执行结果看,使用回调函数compare与使用函数符comparer() 是一样的。

函数符(functor)的优点

我的建议是,如果可以用函数符实现,那么你应该用函数符,而不要用回调。原因在于:
  • 函数符可以不带痕迹地传递上下文参数。而回调技术通常使用一个额外的void*参数传递。这也是多数人认为回调技术丑陋的原因。
  • 更好的性能。

函数符技术可以获得更好的性能,这点直观来讲比较难以理解。你可能说,回调函数申明为inline了,怎么会性能比函数符差?我们这里来分析下。我们假设某 个函数func(例如上面的std::sort)调用中传递了一个回调函数(如上面的compare),那么可以分为两种情况:

  • func是内联函数,并且比较简单,func调用最终被展开了,那么其中对回调函数的调用也成为一普通函数调用(而不是通过函数指针的间接调用),并且如果这个回调函数简单,那么也可能同时被展开。在这种情形下,回调函数与函数符性能相同。
  • func 是非内联函数,或者比较复杂而无法展开(例如上面的std::sort,我们知道它是快速排序,函数因为存在递归而无法展开)。此时回调函数作为一个函数 指针传入,其代码亦无法展开。而函数符则不同。虽然func本身复杂不能展开,但是func函数中对函数符的调用是编译器编译期间就可以确定并进行 inline展开的。因此在这种情形下,函数符比之于回调函数,有着更好的性能。并且,这种性能优势有时是一种无可比拟的优势(对于std::sort就 是如此,因为元素比较的次数非常巨大,是否可以进行内联展开导致了一种雪崩效应)。
函数符(functor)不能做的

话又说回来了,函数符并不能完全取代回调函数所有的应用场合。例如,我在std::AutoFreeAlloc中使用了回调函数,而不是函数符,这是因为AutoFreeAlloc要容纳异质的析构函数,而不是只支持某一种类的析构。这和模板(template)不能处理在同一个容器中支持异质类型,是一个道理。


Several timers in TCP

对于每个连接,TCP管理4个不同的定时器:
  1. 超时定时器使用于当希望收到另一端的确认
  2. 坚持定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
  3. 保活定时器可检测到一个空闲连接的另一端何时崩溃或重启
  4. 2MSL定时器测量一个连接处于TIME_WAIT状态的时间

超时定时器


超时重传是TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。

超时时间的计算是超时的核心部分,TCP要求这个算法能大致估计出当前的网络状况,虽然这确实很困难。要求精确的原因有两个:(1)定时长久会造成网络利用率不高。(2)定时太短会造成多次重传,使得网络阻塞。

有了超时就要有重传,但是就算是重传也是有策略的,而不是将数据简单的发送。

关于相关算法和策略的具体内容在TCPv121中有详细叙述。(慢启动,拥塞避免算法,快速重传与快速恢复算法)


坚持定时器

坚持定时器用于防止通告窗口为0以后双方互相等待死锁的情况。

坚持定时器的原理是简单的,当TCP服务器收到了客户端的0滑动窗口报文的时候,就启动一个定时器来计时,并在定时器溢出的时候向向客户端查询窗口是否已 经增大,如果得到非零的窗口就重新开始发送数据,如果得到0窗口就再开一个新的定时器准备下一次查询。通过观察可以得知,TCP的坚持定时器使用1,2, 4,8,16……64秒这样的普通指数退避序列来作为每一次的溢出时间。

有关此定时器的内容在TCPv122中有详细叙述。(糊涂窗口综合症)


保活定时器

保活定时器更加的简单,还记得FTP或者Http服务器都有Sesstion Time机制么?因为TCP是面向连接的,所以就会出现只连接不传送数据的“半开放连接”,服务器当然要检测到这种连接并且在某些情况下释放这种连接,这 就是保活定时器的作用。另外要提到的是,当其中一端如果崩溃并重新启动的情况下,如果收到该端“前生”的保活探察,则 要发送一个RST数据报文帮助另一端结束连接。

有关此定时器的内容在TCPv1的23有详细叙述。


2MSL定时器

TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifttime)。它是任何报文段被丢弃前在网络内的最长时间。

对于一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)

这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束之后才能再被使用。

2007年11月6日星期二

SCTP: Stream Control Transmission Protocol

/* TCP、UDP和SCTP同属传输层协议 */

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UDP:
RFC768 - User Datagram Protocol
http://www.ietf.org/rfc/rfc0768.txt?number=768

TCP:
RFC793 - Transmission Control Protocol
http://www.ietf.org/rfc/rfc0793.txt?number=793

RFC1323 - TCP Extensions for High Performance
http://www.ietf.org/rfc/rfc1323.txt?number=1323

RFC2581 - TCP Congestion Control
http://www.ietf.org/rfc/rfc2581.txt?number=2581

RFC2988 - Computing TCP's Retransmission Timer
http://www.ietf.org/rfc/rfc2988.txt?number=2988

RFC3390 - Increasing TCP's Initial Window
http://www.ietf.org/rfc/rfc3390.txt?number=3390

SCTP:
RFC2960 - Stream Control Transmission Protocol
http://www.ietf.org/rfc/rfc2960.txt?number=2960

RFC3286 - An Introduction to the Stream Control Transmission Protocol (SCTP)
http://www.ietf.org/rfc/rfc3286.txt?number=3286

RFC3309 - Stream Control Transmission Protocol (SCTP) Checksum Change
http://www.ietf.org/rfc/rfc3309.txt?number=3309

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

SCTP与TCP一样也是一种可靠的传输协议,不过它提供消息边界、传输级别多宿(multihoming)支持以及将头端阻塞(head-of-line blocking)减小到最小的一种方法。

SCTP是一个面向连接的提供可靠全双工关联(association)的协议。SCTP像TCP那样给应用层提供可靠性、排序、流量控制以及全双工的数据传输服务。

SCTP中使用“关联(association)”取代“连接(connection)”是为了避免这样的内涵:一个连接只涉及两个IP地址之间的通信。一个关联指代可能因为多宿而涉及不止一个地址的两个系统之间的一次通信会话。因为SCTP是多宿的,每个关联涉及的源宿两端各有一组IP地址和单个端口号。

与TCP不同的是,SCTP是面向消息的(message-oriented)。SCTP提供消息服务,也就是维护来自应用层的记录边界,它提供各个记录的按序投递服务。与UDP一样,由发送端写入SCTP的每个记录的长度随数据一道传递给接收端应用程序。

SCTP能够在所连接的端点之间提供多个流,每个流各自可靠地按序投递消息。一个流上某个消息的丢失不会阻塞同一关联其他流上消息的投递,这与TCP正好相反。SCTP还提供多宿特性,使得每个SCTP端点能够支持多个IP地址。

2007年11月5日星期一

Converting Strings To/From Ints

(ZZ)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Converting a String Into an int Using atoi

Before I leave the string section, I'd like to talk about two useful functions that could come in handy later on. Both of these require the stdlib.h header file.

First of all, atoi. This converts strings, like "23" or even "29dhjds" into integers (returning 23 and 29 respectively in this case).

atoi requires one char * argument and returns an int (not a float!).

If the string is empty, or first character isn't a number or a minus sign, then atoi returns 0.

If atoi encounters a non-number character, it returns the number formed up until that point.

#include
#include
int main() {
char str1[] = "124z3yu87";
char str2[] = "-3.4";
char *str3 = "e24";
printf("str1: %d\n", atoi(str1));
printf("str2: %d\n", atoi(str2));
printf("str3: %d\n", atoi(str3));
return 0;
}

Output:

str1: 124
str2: -3
str3: 0

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Converting an int Into a String Using itoa (NOT ANSI C)

Before I continue, I must warn you that itoa is NOT an ANSI function, (it's not a standard C function). You should use sprintf to convert an int into a string, which I'll show you in a moment.

I'll cover itoa in case you've ever wondered what it does.

itoa takes three arguments.

The first one is the integer to be converted.

The second is a char * variable - this is where the string is going to be stored. My program crashed if I pass in a char * variable I've already used, so I passed in a normal sized char array and it worked fine.

The last one is NOT the size of the array, but it's the BASE of your number - base 10 is the one you're most likely to use. Base 2 is binary, 8 is octal and 16 is hexadecimal.

For a small lesson on bases, go back to the Hexadecimal section.

itoa is a very useful function, which is supported by some compilers - shame it isn't support by all, unlike atoi.

Maybe converting a base 10 number into binary format as a string isn't so hard after all...

#include
#include

int main() {
char str[10]; /* MUST be big enough to hold all the characters of your number!! */

printf("15 in binary is %s\n", itoa(15, str, 2));
printf("15 in octal is %s\n", itoa(15, str, 8));
printf("15 in decimal is %s\n", itoa(15, str, 10));
printf("15 in hex is %s\n", itoa(15, str, 16));
return 0;
}

Output:
15 in binary is 1111
15 in octal is 17
15 in decimal is 15
15 in hex is f

... itoa can be useful when you know your program doesn't have to be ANSI C only.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sprintf

You can use this function as an alternative to itoa. It's only half as good as itoa because you can't specify the base of your number to be converted.

sprintf takes three arguments.

The first has to be a char * variable, which means you can use a char array, but make sure it's big enough to hold the converted number.

The second argument is a string containing a format specifier, depending on the format of the number you want to convert.

The third argument is the number you want to convert into a string.

sprintf returns the number of characters in the string (not included the null character).

This example convert a few numbers into string format, and prints out the result:

#include

int main() {
char str[10]; /* MUST be big enough to hold all the characters of your number!! */
int i;

i = sprintf(str, "%o", 15);
printf("15 in octal is %s\n", str);
printf("sprintf returns: %d\n\n", i);

i = sprintf(str, "%d", 15);
printf("15 in decimal is %s\n", str);
printf("sprintf returns: %d\n\n", i);

i = sprintf(str, "%x", 15);
printf("15 in hex is %s\n", str);
printf("sprintf returns: %d\n\n", i);

i = sprintf(str, "%f", 15.05);
printf("15.05 as a string is %s\n", str);
printf("sprintf returns: %d\n\n", i);

return 0;
}

Output:
15 in octal is 17
sprintf returns: 2

15 in decimal is 15
sprintf returns: 2

15 in hex is f
sprintf returns: 1

15.05 as a string is 15.050000
sprintf returns: 9

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sscanf

For completeness, I'm going to cover sscanf, seeing that it's paired with sprintf.

You could've guessed that it converts a string into various formats.

sscanf takes three arguments, for example:

sscanf(str, "%d", &num);

The first is a char * variable that contains data to be converted.

The second is a string containing a format specifier that determines how the string is converted.

The third is a memory location to place the result of the conversion. Most of the time, you'll need the "address of" operator (&), then a variable name, or you can place a char * variable here.

Now, if the string you pass into sscanf contains a space, only the data up until that space is converted.

sscanf returns the number of items converted.

This example performs conversions in several formats:

#include

int main() {
char* ints = "20, 40, 60";
char* floats = "10.4, 24.66";
char* hex = "FF, F";

int i;
int n;
float f;
int h;
char* s;

i = sscanf(ints, "%d", &n);
printf("n: %d\n", n);
printf("sscanff returns: %d\n\n", i);

i = sscanf(floats, "%f", &f);
printf("f: %f\n", f);
printf("sscanff returns: %d\n\n", i);

i = sscanf(hex, "%x", &h);
printf("h: %d\n", h);
printf("sscanff returns: %d\n\n", i);

i = sscanf(ints, "%s", s);
printf("s: %s\n", s);
printf("sscanff returns: %d\n\n", i);

return 0;
}

Output:
n: 20
sscanff returns: 1

f: 10.400000
sscanff returns: 1

h: 255
sscanff returns: 1

s: 20,
sscanff returns: 1

Notice how sscanf ignores the comma when converting to a number (see the result for the string variable, s).

World Clocks

Endless Space Headline Animator

Mobile Ads