1. 设置CVS环境
setenv CVSROOT :ext:你的CVS用户名@10.88.15.205:/cvsroot/mailrept
2. 从CVS中检出源代码,操作命令如下:
针对RC1版本:
cvs co -r RELENG_ENTPLATFORM_1_2_RC1 src/entplatform
针对RC2版本:
cvs co -r RELENG_ENTPLATFORM_1_2_RC2 src/entplatform
检出最新代码:
1步骤后:
运行:
cvs co src/entplatform src/entplatform
OK!
setenv CVSROOT :ext:你的CVS用户名@10.88.15.205:/cvsroot/mailrept
2. 从CVS中检出源代码,操作命令如下:
针对RC1版本:
cvs co -r RELENG_ENTPLATFORM_1_2_RC1 src/entplatform
针对RC2版本:
cvs co -r RELENG_ENTPLATFORM_1_2_RC2 src/entplatform
检出最新代码:
1步骤后:
运行:
cvs co src/entplatform src/entplatform
OK!
归根揭底没有很好的用一些原则,加上方法。。。。贯切。。。
图像:
![]()
病人:医生,我折腾了许久,无法理解函数指针。
中医:能意识到自己不理解,不错。那么你说说你目前的理解。函数指针是什么?
病人:函数指针是指向函数的指针。
中医:那么函数是什么?
病人:函数就是函数。
中医:不是指向函数的指针?
病人:自然不是。
中医:那函数怎么可以赋值给函数指针?难道int可以赋值给int* ?
病人:这个。。。。。。
中医:逻辑不通了吧?
病人:是啊,怎么回事哩?
中医:这个问题先搁置一下,我问你,什么是指针?
病人:是放地址的变量。
中医:函数指针里面放的什么?
病人:函数入口地址。
中医:那么函数指针就是放函数入口地址的变量?
病人: (小心地)我同意。
中医:函数是放函数入口地址的常量。
病人:哇!这样一来就好解释了!函数赋值给函数指针就像把常量赋值给同类型变量!
中医:还有问题吗?
病人:有,"函数是放函数入口地址的常量。"这句话不通啊。
函数是放自己入口地址的东西?
中医:孺字可教。这里"函数入口地址"是一个词,不能拆。真正的函数,无非是一块代码,
C/C++中没有描述"一块代码"的东西,
只有描述"一块代码"的入口地址的东西,函数及函数指针。
病人:我懂了,"函数指针是指向函数(1)的指针"和"函数(2)不是指向函数(3)的指针"的矛盾,
出自"函数(1)"的是你刚才说的"真正的函数",
函数(2)(3)指的C/C++语法意义上的"函数",两码事!
中医:嗯,有道理。那么还有问题吗?
病人:"函数指针是指向函数的指针。"这句话固然误导人,不过C/C++的语法,也起到了推波助澜的作用。
//////////////////////////////////
#include
typedef int (*FN_HAHA)();
int real_haha()
{
return printf("haha\n");
}
void main(int argc, char* argv[])
{
FN_HAHA haha=real_haha;
real_haha();
(*haha)();
}
//////////////////////////////////
既然 haha 和 real_haha是一个层次上的东西,
那么调用的时候为什么 一个 "real_haha();",
一个(*haha)()哩?很明显是在搞分化,搞脑子。
医生:连C/C++语法你都敢批评,强的!
正如你所说,这不是好的语法,所以现在的编译器,比如VC和gcc,
都允许用 haha();来代替传统的(*haha)();你44就知道了。
至于书上都写(*haha)();我只能说,
这个问题我自己也被书害了很久,最后扔了书自己想通的。
病人:我的病好了,我回去也把书扔了。
中医:能意识到自己不理解,不错。那么你说说你目前的理解。函数指针是什么?
病人:函数指针是指向函数的指针。
中医:那么函数是什么?
病人:函数就是函数。
中医:不是指向函数的指针?
病人:自然不是。
中医:那函数怎么可以赋值给函数指针?难道int可以赋值给int* ?
病人:这个。。。。。。
中医:逻辑不通了吧?
病人:是啊,怎么回事哩?
中医:这个问题先搁置一下,我问你,什么是指针?
病人:是放地址的变量。
中医:函数指针里面放的什么?
病人:函数入口地址。
中医:那么函数指针就是放函数入口地址的变量?
病人: (小心地)我同意。
中医:函数是放函数入口地址的常量。
病人:哇!这样一来就好解释了!函数赋值给函数指针就像把常量赋值给同类型变量!
中医:还有问题吗?
病人:有,"函数是放函数入口地址的常量。"这句话不通啊。
函数是放自己入口地址的东西?
中医:孺字可教。这里"函数入口地址"是一个词,不能拆。真正的函数,无非是一块代码,
C/C++中没有描述"一块代码"的东西,
只有描述"一块代码"的入口地址的东西,函数及函数指针。
病人:我懂了,"函数指针是指向函数(1)的指针"和"函数(2)不是指向函数(3)的指针"的矛盾,
出自"函数(1)"的是你刚才说的"真正的函数",
函数(2)(3)指的C/C++语法意义上的"函数",两码事!
中医:嗯,有道理。那么还有问题吗?
病人:"函数指针是指向函数的指针。"这句话固然误导人,不过C/C++的语法,也起到了推波助澜的作用。
//////////////////////////////////
#include
typedef int (*FN_HAHA)();
int real_haha()
{
return printf("haha\n");
}
void main(int argc, char* argv[])
{
FN_HAHA haha=real_haha;
real_haha();
(*haha)();
}
//////////////////////////////////
既然 haha 和 real_haha是一个层次上的东西,
那么调用的时候为什么 一个 "real_haha();",
一个(*haha)()哩?很明显是在搞分化,搞脑子。
医生:连C/C++语法你都敢批评,强的!
正如你所说,这不是好的语法,所以现在的编译器,比如VC和gcc,
都允许用 haha();来代替传统的(*haha)();你44就知道了。
至于书上都写(*haha)();我只能说,
这个问题我自己也被书害了很久,最后扔了书自己想通的。
病人:我的病好了,我回去也把书扔了。
如果你要选择成为有钱人,就要从观念、思维方式到行为方式,朝有钱人
靠近。经常与成功人士打打交道,领悟别人成功的经验和要点。要根据自己的资
源、优劣势等,找准自己在社会上的位置:适合做什么?由此选择行业、职业。
但无论如何创业精神是最主要的,即敢想、敢干、勤奋、吃苦耐劳,锐意进取,而不是安于现状,小富即安..
舍得付出,敢于拼搏,能勇往直前,遇到困难不妥协,认准目标,不言
放弃,同时注意节俭,不铺张浪费。反观那些每天工作八小时,有“打工心态”
的人,一辈子不会成为有钱人人,只能沦落为“穷人”。
要永远为自己工作,做自己的主人,要知道“天道酬勤”的道理。只有那
些敢于拼搏、锐意进取、思路清晰、舍得付出的人才会有丰厚的回报
1、做你真正感兴趣的事——你会花很多时间在上面,因此你一定要感兴趣才行,如果不是这样的话,你不愿意把时间花在上面,就得不到成功。
2、自己当老板。为别人打工,你绝不会变成巨富,老板一心一意地缩减开支,他的目标不是使他的职员变成有钱人。
3、提供一种有实效的服务,或一种实际的产品。你要以写作、绘画或作曲变成百万富翁的机会可以说是无限小,而你要在营造业、房地产、制造业发大财的机会比较大。记住,出版商赚的钱比作家多得多。
4、如果你坚持要用自己的灵感来创业?最好选择娱乐业,在这方面,发财的速度相当快,流行歌曲和电视最理想。
5、不论你是演员或商人,尽量增加你的观众。在小咖啡馆唱歌的人,所赚的钱一定比不上替大唱片公司灌唱片的人,地方性的商人,不会比全国性的商人赚的钱多。
6、找出一种需要,然后满足它。社会越变越复杂,人们所需要的产品和服务越来越多,最先发现这些需求而且满足他们的人,是改进现有产品和服务的人,也是最先成为富翁的人。
7、不要不敢采用不同的方式——新的方法和新产品,会造成新的财富。但必须确定你的新方法比旧方法更理想,你的新方法必须增进产品外观、效率、品质、方便或者降低成本。
8、如果你受过专业教育,或者有特殊才能,充分利用它。如果你烧得一手好菜,而却要去当泥水匠,那就太笨了。
9、在你着手任何事情之前,仔细地对周围的情形研究一番。政府机关和公共图书馆,可以提供不少资料,先做研究,可以节省你不少时间和金钱。
10、不要一直都想着发大财,不如你想想如何改进你的事业,您应该常常问自己的是:“我如何改良我的事业?”如何使事业进行顺利,财富就会跟着而来。
11、可能的话,进行一种家庭事业,这种方法可以减少费用,增进士气,利润的分配很简单,利润能够得到充分的利用,整个事业控制也较容易。
12、尽可能减少你的费用,但不能牺牲你的品质,否则的话,你等于是在慢性自杀,赚钱的机会不会大。
13、跟同行的朋友维持友谊——他们可能对你很有帮助。
14、把尽量多的时间花在事业上。一天12小时、一星期6天是最低要求,一天14小时到18小时很平常,一星期工作7天最好了。你必须先牺牲家庭和社会上的娱乐,直到你事业站稳为止。也只有到那时候,你才能把责任分给别人。
15、不要不敢自己下决心。听听别人的赞美和批评,但你自己要下决心。
16、不要不敢说实话。拐弯抹角,只会浪费时间,心里想什么就说什么,而且要尽可能地直截了当地、明确地说出来。
17、不要不敢承认自己的错误。犯了错误并不是一种罪行,犯错不改才是罪过。
18、不要因为失败就裹足不前。失败是难免的,也是有价值的,从失败中,你会学到正确的方法论。
19、不要在不可行的观念上打转。一发现某种方法行不通,立即把它放弃。世界上有无数的方法,把时间浪费在那些不可行的方法上是无可弥补的损失。
20、不要冒你承担不起的风险。如果你损失10万元,若损失得起的话,就可以继续下去,但如果你赔不起5万元,而一旦失败的话,你就完蛋了。
21、一再投资,不要让你的利润空闲着,你的利润要继续投资下去,最好投资别的事业或你控制的事业上,那样,才能钱滚钱,替你增加好几倍的财富。
22、请一位高明的律师——他会替你节约更多的金钱和时间,比起你所给予的将要多的多。
23、请一位精明的会计师。最初的时候,你自己记账,但除非你本身是个会计师,你还是请一位精明的会计师,可能决定你的成功和失败——他是值得你花钱的。
24、请专家替你报税。一位机灵的税务专家,可又替你免很多的税。
25、好好维持你的健康和你的平静心灵——否则的话,拥有再多的钱也没有什么意思。
靠近。经常与成功人士打打交道,领悟别人成功的经验和要点。要根据自己的资
源、优劣势等,找准自己在社会上的位置:适合做什么?由此选择行业、职业。
但无论如何创业精神是最主要的,即敢想、敢干、勤奋、吃苦耐劳,锐意进取,而不是安于现状,小富即安..
舍得付出,敢于拼搏,能勇往直前,遇到困难不妥协,认准目标,不言
放弃,同时注意节俭,不铺张浪费。反观那些每天工作八小时,有“打工心态”
的人,一辈子不会成为有钱人人,只能沦落为“穷人”。
要永远为自己工作,做自己的主人,要知道“天道酬勤”的道理。只有那
些敢于拼搏、锐意进取、思路清晰、舍得付出的人才会有丰厚的回报
1、做你真正感兴趣的事——你会花很多时间在上面,因此你一定要感兴趣才行,如果不是这样的话,你不愿意把时间花在上面,就得不到成功。
2、自己当老板。为别人打工,你绝不会变成巨富,老板一心一意地缩减开支,他的目标不是使他的职员变成有钱人。
3、提供一种有实效的服务,或一种实际的产品。你要以写作、绘画或作曲变成百万富翁的机会可以说是无限小,而你要在营造业、房地产、制造业发大财的机会比较大。记住,出版商赚的钱比作家多得多。
4、如果你坚持要用自己的灵感来创业?最好选择娱乐业,在这方面,发财的速度相当快,流行歌曲和电视最理想。
5、不论你是演员或商人,尽量增加你的观众。在小咖啡馆唱歌的人,所赚的钱一定比不上替大唱片公司灌唱片的人,地方性的商人,不会比全国性的商人赚的钱多。
6、找出一种需要,然后满足它。社会越变越复杂,人们所需要的产品和服务越来越多,最先发现这些需求而且满足他们的人,是改进现有产品和服务的人,也是最先成为富翁的人。
7、不要不敢采用不同的方式——新的方法和新产品,会造成新的财富。但必须确定你的新方法比旧方法更理想,你的新方法必须增进产品外观、效率、品质、方便或者降低成本。
8、如果你受过专业教育,或者有特殊才能,充分利用它。如果你烧得一手好菜,而却要去当泥水匠,那就太笨了。
9、在你着手任何事情之前,仔细地对周围的情形研究一番。政府机关和公共图书馆,可以提供不少资料,先做研究,可以节省你不少时间和金钱。
10、不要一直都想着发大财,不如你想想如何改进你的事业,您应该常常问自己的是:“我如何改良我的事业?”如何使事业进行顺利,财富就会跟着而来。
11、可能的话,进行一种家庭事业,这种方法可以减少费用,增进士气,利润的分配很简单,利润能够得到充分的利用,整个事业控制也较容易。
12、尽可能减少你的费用,但不能牺牲你的品质,否则的话,你等于是在慢性自杀,赚钱的机会不会大。
13、跟同行的朋友维持友谊——他们可能对你很有帮助。
14、把尽量多的时间花在事业上。一天12小时、一星期6天是最低要求,一天14小时到18小时很平常,一星期工作7天最好了。你必须先牺牲家庭和社会上的娱乐,直到你事业站稳为止。也只有到那时候,你才能把责任分给别人。
15、不要不敢自己下决心。听听别人的赞美和批评,但你自己要下决心。
16、不要不敢说实话。拐弯抹角,只会浪费时间,心里想什么就说什么,而且要尽可能地直截了当地、明确地说出来。
17、不要不敢承认自己的错误。犯了错误并不是一种罪行,犯错不改才是罪过。
18、不要因为失败就裹足不前。失败是难免的,也是有价值的,从失败中,你会学到正确的方法论。
19、不要在不可行的观念上打转。一发现某种方法行不通,立即把它放弃。世界上有无数的方法,把时间浪费在那些不可行的方法上是无可弥补的损失。
20、不要冒你承担不起的风险。如果你损失10万元,若损失得起的话,就可以继续下去,但如果你赔不起5万元,而一旦失败的话,你就完蛋了。
21、一再投资,不要让你的利润空闲着,你的利润要继续投资下去,最好投资别的事业或你控制的事业上,那样,才能钱滚钱,替你增加好几倍的财富。
22、请一位高明的律师——他会替你节约更多的金钱和时间,比起你所给予的将要多的多。
23、请一位精明的会计师。最初的时候,你自己记账,但除非你本身是个会计师,你还是请一位精明的会计师,可能决定你的成功和失败——他是值得你花钱的。
24、请专家替你报税。一位机灵的税务专家,可又替你免很多的税。
25、好好维持你的健康和你的平静心灵——否则的话,拥有再多的钱也没有什么意思。
echo 'alias vi /usr/local/bin/vim' > ~/.cshrc
or
echo 'alias vi=/usr/local/bin/vim' > ~/.bashrc
or
echo 'alias vi=/usr/local/bin/vim' > ~/.bashrc
代码:
注意函数原型及其返回数值:
char* get_Cookie(const char* name)
{
char* cookie=0;
char* p=0;
int len=0;
p = getenv("HTTP_COOKIE") ;
if(!p)
{
return 0;
}
len = strlen(name);
cookie = (char*)malloc(len+2) ;
sprintf(cookie,"%s=",name);
len++;
if (p = strstr(p,cookie))
{
p=p+len ;
char* p2;
int len2=0;
if(p2=strstr(p,";"))len2=p2-p;
else len2=strlen(p);
if(len2>len) {
free(cookie);
cookie=(char*)malloc(len2+1);
}
memcpy(cookie,p,len2);
*(cookie+len2)=0;
}
else
{
free(cookie);
cookie=0;
}
return cookie;
}
返回的是char* 指针:
我们main函数:
#include <iostream>
int main(){
printf( "Set-Cookie: curentouturl=kkk...; path=/\n");
printf("Content-type:text/html\n\n");
//out_url.Format("%s", get_Cookie("curentouturl"));
char *tmp;
tmp =(char*)malloc(1024);
printf("%s",get_Cookie(tmp));
return 0;
}
注意函数原型及其返回数值:
char* get_Cookie(const char* name)
{
char* cookie=0;
char* p=0;
int len=0;
p = getenv("HTTP_COOKIE") ;
if(!p)
{
return 0;
}
len = strlen(name);
cookie = (char*)malloc(len+2) ;
sprintf(cookie,"%s=",name);
len++;
if (p = strstr(p,cookie))
{
p=p+len ;
char* p2;
int len2=0;
if(p2=strstr(p,";"))len2=p2-p;
else len2=strlen(p);
if(len2>len) {
free(cookie);
cookie=(char*)malloc(len2+1);
}
memcpy(cookie,p,len2);
*(cookie+len2)=0;
}
else
{
free(cookie);
cookie=0;
}
return cookie;
}
返回的是char* 指针:
我们main函数:
#include <iostream>
int main(){
printf( "Set-Cookie: curentouturl=kkk...; path=/\n");
printf("Content-type:text/html\n\n");
//out_url.Format("%s", get_Cookie("curentouturl"));
char *tmp;
tmp =(char*)malloc(1024);
printf("%s",get_Cookie(tmp));
return 0;
}
Windows Media Player文件我曾怀疑我 走在沙漠中
从不结果 无论种什么梦
才张开翅膀 风却便沉默
习惯伤痛能不能 算收获
庆幸的是我 一直没回头
终于发现 真的是有绿洲
每把汗流了 生命变的厚重
走出沮丧才看见 新宇宙
海阔天空 在勇敢以后
要拿执着 将命运的锁打破
冷漠的人
谢谢你们曾经看轻我
让我不低头 更精采的活
凌晨的窗口 失眠整夜以后
看着黎明 从云里抬起了头
日落是沉潜 日出是成熟
只要是光一定会 灿烂的
海阔天空 在勇敢以后
要拿执着 将命运的锁打破
冷漠的人
谢谢你们曾经看轻我
让我不低头 更精采的活
海阔天空 狂风暴雨以后
转过头 对旧心酸一笑而过
最懂我的人
谢谢一路默默的陪着我
让我拥有好故事可以说
看未来 一步步来了
每个人都不是孤立存在的,所有的人都有或多或少的关系,这些关系交织在一起,形成一张很大的关系网,整个社会就是这样的一个网络。对你来说,这张网以你为中心,伸展到所有你曾接触过的人,和所有你可能去的地方。
可以说,这张网是你自己亲手织就的,每一个你所结交的新朋友,每一次和朋友的相聚,都是在编 织与他人或疏或密的情感纽带,都是在扩展这张网。它在一定程度上决定了你未来的道路是平坦还是曲折,是有所谓的“贵人相助”,还是要自己苦苦奋斗。也许在平日的行动中,很难感觉到这张网的存在,但是每到关键时刻,它总会不失时机地显示自己的力量,或者令你大功告成,或者令你壮志难酬。
你想回顾过去在人际关系方面的得失吗?你了解自己编成的关系网对你是有利还是妨碍吗?下面的题会帮你测试一下。
请选择最适合自己情形的答案。
1、你出门旅行时, A、通常很容易就交到朋友;B、喜欢一个人消磨时间;C、希望结交朋友,但难以做到。
2、你与朋友的友谊能保持多久?
A、大多是日久天长式;
B、有长有短,志趣相投者通常较长久;C、弃旧交新是常有的事。
3、你的朋友,首先应具备哪种品质?
A、能使人快乐轻松;
B、诚实可靠、值得信赖;
C、对我有兴趣、关注我。
4、与朋友们相处,你通常的情形是
A、倾向于赞扬他们的优点;
B、以诚为原则,有错我就指出来;
C、我的信条是不胡乱吹捧,也不苛刻指责。
5、走入一个陌生的环境,对那些陌生人,你A、常能很快记住他们的名字与某些特点;B、想记住这些信息,但失败时居多C、不去注意这些东西。
6、对人来说,结交人的主要目的是
A、使自己愉快;
B、希望被人喜欢;
C、想让他们帮我解决问题。
7、结交一位朋友你通常是
A、由熟人朋友的介绍开始;
B、通过各种场合的接触;
C、经过时间、困难的考验而交定。
计分表 1、A1、B3、C5;2、A1、B3、C5;3、A1、B3、C5;4、A1、B5、C3; 5、A1、B3、C5;6、A1、B3、C5;7、A5、B1、C3。
将各题的得分加在一起为总分。?
7-16为A类,17-26为B类,27-35为C类。
A类:结网能手。你凡事处理得当,合情合理,很有艺术。无论你走到哪里,笑脸和友谊总是围绕着你,你很受朋友的欢迎,他们也愿意帮助你,别人都认为你是很有办
可以说,这张网是你自己亲手织就的,每一个你所结交的新朋友,每一次和朋友的相聚,都是在编 织与他人或疏或密的情感纽带,都是在扩展这张网。它在一定程度上决定了你未来的道路是平坦还是曲折,是有所谓的“贵人相助”,还是要自己苦苦奋斗。也许在平日的行动中,很难感觉到这张网的存在,但是每到关键时刻,它总会不失时机地显示自己的力量,或者令你大功告成,或者令你壮志难酬。
你想回顾过去在人际关系方面的得失吗?你了解自己编成的关系网对你是有利还是妨碍吗?下面的题会帮你测试一下。
请选择最适合自己情形的答案。
1、你出门旅行时, A、通常很容易就交到朋友;B、喜欢一个人消磨时间;C、希望结交朋友,但难以做到。
2、你与朋友的友谊能保持多久?
A、大多是日久天长式;
B、有长有短,志趣相投者通常较长久;C、弃旧交新是常有的事。
3、你的朋友,首先应具备哪种品质?
A、能使人快乐轻松;
B、诚实可靠、值得信赖;
C、对我有兴趣、关注我。
4、与朋友们相处,你通常的情形是
A、倾向于赞扬他们的优点;
B、以诚为原则,有错我就指出来;
C、我的信条是不胡乱吹捧,也不苛刻指责。
5、走入一个陌生的环境,对那些陌生人,你A、常能很快记住他们的名字与某些特点;B、想记住这些信息,但失败时居多C、不去注意这些东西。
6、对人来说,结交人的主要目的是
A、使自己愉快;
B、希望被人喜欢;
C、想让他们帮我解决问题。
7、结交一位朋友你通常是
A、由熟人朋友的介绍开始;
B、通过各种场合的接触;
C、经过时间、困难的考验而交定。
计分表 1、A1、B3、C5;2、A1、B3、C5;3、A1、B3、C5;4、A1、B5、C3; 5、A1、B3、C5;6、A1、B3、C5;7、A5、B1、C3。
将各题的得分加在一起为总分。?
7-16为A类,17-26为B类,27-35为C类。
A类:结网能手。你凡事处理得当,合情合理,很有艺术。无论你走到哪里,笑脸和友谊总是围绕着你,你很受朋友的欢迎,他们也愿意帮助你,别人都认为你是很有办
今天的优秀组织已经不再是等级森严、分工明确、秩序井然,一种可变的、有机的和充满活力的架构正在渐成气候。这种新的架构能够快速响应组织不断变化的需求,成功编织他们的关系网络。在打造关系网的过程中,已经认识的人很重要,他们都有自己的熟人,而他们所熟识的人又有自己的熟人。
成功建立关系网的关键是和适当的人建立稳固的关系。良好的人际关系能拓宽你生活视野,让你了解周围所发生的一切,并提高你倾听和交流的能力。
强力10人内部圈
当你意识到职业关系的重要性,并开始选择可以助你一臂之力的人时,你可能不得不卸掉一些关系网中的额外包袱,其中或许包括那些相识已久但对你的职业生涯无所裨益的人。维持对你益处不大的老关系只会意味着时间的浪费。
良好、稳固、有力的人际关系的核心必须由10个左右你能靠得住的人组成。这首选的10个人可以包括你的朋友、家庭成员和那些在你职业生涯中彼此联系紧密的人,他们构成你的影响力内部圈,因为他们能让你发挥所长,而且彼此都希望对方成功。
当双方建立了稳固关系时,彼此会激发出强大能量。他们会激发对方的创造力,使彼此的灵感达到至美境界。为什么将你的影响力内部圈人数限定为10个人呢?因为强有力的关系需要你一个月至少维护1次,所以10个人或许已经用尽你所有的时间。
另外,应该至少挑选15个人作为你“强力10人内部圈”的后备力量,并经常与他们保持联系。如果你的一位主要关系退休或移民国外,最好的替补就是你的后备军。只要你能每月定期和他们联系,无论是通过电话、传真、聚会、电子邮件或信件,这个团体的人数都可以超过15人。 最初接触的15秒
在试图与你建立关系时,人们总会问你是做什么的,如果回答平淡如水,比如只是一句“我是一名经理”,你就失去了一个与对方交流的机会。比较得体的回答是:“我在某某公司负责一个小组的管理工作,主要为我们的管理软件开发监视软件。我也喜欢骑马,常常打网球,也热爱读书。”在不到15秒的时间里,你不仅使你的回答增添了色彩,也为对方提供了几个话题,说不定其中就有让对方感兴趣的。当他这样回答:“哦,你打网球?我也喜欢”时,你们就开始打造关系了。
建造关系网络必须遵守的规则,不是“别人能为我做什么?”,而是“我能为别人做什么?”在回答别人的问题时,不妨再接着问一下,“我能为你做些什么?” 保持联络 保持联络是成功建立关系网络的另一关键。《纽约时报》记者问美国前总统克林顿是如何保持自己的政治关系网的,克林顿回答:“每天晚上睡觉前,我会在一张卡片上列出我当天联系过的每一个人,注明重要细节、时间、会晤地点和其他一些相关信息,然后添加到秘书为我建立的关系网数据库中。这些年来朋友们帮了我不少。”
要与关系网络中的每个人保持积极联系,惟一的方式就是创造性地运用你的日程表。记下那些对你的关系特别重要的日子,比如生日或周年庆祝等。打电话给他们,至少给他们寄张贺卡让他们知道你心中想着他们。
观察他们在组织中的变化也同样重要。当你的关系网成员升职或调到新的组织去时,要及时祝贺他们。同时,也让他们知道你个人的情况。去度假之前,打电话问问他们有什么需要。当他们落入低谷时,打电话给他们鼓劲。不论你关系网中谁遇到麻烦时,立即与他通话,并主动提供帮助,这是表现支持的最好方式。
珍惜商务旅行的机会。如果你旅行的地点正好邻近你的某位关系成员,不要忘记提议和他共进午餐或晚餐。
3个月调整一次
至少每三个月变动一下你的关系网。要多提类似“为什么要保留这个关系?”的问题。如果你不定期更新或增加新人,你的关系网络就会陈旧。
为你的关系网络和组织提供信息。时刻关注对网络成员有用的信息,应定期将你收到的信息与他们分享。优秀的关系网络是双向的,如果你仅仅是个接受者,无论什么网络都会疏远你。搭建关系网络时,要做得好像你的职业生涯和个人生活都离不开它似的,其实,事实上也的确如此
成功建立关系网的关键是和适当的人建立稳固的关系。良好的人际关系能拓宽你生活视野,让你了解周围所发生的一切,并提高你倾听和交流的能力。
强力10人内部圈
当你意识到职业关系的重要性,并开始选择可以助你一臂之力的人时,你可能不得不卸掉一些关系网中的额外包袱,其中或许包括那些相识已久但对你的职业生涯无所裨益的人。维持对你益处不大的老关系只会意味着时间的浪费。
良好、稳固、有力的人际关系的核心必须由10个左右你能靠得住的人组成。这首选的10个人可以包括你的朋友、家庭成员和那些在你职业生涯中彼此联系紧密的人,他们构成你的影响力内部圈,因为他们能让你发挥所长,而且彼此都希望对方成功。
当双方建立了稳固关系时,彼此会激发出强大能量。他们会激发对方的创造力,使彼此的灵感达到至美境界。为什么将你的影响力内部圈人数限定为10个人呢?因为强有力的关系需要你一个月至少维护1次,所以10个人或许已经用尽你所有的时间。
另外,应该至少挑选15个人作为你“强力10人内部圈”的后备力量,并经常与他们保持联系。如果你的一位主要关系退休或移民国外,最好的替补就是你的后备军。只要你能每月定期和他们联系,无论是通过电话、传真、聚会、电子邮件或信件,这个团体的人数都可以超过15人。 最初接触的15秒
在试图与你建立关系时,人们总会问你是做什么的,如果回答平淡如水,比如只是一句“我是一名经理”,你就失去了一个与对方交流的机会。比较得体的回答是:“我在某某公司负责一个小组的管理工作,主要为我们的管理软件开发监视软件。我也喜欢骑马,常常打网球,也热爱读书。”在不到15秒的时间里,你不仅使你的回答增添了色彩,也为对方提供了几个话题,说不定其中就有让对方感兴趣的。当他这样回答:“哦,你打网球?我也喜欢”时,你们就开始打造关系了。
建造关系网络必须遵守的规则,不是“别人能为我做什么?”,而是“我能为别人做什么?”在回答别人的问题时,不妨再接着问一下,“我能为你做些什么?” 保持联络 保持联络是成功建立关系网络的另一关键。《纽约时报》记者问美国前总统克林顿是如何保持自己的政治关系网的,克林顿回答:“每天晚上睡觉前,我会在一张卡片上列出我当天联系过的每一个人,注明重要细节、时间、会晤地点和其他一些相关信息,然后添加到秘书为我建立的关系网数据库中。这些年来朋友们帮了我不少。”
要与关系网络中的每个人保持积极联系,惟一的方式就是创造性地运用你的日程表。记下那些对你的关系特别重要的日子,比如生日或周年庆祝等。打电话给他们,至少给他们寄张贺卡让他们知道你心中想着他们。
观察他们在组织中的变化也同样重要。当你的关系网成员升职或调到新的组织去时,要及时祝贺他们。同时,也让他们知道你个人的情况。去度假之前,打电话问问他们有什么需要。当他们落入低谷时,打电话给他们鼓劲。不论你关系网中谁遇到麻烦时,立即与他通话,并主动提供帮助,这是表现支持的最好方式。
珍惜商务旅行的机会。如果你旅行的地点正好邻近你的某位关系成员,不要忘记提议和他共进午餐或晚餐。
3个月调整一次
至少每三个月变动一下你的关系网。要多提类似“为什么要保留这个关系?”的问题。如果你不定期更新或增加新人,你的关系网络就会陈旧。
为你的关系网络和组织提供信息。时刻关注对网络成员有用的信息,应定期将你收到的信息与他们分享。优秀的关系网络是双向的,如果你仅仅是个接受者,无论什么网络都会疏远你。搭建关系网络时,要做得好像你的职业生涯和个人生活都离不开它似的,其实,事实上也的确如此
介绍
当你进入 UNIX 的神秘世界后,立刻会发现越来越多的东西难以理解。对于大多数人来说,BSD socket 的概念就是其中一个。这是一个很短的教程来解释他们是什么、他们如何工作并给出一些简单的代码来解释如何使用他们。
类比 (什么是 socket ?)
socket 是进行程序间通讯(IPC)的 BSD 方法。这意味着 socket 用来让一个进程和其他的进程互通信息,就象我们用电话来和其他的人交流一样。
用电话来比喻是很恰当的,我们在后面将一直用电话这个概念来描叙 socket 。
装上你的新电话(怎样侦听?)
一个人要能够收到别人打给他的电话,首先他要装上一门电话。同样,你必须先建立 socket 以侦听线路。这个过程包含几个步骤。首先,你要建立一个新的 socket,就象先装上电话一样。socket() 命令就完成这个工作。
因为 sockets 有几种类型,你要注明你要建立什么类型的。你要做一个选择是 socket 的地址格式。如同电话有音频和脉冲两种形式一样,socket 有两个最重要的选项是 AF_UNIX 和 IAF_INET。AF_UNIX 就象 UNIX 路径名一样识别 sockets。这种形式对于在同一台机器上的 IPC 很有用。而 AF_INET 使用象 192.9.200.10 这样被点号隔开的四个十进制数字的地址格式。除了机器地址以外,还可以利用端口号来允许每台机器上的多个 AF_INET socket。我们这里将着重于 AF_INET 方式,因为他很有用并广泛使用。
另外一个你必须提供的参数是 socket 的类型。两个重要的类型是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM 表明数据象字符流一样通过 socket 。而 SOCK_DGRAM 则表明数据将是数据报(datagrams)的形式。我们将讲解 SOCK_STREAM sockets,他很常见并易于使用。
在建立 socket 后,我们就要提供 socket 侦听的地址了。就象你还要个电话号码来接电话一样。bind() 函数来处理这件事情。
SOCK_STREAM sockets 让连接请求形成一个队列。如果你忙于处理一个连接,别的连接请求将一直等待到该连接处理完毕。listen() 函数用来设置最大不被拒绝的请求数(一般为5个)。一般最好不要使用 listen() 函数。
下面的代码说明如何利用 socket()、 bind() 和 listen() 函数建立连接并可以接受数据。
/* code to establish a socket; originally from bzs@bu-cs.bu.edu
*/
int establish(unsigned short portnum)
{ char myname[MAXHOSTNAME+1];
int s;
struct sockaddr_in sa;
struct hostent *hp;
memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear our address */
gethostname(myname, MAXHOSTNAME); /* who are we? */
hp= gethostbyname(myname); /* get our address info */
if (hp == NULL) /* we don\t exist !? */
return(-1);
sa.sin_family= hp->h_addrtype; /* this is our host address */
sa.sin_port= htons(portnum); /* this is our port number */
if ((s= socket(AF_INET, SOCK_STREAM, 0)) /* obligatory includes */
#include
#include
#include
#include
#include
#include
#include
#include
#define PORTNUM 50000 /* random port number, we need something */
void fireman(void);
void do_something(int);
main()
{ int s, t;
if ((s= establish(PORTNUM)) 0)
;
}
/* this is the function that plays with the socket. it will be called
* after getting a connection.
*/
void do_something(int s)
{
/* do your thing with the socket here
:
:
*/
}
拨号 (如何调用 socket)
现在你应该知道如何建立 socket 来接受调用了。那么如何调用呢?和电话一样,你要先有个电话。用 socket() 函数来完成这件事情,就象建立侦听的 socket 一样。
在给 socket 地址后,你可以用 connect() 函数来连接侦听的 socket 了。下面是一段代码。
int call_socket(char *hostname, unsigned short portnum)
{ struct sockaddr_in sa;
struct hostent *hp;
int a, s;
if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host\s */
errno= ECONNREFUSED; /* address? */
return(-1); /* no */
}
memset(&sa,0,sizeof(sa));
memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */
sa.sin_family= hp->h_addrtype;
sa.sin_port= htons((u_short)portnum);
if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) 0) {
bcount += br; /* increment byte counter */
buf += br; /* move buffer ptr for next read */
}
else if (br < 0) /* signal an error to the caller */
return(-1);
}
return(bcount);
}
相同的函数也可以写数据,留给我们的读者吧。
挂起(结束)
和你通过电话和某人交谈后一样,你要在 socket 间关闭连接。一般 close() 函数用来关闭每边的 socket 连接。如果一边的已经关闭,而另外一边却在向他写数据,则返回一个错误代码。
世界语(交流的语言很重要)
现在你可以在机器间联络了,可是要小心你所说的话。许多机器有自己的方言,如 ASCII 和 EBCDIC。更常见的问题是字节顺序问题。除非你一直传输的都是文本,否则你一定要注意这个问题。幸运的是,人们找出了解决的办法。
在很久以前,人们争论哪种顺序更“正确”。现在必要时有相应的函数来转换。其中有 htons()、ntohs()、htonl() 和 ntohl()。在传输一个整型数据前,先转换一下。
i= htonl(i);
write_data(s, &i, sizeof(i));
在读数据后,再变回来。
read_data(s, &i, sizeof(i));
i= ntohl(i);
如果你一直坚持这个习惯,你将比别人少出错的机会。
未来在你的掌握了(下一步?)
就用我们刚才讨论的东西,你就可以写自己的通讯程序了。和对待所有的新生事物一样, 最好还是看看别人已经做了些什么。这里有许多关于 BSD socket 的东西可以参考。
请注意,例子中没有错误检查,这在“真实”的程序中是很重要的。你应该对此充分重视。
当你进入 UNIX 的神秘世界后,立刻会发现越来越多的东西难以理解。对于大多数人来说,BSD socket 的概念就是其中一个。这是一个很短的教程来解释他们是什么、他们如何工作并给出一些简单的代码来解释如何使用他们。
类比 (什么是 socket ?)
socket 是进行程序间通讯(IPC)的 BSD 方法。这意味着 socket 用来让一个进程和其他的进程互通信息,就象我们用电话来和其他的人交流一样。
用电话来比喻是很恰当的,我们在后面将一直用电话这个概念来描叙 socket 。
装上你的新电话(怎样侦听?)
一个人要能够收到别人打给他的电话,首先他要装上一门电话。同样,你必须先建立 socket 以侦听线路。这个过程包含几个步骤。首先,你要建立一个新的 socket,就象先装上电话一样。socket() 命令就完成这个工作。
因为 sockets 有几种类型,你要注明你要建立什么类型的。你要做一个选择是 socket 的地址格式。如同电话有音频和脉冲两种形式一样,socket 有两个最重要的选项是 AF_UNIX 和 IAF_INET。AF_UNIX 就象 UNIX 路径名一样识别 sockets。这种形式对于在同一台机器上的 IPC 很有用。而 AF_INET 使用象 192.9.200.10 这样被点号隔开的四个十进制数字的地址格式。除了机器地址以外,还可以利用端口号来允许每台机器上的多个 AF_INET socket。我们这里将着重于 AF_INET 方式,因为他很有用并广泛使用。
另外一个你必须提供的参数是 socket 的类型。两个重要的类型是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM 表明数据象字符流一样通过 socket 。而 SOCK_DGRAM 则表明数据将是数据报(datagrams)的形式。我们将讲解 SOCK_STREAM sockets,他很常见并易于使用。
在建立 socket 后,我们就要提供 socket 侦听的地址了。就象你还要个电话号码来接电话一样。bind() 函数来处理这件事情。
SOCK_STREAM sockets 让连接请求形成一个队列。如果你忙于处理一个连接,别的连接请求将一直等待到该连接处理完毕。listen() 函数用来设置最大不被拒绝的请求数(一般为5个)。一般最好不要使用 listen() 函数。
下面的代码说明如何利用 socket()、 bind() 和 listen() 函数建立连接并可以接受数据。
/* code to establish a socket; originally from bzs@bu-cs.bu.edu
*/
int establish(unsigned short portnum)
{ char myname[MAXHOSTNAME+1];
int s;
struct sockaddr_in sa;
struct hostent *hp;
memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear our address */
gethostname(myname, MAXHOSTNAME); /* who are we? */
hp= gethostbyname(myname); /* get our address info */
if (hp == NULL) /* we don\t exist !? */
return(-1);
sa.sin_family= hp->h_addrtype; /* this is our host address */
sa.sin_port= htons(portnum); /* this is our port number */
if ((s= socket(AF_INET, SOCK_STREAM, 0)) /* obligatory includes */
#include
#include
#include
#include
#include
#include
#include
#include
#define PORTNUM 50000 /* random port number, we need something */
void fireman(void);
void do_something(int);
main()
{ int s, t;
if ((s= establish(PORTNUM)) 0)
;
}
/* this is the function that plays with the socket. it will be called
* after getting a connection.
*/
void do_something(int s)
{
/* do your thing with the socket here
:
:
*/
}
拨号 (如何调用 socket)
现在你应该知道如何建立 socket 来接受调用了。那么如何调用呢?和电话一样,你要先有个电话。用 socket() 函数来完成这件事情,就象建立侦听的 socket 一样。
在给 socket 地址后,你可以用 connect() 函数来连接侦听的 socket 了。下面是一段代码。
int call_socket(char *hostname, unsigned short portnum)
{ struct sockaddr_in sa;
struct hostent *hp;
int a, s;
if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host\s */
errno= ECONNREFUSED; /* address? */
return(-1); /* no */
}
memset(&sa,0,sizeof(sa));
memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */
sa.sin_family= hp->h_addrtype;
sa.sin_port= htons((u_short)portnum);
if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) 0) {
bcount += br; /* increment byte counter */
buf += br; /* move buffer ptr for next read */
}
else if (br < 0) /* signal an error to the caller */
return(-1);
}
return(bcount);
}
相同的函数也可以写数据,留给我们的读者吧。
挂起(结束)
和你通过电话和某人交谈后一样,你要在 socket 间关闭连接。一般 close() 函数用来关闭每边的 socket 连接。如果一边的已经关闭,而另外一边却在向他写数据,则返回一个错误代码。
世界语(交流的语言很重要)
现在你可以在机器间联络了,可是要小心你所说的话。许多机器有自己的方言,如 ASCII 和 EBCDIC。更常见的问题是字节顺序问题。除非你一直传输的都是文本,否则你一定要注意这个问题。幸运的是,人们找出了解决的办法。
在很久以前,人们争论哪种顺序更“正确”。现在必要时有相应的函数来转换。其中有 htons()、ntohs()、htonl() 和 ntohl()。在传输一个整型数据前,先转换一下。
i= htonl(i);
write_data(s, &i, sizeof(i));
在读数据后,再变回来。
read_data(s, &i, sizeof(i));
i= ntohl(i);
如果你一直坚持这个习惯,你将比别人少出错的机会。
未来在你的掌握了(下一步?)
就用我们刚才讨论的东西,你就可以写自己的通讯程序了。和对待所有的新生事物一样, 最好还是看看别人已经做了些什么。这里有许多关于 BSD socket 的东西可以参考。
请注意,例子中没有错误检查,这在“真实”的程序中是很重要的。你应该对此充分重视。
在Linux下写了个小的socket程序,分为客户端和服务器端,服务端开一个端口(2000),做为一个daemon,等待客户的连接请求.一旦有客户连接,服务器端打印出客户端的IP地址和端口,并且向服务器端发送欢迎信息和时间.下面是服务端的代码(tcpserver.c).由于这只是个简单的程序,所以只用了单线程实现!
/**
* Tcp Server program, It is a simple example only.
* zhengsh 200520602061 2
* when client connect to server, send a welcome message and timestamp in server.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <time.h>
#define SERVER_PORT 20000 // define the defualt connect port id
#define LENGTH_OF_LISTEN_QUEUE 10 //length of listen queue in server
#define BUFFER_SIZE 255
#define WELCOME_MESSAGE "welcome to connect the server. "
int main(int argc, char **argv)
{
int servfd,clifd;
struct sockaddr_in servaddr,cliaddr;
if ((servfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
printf("create socket error!\n");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(servfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
printf("bind to port %d failure!\n",SERVER_PORT);
exit(1);
}
if (listen(servfd,LENGTH_OF_LISTEN_QUEUE) < 0)
{
printf("call listen failure!\n");
exit(1);
}
while (1)
{//server loop will nerver exit unless any body kill the process
char buf[BUFFER_SIZE];
long timestamp;
socklen_t length = sizeof(cliaddr);
clifd = accept(servfd,(struct sockaddr*)&cliaddr,&length);
if (clifd < 0)
{
printf("error comes when call accept!\n");
break;
}
strcpy(buf,WELCOME_MESSAGE);
//inet_ntop(INET_ADDRSTRLEN,cliaddr.sin_addr,buf,BUFFER_SIZE);
printf("from client,IP:%s,Port:%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
timestamp = time(NULL);
strcat(buf,"timestamp in server:");
strcat(buf,ctime(×tamp));
send(clifd,buf,BUFFER_SIZE,0);
close(clifd);
}//exit
close(servfd);
return 0;
}
客户每次用一个随机的端口连接服务器,并接收来自服务器的欢迎信息
,然后打印出来(tcpclient).运行的时候接受一个参数,也就是服务器的ip地址.
/* Tcp client program, It is a simple example only.
* zhengsh 200520602061 2
* connect to server, and echo a message from server.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#define SERVER_PORT 20000 // define the defualt connect port id
#define CLIENT_PORT ((20001+rand())%65536) // define the defualt client port as a random port
#define BUFFER_SIZE 255
#define REUQEST_MESSAGE "welcome to connect the server.\n"
void usage(char *name)
{
printf("usage: %s IpAddr\n",name);
}
int main(int argc, char **argv)
{
int servfd,clifd,length = 0;
struct sockaddr_in servaddr,cliaddr;
socklen_t socklen = sizeof(servaddr);
char buf[BUFFER_SIZE];
if (argc < 2)
{
usage(argv[0]);
exit(1);
}
if ((clifd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
printf("create socket error!\n");
exit(1);
}
srand(time(NULL));//initialize random generator
bzero(&cliaddr,sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(CLIENT_PORT);
cliaddr.sin_addr.s_addr = htons(INADDR_ANY);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_aton(argv[1],&servaddr.sin_addr);
servaddr.sin_port = htons(SERVER_PORT);
//servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(clifd,(struct sockaddr*)&cliaddr,sizeof(cliaddr))<0)
{
printf("bind to port %d failure!\n",CLIENT_PORT);
exit(1);
}
if (connect(clifd,(struct sockaddr*)&servaddr, socklen) < 0)
{
printf("can't connect to %s!\n",argv[1]);
exit(1);
}
length = recv(clifd,buf,BUFFER_SIZE,0);
if (length < 0)
{
printf("error comes when recieve data from server %s!",argv[1]);
exit(1);
}
printf("from server %s :\n\t%s ",argv[1],buf);
close(clifd);
return 0;
}
程序在Fedora core 4下通过编译,有几个warining.但是不影响.
/**
* Tcp Server program, It is a simple example only.
* zhengsh 200520602061 2
* when client connect to server, send a welcome message and timestamp in server.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <time.h>
#define SERVER_PORT 20000 // define the defualt connect port id
#define LENGTH_OF_LISTEN_QUEUE 10 //length of listen queue in server
#define BUFFER_SIZE 255
#define WELCOME_MESSAGE "welcome to connect the server. "
int main(int argc, char **argv)
{
int servfd,clifd;
struct sockaddr_in servaddr,cliaddr;
if ((servfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
printf("create socket error!\n");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(servfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
printf("bind to port %d failure!\n",SERVER_PORT);
exit(1);
}
if (listen(servfd,LENGTH_OF_LISTEN_QUEUE) < 0)
{
printf("call listen failure!\n");
exit(1);
}
while (1)
{//server loop will nerver exit unless any body kill the process
char buf[BUFFER_SIZE];
long timestamp;
socklen_t length = sizeof(cliaddr);
clifd = accept(servfd,(struct sockaddr*)&cliaddr,&length);
if (clifd < 0)
{
printf("error comes when call accept!\n");
break;
}
strcpy(buf,WELCOME_MESSAGE);
//inet_ntop(INET_ADDRSTRLEN,cliaddr.sin_addr,buf,BUFFER_SIZE);
printf("from client,IP:%s,Port:%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
timestamp = time(NULL);
strcat(buf,"timestamp in server:");
strcat(buf,ctime(×tamp));
send(clifd,buf,BUFFER_SIZE,0);
close(clifd);
}//exit
close(servfd);
return 0;
}
客户每次用一个随机的端口连接服务器,并接收来自服务器的欢迎信息
,然后打印出来(tcpclient).运行的时候接受一个参数,也就是服务器的ip地址.
/* Tcp client program, It is a simple example only.
* zhengsh 200520602061 2
* connect to server, and echo a message from server.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#define SERVER_PORT 20000 // define the defualt connect port id
#define CLIENT_PORT ((20001+rand())%65536) // define the defualt client port as a random port
#define BUFFER_SIZE 255
#define REUQEST_MESSAGE "welcome to connect the server.\n"
void usage(char *name)
{
printf("usage: %s IpAddr\n",name);
}
int main(int argc, char **argv)
{
int servfd,clifd,length = 0;
struct sockaddr_in servaddr,cliaddr;
socklen_t socklen = sizeof(servaddr);
char buf[BUFFER_SIZE];
if (argc < 2)
{
usage(argv[0]);
exit(1);
}
if ((clifd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
printf("create socket error!\n");
exit(1);
}
srand(time(NULL));//initialize random generator
bzero(&cliaddr,sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(CLIENT_PORT);
cliaddr.sin_addr.s_addr = htons(INADDR_ANY);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_aton(argv[1],&servaddr.sin_addr);
servaddr.sin_port = htons(SERVER_PORT);
//servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(clifd,(struct sockaddr*)&cliaddr,sizeof(cliaddr))<0)
{
printf("bind to port %d failure!\n",CLIENT_PORT);
exit(1);
}
if (connect(clifd,(struct sockaddr*)&servaddr, socklen) < 0)
{
printf("can't connect to %s!\n",argv[1]);
exit(1);
}
length = recv(clifd,buf,BUFFER_SIZE,0);
if (length < 0)
{
printf("error comes when recieve data from server %s!",argv[1]);
exit(1);
}
printf("from server %s :\n\t%s ",argv[1],buf);
close(clifd);
return 0;
}
程序在Fedora core 4下通过编译,有几个warining.但是不影响.
下面我们先编写一个非常简单的套接口客户端程序client,这个程序较为简单,它演示了一个无名的套接口连接,
以及如何与一个服务器套接口连接,假设服务器套接口的名字是色server_socket.
/*
client.c
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
int main()
{
int sockfd;
int len;
struct sockaddr_un address;
int result;
char ch=A; //A好像有问题,改为ch了。。。编译通过。
sockfd=socket(AF_UNIX,SOCK_STREAM,0);
/*以上建立客户端的套接口,采用AF_UNIX的unix域协议*/
address.sun_family=AF_UNIX;
strcpy(address.sun_path,"server_socket");
len=sizeof(address);
/*以上创建服务器套接口的地址,其中包括套接口类型,名称*/
result=connect(sockfd,(struct sockaddr *)&address,len);
if(result==-1){
perror("oops:client1");
exit(1);
}
/*以上我们试图与服务器套接口建立连接*/
write(sockfd,&ch,1);
read(sockfd,&ch,1);
/*如果成功,将向服务器端发送一个字符,然后读取服务器的回答*/
printf("char from server=%c\n",ch);
close(sockfd);
exit(0);
}
/*
server.c
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
int main()
{
int server_sockfd,client_sockfd;
int server_len,client_len;
struct sockaddr_un server_address;
struct sockaddr_un client_address;
unlink("server_socket");
/*如果存在同名的套接口,则先删除*/
server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);
/*以上建立套接口,这时候无名*/
server_address.sun_family=AF_UNIX;
strcpy(server_address.sun_path,"server_socket");
server_len=sizeof(server_address);
bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
listen(server_sockfd,5);
/*以上创建监听队列.等待用户的连接请求*/
while(1)
{
char ch;
printf("server waiting\n");
client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);
/*以上接受一个客户的请求*/
read(client_sockfd,&ch,1);
/*因为连接一旦建立,客户就会先发消息过来,所以服务器先读*/
ch++;
write(client_sockfd,&ch,1);
/*把读取的字符串做简单处理,回送*/
close(client_sockfd);
}
}
以及如何与一个服务器套接口连接,假设服务器套接口的名字是色server_socket.
/*
client.c
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
int main()
{
int sockfd;
int len;
struct sockaddr_un address;
int result;
char ch=A; //A好像有问题,改为ch了。。。编译通过。
sockfd=socket(AF_UNIX,SOCK_STREAM,0);
/*以上建立客户端的套接口,采用AF_UNIX的unix域协议*/
address.sun_family=AF_UNIX;
strcpy(address.sun_path,"server_socket");
len=sizeof(address);
/*以上创建服务器套接口的地址,其中包括套接口类型,名称*/
result=connect(sockfd,(struct sockaddr *)&address,len);
if(result==-1){
perror("oops:client1");
exit(1);
}
/*以上我们试图与服务器套接口建立连接*/
write(sockfd,&ch,1);
read(sockfd,&ch,1);
/*如果成功,将向服务器端发送一个字符,然后读取服务器的回答*/
printf("char from server=%c\n",ch);
close(sockfd);
exit(0);
}
/*
server.c
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
int main()
{
int server_sockfd,client_sockfd;
int server_len,client_len;
struct sockaddr_un server_address;
struct sockaddr_un client_address;
unlink("server_socket");
/*如果存在同名的套接口,则先删除*/
server_sockfd=socket(AF_UNIX,SOCK_STREAM,0);
/*以上建立套接口,这时候无名*/
server_address.sun_family=AF_UNIX;
strcpy(server_address.sun_path,"server_socket");
server_len=sizeof(server_address);
bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
listen(server_sockfd,5);
/*以上创建监听队列.等待用户的连接请求*/
while(1)
{
char ch;
printf("server waiting\n");
client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);
/*以上接受一个客户的请求*/
read(client_sockfd,&ch,1);
/*因为连接一旦建立,客户就会先发消息过来,所以服务器先读*/
ch++;
write(client_sockfd,&ch,1);
/*把读取的字符串做简单处理,回送*/
close(client_sockfd);
}
}
摘要:
本文简单介绍了RPC(Remote Procedure Call 远程过程调用)的原理结构、特点,
及其开放给编程人员不同层次的编程接口。并且例举实例示度绾瓮ü齊pcgen 编译工
具来快速开发RPC应用。
一、 概述
在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段
代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在网络日益
发展的今天已无法适应实际需求。总所周知,传统过程调用模式无法充分利用网络上其
他主机的资源(如CPU、Memory等),也无法提高代码在实体间的共享程度,使得主机资
源大量浪费。
而本文要介绍的RPC编程,正是很好地解决了传统过程所存在的一系列弊端。通过RPC我
们可以充分利用非共享内存的多处理器环境(例如通过局域汪连接得多台工作站),这样
可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一个多处理器的计算机
上一样。你可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值
处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。
二、 RPC的结构原理及其调用机制
如前所述RPC其实也是种C/S的编程模式,有点类似C/S Socket 编程模式,但要比它
更高一层。当我们在建立RPC服务以后,客户端的调用参数通过底层的RPC传输通道,可以
是UDP,也可以是TCP(也即TI-RPC-无关性传输),并根据传输前所提供的目的地址及RPC
上层应用程序号转至相应的RPC Application Porgramme Server ,且此时的客户端处于等
待状态,直至收到应答或Time Out超时信号。具体的流程图如F1。当服务器端获得了请求
消息,则会根据注册RPC时告诉RPC系统的例程入口地址,执行相应的操作,并将结果返回
至客户端。
F1
当一次RPC调用结束后,相应线程发送相应的信号,客户端程序才会继续运行。
当然,一台服务主机上可以有多个远程过程提供服务,那么如何来表示一个唯一存
在的远程过程呢?一个远程过程是有三个要素来唯一确定的:程序号、版本号和过程号。
程序号是用来区别一组相关的并且具有唯一过程好的远程过程。一个程序可以有一个或几
个不同的版本,而每个版本的程序都包含一系列能被远程调用的过程,通过版本的引入,
使得不同版本下的RPC能同时提供服务。每个版本都包含有许多可供远程调用的过程,每个
过程则有其唯一标示的过程号。
三、 基于RPC的应用系统开发
通过以上对RPC原理的简介后,我们再来继续讨论如何来开发基于RPC的应用系统。
一般而言在开发RPC时,我们通常分为三个步骤:
a、 定义说明客户/服务器的通信协议。
这里所说的通信协议是指定义服务过程的名称、调用参数的数据类型和返回参数的数据
类型,还包括底层传输类型(可以是UDP或TCP),当然也可以由RPC底层函数自动选择
连接类型建立TI-RPC。最简单的协议生成的方法是采用协议编译工具,常用的有Rpcgen,
我会在后面实例中详细描述其使用方法。
b、 开发客户端程序。
c、 开发服务器端程序。
开发客户端和服务器端的程序时,RPC提供了我们不同层次的开发例程调用接口。不
同层次的接口提供了对RPC不同程度控制。一般可分为5个等级的编程接口,接下来我们
分别讨论一下各层所提供的功能函数。
1、 简单层例程
简单层是面向普通RPC应用,为了快速开发RPC应用服务而设计的,他提供
了如下功能函数。
函数名 功能描述
Rpc_reg( ) 在一特定类型的传输层上注册某个过程,来作为提供服务的RPC程序
Rpc_call( ) 远程调用在指定主机上指定的过程
Rpc_Broadcast( ) 向指定类型的所有传输端口上广播一个远程过程调用请求
2、 高层例程
在这一层,程序需要在发出调用请求前先创建一个客户端句柄,或是在侦听请
求前先建立一个服务器端句柄。程序在该层可以自由的将自己的应用绑在所有的
传输端口上,它提供了如下功能函数。
函数名 功能描述
Clnt_create( ) 程序通过这个功能调用,告诉底层RPC服务器的位置及其传输类型
Clnt_create_timed( ) 定义每次尝试连接的超时最大时间
Svc_create( ) 在指定类型的传输端口上建立服务器句柄,告诉底层RPC事件过程的相应入口地址
Clnt_call() 向服务器端发出一个RPC调用请求
3、 中间层例程
中间层向程序提供更为详细的RPC控制接口,而这一层的代码变得更为复杂,
但运行也更为有效,它提供了如下功能函数。
函数名 功能描述
Clnt_tp_create( ) 在指定的传输端口上建立客户端句柄
Clnt_tp_create_timed( ) 定义最大传输时延
Svc_tp_creaet( ) 在指定的传输端口上建立服务句柄
Clnt_call( ) 向服务器端发出RPC调用请求
4、 专家层例程
这层提供了更多的一系列与传输相关的功能调用,它提供了如下功能函数。
函数名 功能描述
Clnt_tli_create( ) 在指定的传输端口上建立客户端句柄
Svc_tli_create( ) 在指定的传输端口上建立服务句柄
Rpcb_set( ) 通过调用rpcbind将RPC服务和网络地址做映射
Rpcb_unset( ) 删除rpcb_set( ) 所建的映射关系
Rpcb_getaddr( ) 调用rpcbind来犯会指定RPC服务所对应的传输地址
Svc_reg( ) 将指定的程序和版本号与相应的时间例程建起关联
Svc_ureg( ) 删除有svc_reg( ) 所建的关联
Clnt_call( ) 客户端向指定的服务器端发起RPC请求
5、 底层例程
该层提供了所有对传输选项进行控制的调用接口,它提供了如下功能函数。
函数名 功能描述
Clnt_dg_create( ) 采用无连接方式向远程过程在客户端建立客户句柄
Svc_dg_create( ) 采用无连接方式建立服务句柄
Clnt_vc_create( ) 采用面向连接的方式建立客户句柄
Svc_vc_create( ) 采用面向连接的方式建立RPC服务句柄
Clnt_call( ) 客户端向服务器端发送调用请求
四、 实例介绍
以下我将通过实例向读者介绍通过简单层RPC的实现方法。通常在此过程中我们
将使用RPC协议编译工具-Rpcgen。Rpcgen 工具用来生成远程程序接口模块,它将以RPC
语言书写的源代码进行编译,Rpc 语言在结构和语法上同C语言相似。由Rpcgen 编译生
成的C源程序可以直接用C编译器进行编译,因此整个编译工作将分为两个部分。Rpcgen
的源程序以.x结尾,通过其编译将生成如下文件:
a) 一个头文件(.h)包括服务器和客户端程序变量、常量、类型等说明。
b) 一系列的XDR例程,它可以对头文件中定义的数据类型进行处理。
c) 一个Server 端的标准程序框架。
d) 一个Client 端的标准程序框架。
当然,这些输出可以是选择性的,Rpcgen 的编译选项说明如下:
选项 功能
'-' a 生成所有的模板文件
'-' Sc 生成客户端的模板文件
'-' Ss 生成服务器端的模板文件
'-' Sm 生成Makefile 文件
(详见Solaris Rpcgen Manaul)
Rpcgen 源程序 time.x:
/* time.x: Remote time printing protocol */
program TIMEPROG {
version PRINTIMEVERS {
string PRINTIME(string) = 1;
} = 1;
} = 0x20000001;
time_proc.c源程序(服务端):
/* time_proc.c: implementation of the remote procedure "printime" */
#include <stdio.h>
#include <rpc/rpc.h> /* always needed */
#include "time.h" /* time.h will be generated by rpcgen */
#include <time.h>
/* Remote version of "printime" */
char ** printime_1(char **msg,struct svc_req *req)
{
static char * result; /* must be static! */
static char tmp_char[100];
time_t rawtime;
FILE *f;
f = fopen("/tmp/rpc_result", "a+");
if (f == (FILE *)NULL) {
strcpy(tmp_char,"Error");
result = tmp_char;;
return (&result);
}
fprintf(f, "%s\n", *msg); //used for debugging
fclose(f);
time(&rawtime);
sprintf(tmp_char,"Current time is :%s",ctime(&rawtime));
result =tmp_char;
return (&result);
}
rtime.c源代码 (客户端)
/*
* rtime.c: remote version
* of "printime.c"
*/
#include <stdio.h>
#include "time.h" /* time.h generated by rpcgen */
main(int argc, char **argv)
{
CLIENT *clnt;
char *result;
char *server;
char *message;
if (argc != 3) {
fprintf(stderr, "usage: %s host message\n", argv[0]);
exit(1);
}
server = argv[1];
message = argv[2];
/*
* Create client "handle" used for
* calling TIMEPROG on the server
* designated on the command line.
*/
clnt = clnt_create(server, TIMEPROG, PRINTIMEVERS, "visible");
if (clnt == (CLIENT *)NULL) {
/*
* Couldn't establish connection
* with server.
* Print error message and die.
*/
clnt_pcreateerror(server);
exit(1);
}
/*
* Call the remote procedure
* "printime" on the server
*/
result =*printime_1(&message,clnt);
if (result== (char *)NULL) {
/*
* An error occurred while calling
* the server.
* Print error message and die.
*/
clnt_perror(clnt, server);
exit(1);
}
/* Okay, we successfully called
* the remote procedure.
*/
if (strcmp(result,"Error") == 0) {
/*
* Server was unable to print
* the time.
* Print error message and die.
*/
fprintf(stderr, "%s: could not get the time\n",argv[0]);
exit(1);
}
printf("From the Time Server ...%s\n",result);
clnt_destroy( clnt );
exit(0);
}
有了以上的三段代码后,就可用rpcgen 编译工具进行RPC协议编译,命令如下:
$rpcgen time.x
rpcgen 会自动生成time.h、time_svc.c、time_clnt.c
再用系统提供的gcc进行C的编译,命令如下:
$gcc rtime.c time_clnt.c -o rtime -lnsl //客户端编译 在freebsd下不要用-lnsl
$gcc time_proc.c time_svc.c -o time_server -lnsl //服务器端编译 在freebsd下不要用-lnsl
编译成功后即可在Server端运行time_server,立即将该服务绑定在rpc服务端口上提供
服务。在客户端运行./rtime hostname msg (msg 是一字符串,笔者用来测试时建立的),
立即会返回hostname 端的时间。
由于,在Sun Solaris 中无法获取远端Server 上时钟信息的功能(不改变本
地Server时钟),笔者曾将此程序应用于计费服务器同时钟服务器同步监测的网管
系统中,运行稳定,获得了较好的效果。应该说RPC的应用是十分广泛的,特别是
在分布式计算领域中尤为显得重要。当然,笔者也是刚接触RPC,还有很多地方了
解的不够深刻,望广大读者多指教。
本文简单介绍了RPC(Remote Procedure Call 远程过程调用)的原理结构、特点,
及其开放给编程人员不同层次的编程接口。并且例举实例示度绾瓮ü齊pcgen 编译工
具来快速开发RPC应用。
一、 概述
在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段
代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在网络日益
发展的今天已无法适应实际需求。总所周知,传统过程调用模式无法充分利用网络上其
他主机的资源(如CPU、Memory等),也无法提高代码在实体间的共享程度,使得主机资
源大量浪费。
而本文要介绍的RPC编程,正是很好地解决了传统过程所存在的一系列弊端。通过RPC我
们可以充分利用非共享内存的多处理器环境(例如通过局域汪连接得多台工作站),这样
可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一个多处理器的计算机
上一样。你可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值
处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。
二、 RPC的结构原理及其调用机制
如前所述RPC其实也是种C/S的编程模式,有点类似C/S Socket 编程模式,但要比它
更高一层。当我们在建立RPC服务以后,客户端的调用参数通过底层的RPC传输通道,可以
是UDP,也可以是TCP(也即TI-RPC-无关性传输),并根据传输前所提供的目的地址及RPC
上层应用程序号转至相应的RPC Application Porgramme Server ,且此时的客户端处于等
待状态,直至收到应答或Time Out超时信号。具体的流程图如F1。当服务器端获得了请求
消息,则会根据注册RPC时告诉RPC系统的例程入口地址,执行相应的操作,并将结果返回
至客户端。
F1
当一次RPC调用结束后,相应线程发送相应的信号,客户端程序才会继续运行。
当然,一台服务主机上可以有多个远程过程提供服务,那么如何来表示一个唯一存
在的远程过程呢?一个远程过程是有三个要素来唯一确定的:程序号、版本号和过程号。
程序号是用来区别一组相关的并且具有唯一过程好的远程过程。一个程序可以有一个或几
个不同的版本,而每个版本的程序都包含一系列能被远程调用的过程,通过版本的引入,
使得不同版本下的RPC能同时提供服务。每个版本都包含有许多可供远程调用的过程,每个
过程则有其唯一标示的过程号。
三、 基于RPC的应用系统开发
通过以上对RPC原理的简介后,我们再来继续讨论如何来开发基于RPC的应用系统。
一般而言在开发RPC时,我们通常分为三个步骤:
a、 定义说明客户/服务器的通信协议。
这里所说的通信协议是指定义服务过程的名称、调用参数的数据类型和返回参数的数据
类型,还包括底层传输类型(可以是UDP或TCP),当然也可以由RPC底层函数自动选择
连接类型建立TI-RPC。最简单的协议生成的方法是采用协议编译工具,常用的有Rpcgen,
我会在后面实例中详细描述其使用方法。
b、 开发客户端程序。
c、 开发服务器端程序。
开发客户端和服务器端的程序时,RPC提供了我们不同层次的开发例程调用接口。不
同层次的接口提供了对RPC不同程度控制。一般可分为5个等级的编程接口,接下来我们
分别讨论一下各层所提供的功能函数。
1、 简单层例程
简单层是面向普通RPC应用,为了快速开发RPC应用服务而设计的,他提供
了如下功能函数。
函数名 功能描述
Rpc_reg( ) 在一特定类型的传输层上注册某个过程,来作为提供服务的RPC程序
Rpc_call( ) 远程调用在指定主机上指定的过程
Rpc_Broadcast( ) 向指定类型的所有传输端口上广播一个远程过程调用请求
2、 高层例程
在这一层,程序需要在发出调用请求前先创建一个客户端句柄,或是在侦听请
求前先建立一个服务器端句柄。程序在该层可以自由的将自己的应用绑在所有的
传输端口上,它提供了如下功能函数。
函数名 功能描述
Clnt_create( ) 程序通过这个功能调用,告诉底层RPC服务器的位置及其传输类型
Clnt_create_timed( ) 定义每次尝试连接的超时最大时间
Svc_create( ) 在指定类型的传输端口上建立服务器句柄,告诉底层RPC事件过程的相应入口地址
Clnt_call() 向服务器端发出一个RPC调用请求
3、 中间层例程
中间层向程序提供更为详细的RPC控制接口,而这一层的代码变得更为复杂,
但运行也更为有效,它提供了如下功能函数。
函数名 功能描述
Clnt_tp_create( ) 在指定的传输端口上建立客户端句柄
Clnt_tp_create_timed( ) 定义最大传输时延
Svc_tp_creaet( ) 在指定的传输端口上建立服务句柄
Clnt_call( ) 向服务器端发出RPC调用请求
4、 专家层例程
这层提供了更多的一系列与传输相关的功能调用,它提供了如下功能函数。
函数名 功能描述
Clnt_tli_create( ) 在指定的传输端口上建立客户端句柄
Svc_tli_create( ) 在指定的传输端口上建立服务句柄
Rpcb_set( ) 通过调用rpcbind将RPC服务和网络地址做映射
Rpcb_unset( ) 删除rpcb_set( ) 所建的映射关系
Rpcb_getaddr( ) 调用rpcbind来犯会指定RPC服务所对应的传输地址
Svc_reg( ) 将指定的程序和版本号与相应的时间例程建起关联
Svc_ureg( ) 删除有svc_reg( ) 所建的关联
Clnt_call( ) 客户端向指定的服务器端发起RPC请求
5、 底层例程
该层提供了所有对传输选项进行控制的调用接口,它提供了如下功能函数。
函数名 功能描述
Clnt_dg_create( ) 采用无连接方式向远程过程在客户端建立客户句柄
Svc_dg_create( ) 采用无连接方式建立服务句柄
Clnt_vc_create( ) 采用面向连接的方式建立客户句柄
Svc_vc_create( ) 采用面向连接的方式建立RPC服务句柄
Clnt_call( ) 客户端向服务器端发送调用请求
四、 实例介绍
以下我将通过实例向读者介绍通过简单层RPC的实现方法。通常在此过程中我们
将使用RPC协议编译工具-Rpcgen。Rpcgen 工具用来生成远程程序接口模块,它将以RPC
语言书写的源代码进行编译,Rpc 语言在结构和语法上同C语言相似。由Rpcgen 编译生
成的C源程序可以直接用C编译器进行编译,因此整个编译工作将分为两个部分。Rpcgen
的源程序以.x结尾,通过其编译将生成如下文件:
a) 一个头文件(.h)包括服务器和客户端程序变量、常量、类型等说明。
b) 一系列的XDR例程,它可以对头文件中定义的数据类型进行处理。
c) 一个Server 端的标准程序框架。
d) 一个Client 端的标准程序框架。
当然,这些输出可以是选择性的,Rpcgen 的编译选项说明如下:
选项 功能
'-' a 生成所有的模板文件
'-' Sc 生成客户端的模板文件
'-' Ss 生成服务器端的模板文件
'-' Sm 生成Makefile 文件
(详见Solaris Rpcgen Manaul)
Rpcgen 源程序 time.x:
/* time.x: Remote time printing protocol */
program TIMEPROG {
version PRINTIMEVERS {
string PRINTIME(string) = 1;
} = 1;
} = 0x20000001;
time_proc.c源程序(服务端):
/* time_proc.c: implementation of the remote procedure "printime" */
#include <stdio.h>
#include <rpc/rpc.h> /* always needed */
#include "time.h" /* time.h will be generated by rpcgen */
#include <time.h>
/* Remote version of "printime" */
char ** printime_1(char **msg,struct svc_req *req)
{
static char * result; /* must be static! */
static char tmp_char[100];
time_t rawtime;
FILE *f;
f = fopen("/tmp/rpc_result", "a+");
if (f == (FILE *)NULL) {
strcpy(tmp_char,"Error");
result = tmp_char;;
return (&result);
}
fprintf(f, "%s\n", *msg); //used for debugging
fclose(f);
time(&rawtime);
sprintf(tmp_char,"Current time is :%s",ctime(&rawtime));
result =tmp_char;
return (&result);
}
rtime.c源代码 (客户端)
/*
* rtime.c: remote version
* of "printime.c"
*/
#include <stdio.h>
#include "time.h" /* time.h generated by rpcgen */
main(int argc, char **argv)
{
CLIENT *clnt;
char *result;
char *server;
char *message;
if (argc != 3) {
fprintf(stderr, "usage: %s host message\n", argv[0]);
exit(1);
}
server = argv[1];
message = argv[2];
/*
* Create client "handle" used for
* calling TIMEPROG on the server
* designated on the command line.
*/
clnt = clnt_create(server, TIMEPROG, PRINTIMEVERS, "visible");
if (clnt == (CLIENT *)NULL) {
/*
* Couldn't establish connection
* with server.
* Print error message and die.
*/
clnt_pcreateerror(server);
exit(1);
}
/*
* Call the remote procedure
* "printime" on the server
*/
result =*printime_1(&message,clnt);
if (result== (char *)NULL) {
/*
* An error occurred while calling
* the server.
* Print error message and die.
*/
clnt_perror(clnt, server);
exit(1);
}
/* Okay, we successfully called
* the remote procedure.
*/
if (strcmp(result,"Error") == 0) {
/*
* Server was unable to print
* the time.
* Print error message and die.
*/
fprintf(stderr, "%s: could not get the time\n",argv[0]);
exit(1);
}
printf("From the Time Server ...%s\n",result);
clnt_destroy( clnt );
exit(0);
}
有了以上的三段代码后,就可用rpcgen 编译工具进行RPC协议编译,命令如下:
$rpcgen time.x
rpcgen 会自动生成time.h、time_svc.c、time_clnt.c
再用系统提供的gcc进行C的编译,命令如下:
$gcc rtime.c time_clnt.c -o rtime -lnsl //客户端编译 在freebsd下不要用-lnsl
$gcc time_proc.c time_svc.c -o time_server -lnsl //服务器端编译 在freebsd下不要用-lnsl
编译成功后即可在Server端运行time_server,立即将该服务绑定在rpc服务端口上提供
服务。在客户端运行./rtime hostname msg (msg 是一字符串,笔者用来测试时建立的),
立即会返回hostname 端的时间。
由于,在Sun Solaris 中无法获取远端Server 上时钟信息的功能(不改变本
地Server时钟),笔者曾将此程序应用于计费服务器同时钟服务器同步监测的网管
系统中,运行稳定,获得了较好的效果。应该说RPC的应用是十分广泛的,特别是
在分布式计算领域中尤为显得重要。当然,笔者也是刚接触RPC,还有很多地方了
解的不够深刻,望广大读者多指教。
http://photo.sina.com.cn/xlxc001
前老大到互动,我没有过去,继续搞邮箱。。。。吖!
前老大到互动,我没有过去,继续搞邮箱。。。。吖!
近来在徐州采访时发现,这里的汉文化十分丰富,当地人谈起出生于此的汉高祖刘邦,脸上显露出的都是一种自豪。今天,来聊聊刘邦这个皇帝与同为布衣出身的朱元璋的故事,特别是在“夫妻生活”上的不同。
“惟公与我起布衣而有天下”,这话是朱元璋说的,其中的“公”指汉高祖刘邦。当年,朱元璋称帝后,拜祭历代帝王庙时,仅给刘邦敬了一杯酒,原因就在这句话里:两人一样都是从平头老百姓起家当上皇帝的。
从史书的记载上看,刘邦与朱元璋这两位“皇帝哥们”出身和经历有相似之处,而且在位时都很有作为。其在巩固政权的手段上也惊人一巧合,就是杀尽出生入死的大臣,刘邦身边的韩信、彭越、英布等都被他杀了;朱元璋也是,开国之臣李善长让他逼自杀了,大将军徐达让他赐发物蒸鹅给弄死了。在休养生息政策上,刘邦和朱元璋都采取了不少有益于民众的措施,促进了当时社会稳定和生产力水平的提高。但是,两人也有很大的不同,就是对待女色上,在后宫生活中的反差相当大。
在性生活,在美色消费方面,刘邦是很滥的,性取向混乱,这可能是由于秦汉时期从民间到宫里,纵欲之风盛行(目前出土有大量汉代性用具,就是一个佐证)之故;而吕后与戚夫人之间的明争暗斗,也给刘邦的丰功伟绩上抹了一笔黑。而朱元璋的形象很好,虽然滥杀了重多大臣,亦有众多嫔妃,但那是中国传统帝王后宫制度造成的,比起刘邦,可以说朱元璋在性事方面规矩多了,特别钟情于妻子马秀英。而马氏也是中国历史上难得的一位好皇后,她死后,朱元璋多年不册立新后,如果在民间,他们真的就是一对“模范夫妻”了。
图:历史上的刘邦与吕雉(资料图合成)
图:历史上的朱元璋与马秀英(资料图合成)
先来聊聊刘邦的后宫。
多读历史的人都知道,刘邦的后宫是充满血腥味的。刘邦的正妻吕后,名雉,字娥姁,今山东单县人,迁居徐州沛县。吕后在中国众多的皇后中,算是心毒手狠、极有心计的一个女人,如果没有唐代的武则天,我想就数她最著名了。
刘邦是徐州市沛县阳里村人(一说今丰县城西),本是一个好吃懒做的人,整日游手好闲,40岁时还是光棍一个。他解决性需要的办法就是,常年与一个姓曹的女人鬼混,还生了一个儿子,取名刘肥。刘邦当了皇帝后,刘肥被立为齐王,这是后话。虽然是个混混,但刘邦脑子好使,什么东西一学就会。后来,经人指点,给当地的官员跑腿,混上了泗水亭长。从此,他与县里一班官员有了来往,如萧何、曹参、夏侯婴。虽然已是个地方小官,但因为有劣迹在前,此时仍娶不到老婆,良家不愿把闺女嫁给这个“流氓”。据说吕雉的父亲会相面,觉得刘邦相貌不俗,将来必成大器,于是将当时已是“大龄女青年”的闺女吕雉嫁给刘邦,刘邦这才有了老婆。其实,在今天看来吕雉嫁给刘邦时并不算大,才25岁。当时乡亲们都嘲笑刘邦的老丈人嫁女行为很愚蠢。谁想,刘邦后来做了皇帝。据说,吕雉当年也觉得丈夫将来会有出息,说是刘邦到哪头顶上总有一团祥云跟着。
吕雉给刘邦生了一儿一女,即惠帝刘盈和鲁元公主。但吕雉好争风吃醋,在当了皇后后更做了许多“人做不出来”的事情,如把戚夫人制成“人彘”,成就了她中国历史上最毒“毒妇”的骂名。
刘邦是很有女人缘的,结婚之前就把一曹姓女人勾上手了,在婚后一样走桃花运。在与项羽争夺江山期间,前期老吃败仗,但却收获到了一个年轻美貌、后来影响后宫的女人——戚夫人。得到戚夫人的故事很浪漫,说是有一次败给项羽后,连饭也没得吃,逃到一村子里遇见一个老人。老人姓戚,带着18岁的闺女在此躲避战乱。一见带兵的刘邦,老人吓得连忙下拜,并带他回家里弄菜弄酒给他吃。刘邦见到老人的闺女,顿时动了心思,得知女孩尚未嫁人后,心中窃喜。老人看出意思,就说相面先生讲他闺女有贵人之相,难道遇到大王,就是她的前世姻缘?于是要把闺女许给刘邦为妻。虽然说刘邦心里暗喜,考虑家有妻室,已有吕雉,也客气了一番才应下。据说,刘邦是解下自己的玉带作为定情之物,老人当晚便让闺女陪刘邦睡觉了,刘邦这个老岳父看来比今天的父母们还想得开呢。
图:现代影视作品中的刘邦之皇后吕雉(,吴倩莲饰,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的刘邦之妃戚夫人(嘉碧仪饰,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的刘邦之情人曹女(沈傲君饰,图源于网络,摄影者不详,如有侵权请指出)
到此,刘邦已有了三个女人,一个情人曹氏,第一房妻子吕氏,第二妻子戚氏。刘邦与吕雉的感情本来是不错的,但在夺了天下后,情况却发生了变化。吕雉比戚夫人大多了,戚献身刘邦是才18女人的黄花大闺女,也是中国历史上有名的美女之一,而吕当年是有嫁不出之嫌的女人,年龄一大了自然就成了“豆腐渣”,年老色衰的吕雉敌不过戚。分别当了皇后和爱妃(夫人)后的两个女人,就开始明争暗斗起来了。
起先戚夫人占上风,刘邦每次外出都由戚夫人陪侍,而把吕后丢在后宫。戚夫人长得漂亮,歌舞也好。乐得刘邦天天把美人搂在怀里,而冷落了吕后,渐渐刘邦与吕后之间的情感就出了问题。本来已定下吕后生的儿子刘盈为太子,戚夫人却希望让自己10岁的儿子如意继位。刘邦也看不好刘盈,觉得性格不像自己,而如意却很聪明,有自己年轻时的样子。当刘邦把自己废太子想法拿到朝中商议时,如果不是有口吃的大臣周昌冒死力谏,戚夫人的阴谋差点就成了。后来,戚夫人又多次向刘邦提出立自己儿子为太子的事情,但年老的刘邦心有余而力不足了,因为在吕后的精心策划下,太子的势力已形成,没有办法废了。年幼的如意被迫离开京城到三千外的封地为王。
刘邦死后,刘盈继位,史称惠帝。贵为太后的吕雉卷土重来,“恶毒妇人心”显露了出来。她第一件事情是把“情故”戚夫人罚为奴隶,让人用钳子把她的一头秀发统统拨光,搞成了秃子,罚她去舂米劳动,限每天要舂一石,如果少半升则要打她一百棍。据史书上记载,自知命运不济的戚夫人悲中心中来:“子为王,母为虏,终日舂,薄暮常与死相伍,相隔三千里,谁当使告汝?”吕后闻讯,心生毒计,把戚夫人的儿子如意透进京城,暗暗给毒死了。如意死时是七窍出血,连已称帝的刘盈也于心不忍,大哭了一场,用王的礼仪将同父螶母的如意葬了,谥号隐王。
但就这样还不解恨,吕雉最后用“人彘”之刑把戚夫人活活给弄死了。自己的兄弟死后,刘盈很悲伤的,但吕后竟然让他去看“人彘”表演。刘盈也不知“人彘”为何物,便跟着太监去看了,七弯八绕到一间厕所里,看到一个血人,四肢全被砍了,眼珠被挖了,剩下两个血窟窿,人还没有死,身子还能动,嘴一张一张的。刘盈便问太监这是什么,一听是戚夫人,他差点被吓晕了。原来,吕雉对戚夫人下了毒手,施了酷刑后,又给她硬灌了药,让她听不见,不能语,半死不活地扔到了厕所里。惠帝因为受此惊吓,从此也不敢“治天下”了,终日饮酒作乐,仅做了七年皇帝就死了。
吕后的恶毒其实与刘邦有直接关系:他没有处理好夫妻之间的感情问题,特别是在称帝后十分好色,纵欲,把宠爱全给了年轻美貌的戚夫人,让结发之妻吕后独守冷宫,从常理上讲,吕后对戚夫人怀有不满是可以理解的。戚夫人希望自己的儿子当太子,也是感到刘邦死后自己的日子会很难过的,所以才希望刘邦废了刘盈。
这里再说一下,刘邦不仅十分好女色,性取向也十分混乱,是一个“双性恋”。他的男宠据说叫籍孺,刘邦经常与他同寝。这种性取向的存在可能与汉代人相信“美男破老”的习俗有关,当时人认为与年轻的美男子同房可以延年益寿。从这里可以看出,在女色方面,刘邦确实是不能与朱元璋相比的。
再聊聊朱元璋的后宫。
刘邦没有一个好的皇后,朱元璋的后宫却很幸福,自然是因为皇后马秀英的仁慈。因为马皇后的出现,中国帝王的后宫里才多了一位值得称道的女性。
图:现代影视作品中的马皇后之徐帆版(《传奇皇帝朱元璋》,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的马皇后之吕丽萍版(《大脚马皇后》,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的马皇后之剧雪版(《朱元璋》,图源于网络,摄影者不详,如有侵权请指出)
在与马秀英认识前,朱元璋不像刘邦那样有前科,既无情人,也不游手好闲。他放过牛,做过和尚。因为瘟疫,家里的人全死光了;因为贫穷,父母哥兄死后只能用草席埋了了事。他成了孤儿,可以说家境比当年的刘邦差多了。但就是这样,朱元璋“奋起淮甸,仗剑渡江,英贤云集,平伪汉,伐伪昊,定关中,廓清中原,遂平元都,混一海宇,不十年而成大业。”(《太祖实录》)
与刘邦一样,朱元璋的妻室也是人家“送”的。不同的地方是,刘邦是名声不好,娶不到,朱元璋则因家里贫寒,娶不起。元顺帝至正十二年(1352年)三月年,朱元璋投奔郭子兴时,其时还是一个穷和尚。郭子兴是安徽定远县有名的土财主,因无法忍受元人的欺侮,在濠州发动起义。收了朱元璋后,郭子兴常带他在身边,当亲兵用。
在智慧上,朱元璋与刘邦都有过人的地方。因为有勇有谋,才受到郭子兴的信任和器重,投奔两个月后,郭子兴与夫人张氏作主,将义女马秀英嫁给了朱元璋,这样也好拴着朱元璋的野心,让他忠心效劳。马秀英是安徽宿州人,父亲名字不详,史书上只称“马公”,母亲叫郑媪,在马秀英很小的时候就死了。马秀英的父亲因为杀了人,从宿州逃到定远,把闺女托付给有交情的郭子兴,这样马秀英成了郭子兴的义女,寄养郭家。后来,马父客死他乡,郭子兴待马秀英更如亲生闺女,据说亲自教她读书写字。马氏长大后,端庄秀丽,但天生一双大足,时人称天足,未缠过脚。因为这双脚,民间戏称马秀英为“马大脚”。
马秀英嫁给朱元璋后,很是疼爱自己这位小和尚出身的丈夫。据说有一次因为伤了郭子兴的面子,郭一气之下将他关了禁闭,也不给吃的。马秀英一听急了,从伙房偷了一个刚出锅的馒头送给朱元璋,路上碰巧碰到了义母张氏,便慌忙把馒头往怀里藏,结果把乳房都烫伤了,可见朱元璋与马秀英之间的恩爱程度。正因为这样,朱元璋当皇帝后,天不怕地不怕,就怕皇后马娘娘,生怕马秀英不高兴。而马氏因为恪守妇道,人品好,后宫嫔妃没有人不服,史学家称马氏是一个称职贤惠宽厚仁慈的正宫娘娘。
史书上对马皇后多有褒言,《明通鉴》称,“后,宿州人,仁慈有智鉴,好书史,佐上定天下,恒劝以不嗜杀人为本。及册为皇后,勤于内治,暇则讲求古训,告六宫以宋多贤后,命女史录其家法,朝夕省览。·······妃嫔、宫人皆厚待之。命妇入朝,如家人礼。爱诵《小学》,尝求上表章。上决事或震怒,辄随事微谏。虽上性严,为缓刑,戮者数矣。”可见,吕雉与马秀英是两个完全不能相比同论的皇后,一个恶毒,一个仁慈;一个不守妇道,搞乱后宫,一个恪守女道,稳定后宫;一个烦神,一个省心。
在这一点,刘邦确实是不幸的,而朱元璋则是幸运的。马皇后为他生育了不少儿子,《历代陵寝备考》称,“后生懿文太子、泰王樉、晋王桐、成祖、周王”(注,有史书称马皇后不能生育,或是朱棣非其亲生,见:《明成祖朱棣与四个女人说不清道不明的关系》),还为他的政事操心。马皇后多次劝朱元璋,“诚如陛下言。妾与陛下起贫贱,至今日,恒恐骄纵起于奢侈,危亡起于细微。故欲得贤人,共理天下。”如果不是马皇后,朱元璋还不知要滥杀多少人呢。而刘邦的皇后吕雉呢,则嫉贤妒能,为了坐稳自己的皇后,什么事都干得出来。
朱元璋也深知自己妻子的贤能,在马皇后生病后,朱元璋为她请来了良医,还亲自送饭,亲手喂药,大臣也为她祷祀。《明通鉴》载,马皇后告诉朱元璋,“死生命也,祷祀何益?且医何能活人,使服药不效,得毋以妾故罪诸医平?”《国榷》也称,“后微时,依郭子兴家,事上备极艰苦。每佐征讨大策,补缝行间,虽贵极,谦素不渝。上或谴怒,辄婉辞。朝夕尚食,手剂之,其谨微类此。疾笃,不复饮药。曰:‘药无益,徒为医者累’。”临死时,朱元璋问她有什么话留下,她说,“愿陛下求贤纳诛,慎终如始。”洪武15年,马皇后死了,时年51岁。当时朱元璋是泪如雨下,至死也没有再立一个皇后。
死后,朱元璋给马皇后很高的荣誉,谥之“孝慈昭宪至仁文德承天顺圣高皇后”,孝陵之名即由此而来。嘉靖十七年,加谥“孝慈贞化哲顺仁徽成天育圣至德高皇后”(《明史·后妃传》)。
虽然朱元璋与马秀英两人之间感情极好,但并不能说朱元璋的后宫生活就不丰富,朱元璋也是男人,是男人就喜欢美女,他性生活同样出色。《明会典》中称,“太祖四十妃嫔,惟二妃葬陵之东西,余俱从葬。”又有史书称是四十六女嫔妃。不论到底哪一个数字正确,至少可以证明一点,朱元璋死前享用的女人不低于40名啊。《国榷》中记载,有昭敬充妃胡氏、成穆贵妃孙氏、淑妃李氏、安妃郑氏、庄清安荣惠妃崔氏、安妃达氏、碽妃、宁妃郭氏、惠妃郭氏、顺妃胡氏、郜氏、韩氏、余氏、杨氏、周氏、贵妃赵氏、贤妃李氏、惠妃刘氏、丽妃万氏。等等。
汉、明两朝的帝王生活都是很荒淫的,两朝都出了很多风流帝王,如汉武帝“金屋藏娇”、汉成帝“牡丹花下死”、明武帝“豹房纵欲”、明世宗“炼丹恋色”。而朱元璋虽有众多嫔妃,却独独没有“荒淫皇帝”的骂名,令人称奇。刘邦不同了,同样是开国之君,民间则认为他是一位“流氓皇帝”。这到底为何?我前面说了,原因除了生理需要上差异之外,也许刘邦性欲比朱元璋强嘛,但主要是各人在对待女色、对待皇后态度上的不同所致。
刘邦一见吕后年老色衰,就拿结发妻子不当妻子了,而独宠戚夫人。朱元璋则不同,虽然马皇后生的是一双大脚,这在过去是很丑的女人,但朱元璋一直视之如贤妻。马皇后病了,他是“朝夕尚食,手剂之”。这种只能在寻常夫妻中才能看到的情形,出现在朱元璋的后宫中,实在是难得啊。而马皇后在朱元璋的女色消费上,也不是不管不问的,但她对朱元璋并不采取性控制的手段,让他专宠她一人,而是充许、甚至鼓励朱元璋纳妃子,包括前朝元顺帝的妃子洪吉喇氏(有人称是朱棣生母)、朝鲜女人李氏、对手陈友谅的小老婆。如果是吕后,这些女人恐怕早给折磨死了。但马皇后却很好地理顺了这么多女人之间的关系,宽厚仁慈,同样难得!
俗话说,家有贤妻旺夫啊,对于帝王来说,这道理是一样的。刘邦和朱元璋虽然都出生平民,但在史上留下了不同的评价,我想与两人皇后的优劣不无关系,但归根到底,还与两人对性生活态度的不同造成的。这里再补充说一下,刘邦的老婆还曾“红杏出墙”呢。据说,在刘邦与项羽南征北战,连性命都不保时,吕雉却在家里与同村的名叫审食其的男人勾搭成奸。本来刘邦考虑自己常年征战在外,家里无人照应,让审食其帮着照料自己妻小的,谁想性欲难忍的吕雉却与他眉来眼去,日久生情,在家过起了“夫妻生活”。后来,项羽把他们作为人质扣留时,吕雉与审食其仍同食同宿,外人竟然发现不了,故有史学家称吕雉不只是“毒妇”,还是“偷情高手”。
刘邦称帝后,吕雉还提请封审食其为“辟阳侯”,为刘邦同意。据说此后,俩人“偷情”时,审令其在床上更卖力了,答谢吕雉为自己的努力。刘邦虽然是战场上的大丈夫,但在情场上则是戴绿帽的君子,后宫失守啊。可叹的是,他死时也不知此事啊(也许是故意装着不知)!这是不是一种报应,有一种因果关系?如果刘邦如朱元璋那样,对老婆忠贞不渝,性生活讲点规矩,吕雉的行为或许也会收敛一些的,或许吕雉当年就是这样想的,你能偷人我为什么不能养汉?而按照以性问题为研究对象的社会学家李银河女士的观点,吕雉这么做也是争取性生活的平等,呵呵。(作者 倪方六)
“惟公与我起布衣而有天下”,这话是朱元璋说的,其中的“公”指汉高祖刘邦。当年,朱元璋称帝后,拜祭历代帝王庙时,仅给刘邦敬了一杯酒,原因就在这句话里:两人一样都是从平头老百姓起家当上皇帝的。
从史书的记载上看,刘邦与朱元璋这两位“皇帝哥们”出身和经历有相似之处,而且在位时都很有作为。其在巩固政权的手段上也惊人一巧合,就是杀尽出生入死的大臣,刘邦身边的韩信、彭越、英布等都被他杀了;朱元璋也是,开国之臣李善长让他逼自杀了,大将军徐达让他赐发物蒸鹅给弄死了。在休养生息政策上,刘邦和朱元璋都采取了不少有益于民众的措施,促进了当时社会稳定和生产力水平的提高。但是,两人也有很大的不同,就是对待女色上,在后宫生活中的反差相当大。
在性生活,在美色消费方面,刘邦是很滥的,性取向混乱,这可能是由于秦汉时期从民间到宫里,纵欲之风盛行(目前出土有大量汉代性用具,就是一个佐证)之故;而吕后与戚夫人之间的明争暗斗,也给刘邦的丰功伟绩上抹了一笔黑。而朱元璋的形象很好,虽然滥杀了重多大臣,亦有众多嫔妃,但那是中国传统帝王后宫制度造成的,比起刘邦,可以说朱元璋在性事方面规矩多了,特别钟情于妻子马秀英。而马氏也是中国历史上难得的一位好皇后,她死后,朱元璋多年不册立新后,如果在民间,他们真的就是一对“模范夫妻”了。
图:历史上的刘邦与吕雉(资料图合成)
图:历史上的朱元璋与马秀英(资料图合成)
先来聊聊刘邦的后宫。
多读历史的人都知道,刘邦的后宫是充满血腥味的。刘邦的正妻吕后,名雉,字娥姁,今山东单县人,迁居徐州沛县。吕后在中国众多的皇后中,算是心毒手狠、极有心计的一个女人,如果没有唐代的武则天,我想就数她最著名了。
刘邦是徐州市沛县阳里村人(一说今丰县城西),本是一个好吃懒做的人,整日游手好闲,40岁时还是光棍一个。他解决性需要的办法就是,常年与一个姓曹的女人鬼混,还生了一个儿子,取名刘肥。刘邦当了皇帝后,刘肥被立为齐王,这是后话。虽然是个混混,但刘邦脑子好使,什么东西一学就会。后来,经人指点,给当地的官员跑腿,混上了泗水亭长。从此,他与县里一班官员有了来往,如萧何、曹参、夏侯婴。虽然已是个地方小官,但因为有劣迹在前,此时仍娶不到老婆,良家不愿把闺女嫁给这个“流氓”。据说吕雉的父亲会相面,觉得刘邦相貌不俗,将来必成大器,于是将当时已是“大龄女青年”的闺女吕雉嫁给刘邦,刘邦这才有了老婆。其实,在今天看来吕雉嫁给刘邦时并不算大,才25岁。当时乡亲们都嘲笑刘邦的老丈人嫁女行为很愚蠢。谁想,刘邦后来做了皇帝。据说,吕雉当年也觉得丈夫将来会有出息,说是刘邦到哪头顶上总有一团祥云跟着。
吕雉给刘邦生了一儿一女,即惠帝刘盈和鲁元公主。但吕雉好争风吃醋,在当了皇后后更做了许多“人做不出来”的事情,如把戚夫人制成“人彘”,成就了她中国历史上最毒“毒妇”的骂名。
刘邦是很有女人缘的,结婚之前就把一曹姓女人勾上手了,在婚后一样走桃花运。在与项羽争夺江山期间,前期老吃败仗,但却收获到了一个年轻美貌、后来影响后宫的女人——戚夫人。得到戚夫人的故事很浪漫,说是有一次败给项羽后,连饭也没得吃,逃到一村子里遇见一个老人。老人姓戚,带着18岁的闺女在此躲避战乱。一见带兵的刘邦,老人吓得连忙下拜,并带他回家里弄菜弄酒给他吃。刘邦见到老人的闺女,顿时动了心思,得知女孩尚未嫁人后,心中窃喜。老人看出意思,就说相面先生讲他闺女有贵人之相,难道遇到大王,就是她的前世姻缘?于是要把闺女许给刘邦为妻。虽然说刘邦心里暗喜,考虑家有妻室,已有吕雉,也客气了一番才应下。据说,刘邦是解下自己的玉带作为定情之物,老人当晚便让闺女陪刘邦睡觉了,刘邦这个老岳父看来比今天的父母们还想得开呢。
图:现代影视作品中的刘邦之皇后吕雉(,吴倩莲饰,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的刘邦之妃戚夫人(嘉碧仪饰,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的刘邦之情人曹女(沈傲君饰,图源于网络,摄影者不详,如有侵权请指出)
到此,刘邦已有了三个女人,一个情人曹氏,第一房妻子吕氏,第二妻子戚氏。刘邦与吕雉的感情本来是不错的,但在夺了天下后,情况却发生了变化。吕雉比戚夫人大多了,戚献身刘邦是才18女人的黄花大闺女,也是中国历史上有名的美女之一,而吕当年是有嫁不出之嫌的女人,年龄一大了自然就成了“豆腐渣”,年老色衰的吕雉敌不过戚。分别当了皇后和爱妃(夫人)后的两个女人,就开始明争暗斗起来了。
起先戚夫人占上风,刘邦每次外出都由戚夫人陪侍,而把吕后丢在后宫。戚夫人长得漂亮,歌舞也好。乐得刘邦天天把美人搂在怀里,而冷落了吕后,渐渐刘邦与吕后之间的情感就出了问题。本来已定下吕后生的儿子刘盈为太子,戚夫人却希望让自己10岁的儿子如意继位。刘邦也看不好刘盈,觉得性格不像自己,而如意却很聪明,有自己年轻时的样子。当刘邦把自己废太子想法拿到朝中商议时,如果不是有口吃的大臣周昌冒死力谏,戚夫人的阴谋差点就成了。后来,戚夫人又多次向刘邦提出立自己儿子为太子的事情,但年老的刘邦心有余而力不足了,因为在吕后的精心策划下,太子的势力已形成,没有办法废了。年幼的如意被迫离开京城到三千外的封地为王。
刘邦死后,刘盈继位,史称惠帝。贵为太后的吕雉卷土重来,“恶毒妇人心”显露了出来。她第一件事情是把“情故”戚夫人罚为奴隶,让人用钳子把她的一头秀发统统拨光,搞成了秃子,罚她去舂米劳动,限每天要舂一石,如果少半升则要打她一百棍。据史书上记载,自知命运不济的戚夫人悲中心中来:“子为王,母为虏,终日舂,薄暮常与死相伍,相隔三千里,谁当使告汝?”吕后闻讯,心生毒计,把戚夫人的儿子如意透进京城,暗暗给毒死了。如意死时是七窍出血,连已称帝的刘盈也于心不忍,大哭了一场,用王的礼仪将同父螶母的如意葬了,谥号隐王。
但就这样还不解恨,吕雉最后用“人彘”之刑把戚夫人活活给弄死了。自己的兄弟死后,刘盈很悲伤的,但吕后竟然让他去看“人彘”表演。刘盈也不知“人彘”为何物,便跟着太监去看了,七弯八绕到一间厕所里,看到一个血人,四肢全被砍了,眼珠被挖了,剩下两个血窟窿,人还没有死,身子还能动,嘴一张一张的。刘盈便问太监这是什么,一听是戚夫人,他差点被吓晕了。原来,吕雉对戚夫人下了毒手,施了酷刑后,又给她硬灌了药,让她听不见,不能语,半死不活地扔到了厕所里。惠帝因为受此惊吓,从此也不敢“治天下”了,终日饮酒作乐,仅做了七年皇帝就死了。
吕后的恶毒其实与刘邦有直接关系:他没有处理好夫妻之间的感情问题,特别是在称帝后十分好色,纵欲,把宠爱全给了年轻美貌的戚夫人,让结发之妻吕后独守冷宫,从常理上讲,吕后对戚夫人怀有不满是可以理解的。戚夫人希望自己的儿子当太子,也是感到刘邦死后自己的日子会很难过的,所以才希望刘邦废了刘盈。
这里再说一下,刘邦不仅十分好女色,性取向也十分混乱,是一个“双性恋”。他的男宠据说叫籍孺,刘邦经常与他同寝。这种性取向的存在可能与汉代人相信“美男破老”的习俗有关,当时人认为与年轻的美男子同房可以延年益寿。从这里可以看出,在女色方面,刘邦确实是不能与朱元璋相比的。
再聊聊朱元璋的后宫。
刘邦没有一个好的皇后,朱元璋的后宫却很幸福,自然是因为皇后马秀英的仁慈。因为马皇后的出现,中国帝王的后宫里才多了一位值得称道的女性。
图:现代影视作品中的马皇后之徐帆版(《传奇皇帝朱元璋》,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的马皇后之吕丽萍版(《大脚马皇后》,图源于网络,摄影者不详,如有侵权请指出)
图:现代影视作品中的马皇后之剧雪版(《朱元璋》,图源于网络,摄影者不详,如有侵权请指出)
在与马秀英认识前,朱元璋不像刘邦那样有前科,既无情人,也不游手好闲。他放过牛,做过和尚。因为瘟疫,家里的人全死光了;因为贫穷,父母哥兄死后只能用草席埋了了事。他成了孤儿,可以说家境比当年的刘邦差多了。但就是这样,朱元璋“奋起淮甸,仗剑渡江,英贤云集,平伪汉,伐伪昊,定关中,廓清中原,遂平元都,混一海宇,不十年而成大业。”(《太祖实录》)
与刘邦一样,朱元璋的妻室也是人家“送”的。不同的地方是,刘邦是名声不好,娶不到,朱元璋则因家里贫寒,娶不起。元顺帝至正十二年(1352年)三月年,朱元璋投奔郭子兴时,其时还是一个穷和尚。郭子兴是安徽定远县有名的土财主,因无法忍受元人的欺侮,在濠州发动起义。收了朱元璋后,郭子兴常带他在身边,当亲兵用。
在智慧上,朱元璋与刘邦都有过人的地方。因为有勇有谋,才受到郭子兴的信任和器重,投奔两个月后,郭子兴与夫人张氏作主,将义女马秀英嫁给了朱元璋,这样也好拴着朱元璋的野心,让他忠心效劳。马秀英是安徽宿州人,父亲名字不详,史书上只称“马公”,母亲叫郑媪,在马秀英很小的时候就死了。马秀英的父亲因为杀了人,从宿州逃到定远,把闺女托付给有交情的郭子兴,这样马秀英成了郭子兴的义女,寄养郭家。后来,马父客死他乡,郭子兴待马秀英更如亲生闺女,据说亲自教她读书写字。马氏长大后,端庄秀丽,但天生一双大足,时人称天足,未缠过脚。因为这双脚,民间戏称马秀英为“马大脚”。
马秀英嫁给朱元璋后,很是疼爱自己这位小和尚出身的丈夫。据说有一次因为伤了郭子兴的面子,郭一气之下将他关了禁闭,也不给吃的。马秀英一听急了,从伙房偷了一个刚出锅的馒头送给朱元璋,路上碰巧碰到了义母张氏,便慌忙把馒头往怀里藏,结果把乳房都烫伤了,可见朱元璋与马秀英之间的恩爱程度。正因为这样,朱元璋当皇帝后,天不怕地不怕,就怕皇后马娘娘,生怕马秀英不高兴。而马氏因为恪守妇道,人品好,后宫嫔妃没有人不服,史学家称马氏是一个称职贤惠宽厚仁慈的正宫娘娘。
史书上对马皇后多有褒言,《明通鉴》称,“后,宿州人,仁慈有智鉴,好书史,佐上定天下,恒劝以不嗜杀人为本。及册为皇后,勤于内治,暇则讲求古训,告六宫以宋多贤后,命女史录其家法,朝夕省览。·······妃嫔、宫人皆厚待之。命妇入朝,如家人礼。爱诵《小学》,尝求上表章。上决事或震怒,辄随事微谏。虽上性严,为缓刑,戮者数矣。”可见,吕雉与马秀英是两个完全不能相比同论的皇后,一个恶毒,一个仁慈;一个不守妇道,搞乱后宫,一个恪守女道,稳定后宫;一个烦神,一个省心。
在这一点,刘邦确实是不幸的,而朱元璋则是幸运的。马皇后为他生育了不少儿子,《历代陵寝备考》称,“后生懿文太子、泰王樉、晋王桐、成祖、周王”(注,有史书称马皇后不能生育,或是朱棣非其亲生,见:《明成祖朱棣与四个女人说不清道不明的关系》),还为他的政事操心。马皇后多次劝朱元璋,“诚如陛下言。妾与陛下起贫贱,至今日,恒恐骄纵起于奢侈,危亡起于细微。故欲得贤人,共理天下。”如果不是马皇后,朱元璋还不知要滥杀多少人呢。而刘邦的皇后吕雉呢,则嫉贤妒能,为了坐稳自己的皇后,什么事都干得出来。
朱元璋也深知自己妻子的贤能,在马皇后生病后,朱元璋为她请来了良医,还亲自送饭,亲手喂药,大臣也为她祷祀。《明通鉴》载,马皇后告诉朱元璋,“死生命也,祷祀何益?且医何能活人,使服药不效,得毋以妾故罪诸医平?”《国榷》也称,“后微时,依郭子兴家,事上备极艰苦。每佐征讨大策,补缝行间,虽贵极,谦素不渝。上或谴怒,辄婉辞。朝夕尚食,手剂之,其谨微类此。疾笃,不复饮药。曰:‘药无益,徒为医者累’。”临死时,朱元璋问她有什么话留下,她说,“愿陛下求贤纳诛,慎终如始。”洪武15年,马皇后死了,时年51岁。当时朱元璋是泪如雨下,至死也没有再立一个皇后。
死后,朱元璋给马皇后很高的荣誉,谥之“孝慈昭宪至仁文德承天顺圣高皇后”,孝陵之名即由此而来。嘉靖十七年,加谥“孝慈贞化哲顺仁徽成天育圣至德高皇后”(《明史·后妃传》)。
虽然朱元璋与马秀英两人之间感情极好,但并不能说朱元璋的后宫生活就不丰富,朱元璋也是男人,是男人就喜欢美女,他性生活同样出色。《明会典》中称,“太祖四十妃嫔,惟二妃葬陵之东西,余俱从葬。”又有史书称是四十六女嫔妃。不论到底哪一个数字正确,至少可以证明一点,朱元璋死前享用的女人不低于40名啊。《国榷》中记载,有昭敬充妃胡氏、成穆贵妃孙氏、淑妃李氏、安妃郑氏、庄清安荣惠妃崔氏、安妃达氏、碽妃、宁妃郭氏、惠妃郭氏、顺妃胡氏、郜氏、韩氏、余氏、杨氏、周氏、贵妃赵氏、贤妃李氏、惠妃刘氏、丽妃万氏。等等。
汉、明两朝的帝王生活都是很荒淫的,两朝都出了很多风流帝王,如汉武帝“金屋藏娇”、汉成帝“牡丹花下死”、明武帝“豹房纵欲”、明世宗“炼丹恋色”。而朱元璋虽有众多嫔妃,却独独没有“荒淫皇帝”的骂名,令人称奇。刘邦不同了,同样是开国之君,民间则认为他是一位“流氓皇帝”。这到底为何?我前面说了,原因除了生理需要上差异之外,也许刘邦性欲比朱元璋强嘛,但主要是各人在对待女色、对待皇后态度上的不同所致。
刘邦一见吕后年老色衰,就拿结发妻子不当妻子了,而独宠戚夫人。朱元璋则不同,虽然马皇后生的是一双大脚,这在过去是很丑的女人,但朱元璋一直视之如贤妻。马皇后病了,他是“朝夕尚食,手剂之”。这种只能在寻常夫妻中才能看到的情形,出现在朱元璋的后宫中,实在是难得啊。而马皇后在朱元璋的女色消费上,也不是不管不问的,但她对朱元璋并不采取性控制的手段,让他专宠她一人,而是充许、甚至鼓励朱元璋纳妃子,包括前朝元顺帝的妃子洪吉喇氏(有人称是朱棣生母)、朝鲜女人李氏、对手陈友谅的小老婆。如果是吕后,这些女人恐怕早给折磨死了。但马皇后却很好地理顺了这么多女人之间的关系,宽厚仁慈,同样难得!
俗话说,家有贤妻旺夫啊,对于帝王来说,这道理是一样的。刘邦和朱元璋虽然都出生平民,但在史上留下了不同的评价,我想与两人皇后的优劣不无关系,但归根到底,还与两人对性生活态度的不同造成的。这里再补充说一下,刘邦的老婆还曾“红杏出墙”呢。据说,在刘邦与项羽南征北战,连性命都不保时,吕雉却在家里与同村的名叫审食其的男人勾搭成奸。本来刘邦考虑自己常年征战在外,家里无人照应,让审食其帮着照料自己妻小的,谁想性欲难忍的吕雉却与他眉来眼去,日久生情,在家过起了“夫妻生活”。后来,项羽把他们作为人质扣留时,吕雉与审食其仍同食同宿,外人竟然发现不了,故有史学家称吕雉不只是“毒妇”,还是“偷情高手”。
刘邦称帝后,吕雉还提请封审食其为“辟阳侯”,为刘邦同意。据说此后,俩人“偷情”时,审令其在床上更卖力了,答谢吕雉为自己的努力。刘邦虽然是战场上的大丈夫,但在情场上则是戴绿帽的君子,后宫失守啊。可叹的是,他死时也不知此事啊(也许是故意装着不知)!这是不是一种报应,有一种因果关系?如果刘邦如朱元璋那样,对老婆忠贞不渝,性生活讲点规矩,吕雉的行为或许也会收敛一些的,或许吕雉当年就是这样想的,你能偷人我为什么不能养汉?而按照以性问题为研究对象的社会学家李银河女士的观点,吕雉这么做也是争取性生活的平等,呵呵。(作者 倪方六)











