沉重的话题: 一个毕业后进微软的同志,上月死了,睡一觉就起不来了,医生说是猝死,
推测是过劳,只不过25岁的黄金年龄。
晚上9-11点为免疫系统(淋巴)排毒时间,此段时间应安静或听音乐
晚间11-凌晨1点,肝的排毒,需在熟睡中进行。
凌晨1-3点,胆的排毒,亦同。
凌晨3-5点,肺的排毒。此即为何咳嗽的人在这段时间咳得最剧烈,因排毒动作已走到肺;
不应用止咳药,以免抑制废积物的排除。
凌晨5-7点,大肠的排毒,应上厕所排便。
凌晨7-9点,小肠大量吸收营养的时段,应吃早餐。
疗病者最好早吃,在6点半前,养生者在7点半前,
不吃早餐者应改变习惯,即使拖到9、10点吃都比不吃好。
半夜至凌晨4点为脊椎造血时段,必须熟睡,不宜熬夜。
推测是过劳,只不过25岁的黄金年龄。
晚上9-11点为免疫系统(淋巴)排毒时间,此段时间应安静或听音乐
晚间11-凌晨1点,肝的排毒,需在熟睡中进行。
凌晨1-3点,胆的排毒,亦同。
凌晨3-5点,肺的排毒。此即为何咳嗽的人在这段时间咳得最剧烈,因排毒动作已走到肺;
不应用止咳药,以免抑制废积物的排除。
凌晨5-7点,大肠的排毒,应上厕所排便。
凌晨7-9点,小肠大量吸收营养的时段,应吃早餐。
疗病者最好早吃,在6点半前,养生者在7点半前,
不吃早餐者应改变习惯,即使拖到9、10点吃都比不吃好。
半夜至凌晨4点为脊椎造血时段,必须熟睡,不宜熬夜。
经常听到很多女孩子有手提,不知道是听音乐感受不好还是听惯了,我觉得很差的音质,她们觉得很好听。可能是我觉得听不动吧,我建议用beoplayer:
丹麦的一家全球顶级数字产品设备提供商开发的媒体播放器,它美奂美仑并且独特高贵的产品设计理念,一直受到了很多地球人的追捧以及崇拜,并且号称在音质上是比foobar还好,感兴趣的朋友可以下载来玩玩
还有就是SRS:
(Sound Retrieval System)是由SRS研究所开发的、最具代表性的3D立体声技术。该技术的核心是可以利用2...是美国SRS实验室的最新音响科技专利,SRS的主功方向是双声道环绕,SRS( )是它的注册标志。 SRS技术的发明人是阿诺德
都有破解版本,个人觉得一般的声卡加上sony的耳塞或者好点的漫步者音响感觉很爽的。。。呵呵
丹麦的一家全球顶级数字产品设备提供商开发的媒体播放器,它美奂美仑并且独特高贵的产品设计理念,一直受到了很多地球人的追捧以及崇拜,并且号称在音质上是比foobar还好,感兴趣的朋友可以下载来玩玩
还有就是SRS:
(Sound Retrieval System)是由SRS研究所开发的、最具代表性的3D立体声技术。该技术的核心是可以利用2...是美国SRS实验室的最新音响科技专利,SRS的主功方向是双声道环绕,SRS( )是它的注册标志。 SRS技术的发明人是阿诺德
都有破解版本,个人觉得一般的声卡加上sony的耳塞或者好点的漫步者音响感觉很爽的。。。呵呵
费用项目 | 金额(元) | 费用项目 | 金额(元) | 费用项目 | 金额(元) |
月租费 通话费 国内漫游基本费 | 15.00 | 增值服务费 数据业务费 MMS通信费 | 0.30 | 代收费 其它 优惠项目 | |
费用合计 | 146.67 |
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;
}

我曾怀疑我 走在沙漠中
从不结果 无论种什么梦
才张开翅膀 风却便沉默
习惯伤痛能不能 算收获
庆幸的是我 一直没回头
终于发现 真的是有绿洲
每把汗流了 生命变的厚重
走出沮丧才看见 新宇宙
海阔天空 在勇敢以后
要拿执着 将命运的锁打破
冷漠的人
谢谢你们曾经看轻我
让我不低头 更精采的活
凌晨的窗口 失眠整夜以后
看着黎明 从云里抬起了头
日落是沉潜 日出是成熟
只要是光一定会 灿烂的
海阔天空 在勇敢以后
要拿执着 将命运的锁打破
冷漠的人
谢谢你们曾经看轻我
让我不低头 更精采的活
海阔天空 狂风暴雨以后
转过头 对旧心酸一笑而过
最懂我的人
谢谢一路默默的陪着我
让我拥有好故事可以说
看未来 一步步来了
每个人都不是孤立存在的,所有的人都有或多或少的关系,这些关系交织在一起,形成一张很大的关系网,整个社会就是这样的一个网络。对你来说,这张网以你为中心,伸展到所有你曾接触过的人,和所有你可能去的地方。
可以说,这张网是你自己亲手织就的,每一个你所结交的新朋友,每一次和朋友的相聚,都是在编 织与他人或疏或密的情感纽带,都是在扩展这张网。它在一定程度上决定了你未来的道路是平坦还是曲折,是有所谓的“贵人相助”,还是要自己苦苦奋斗。也许在平日的行动中,很难感觉到这张网的存在,但是每到关键时刻,它总会不失时机地显示自己的力量,或者令你大功告成,或者令你壮志难酬。
你想回顾过去在人际关系方面的得失吗?你了解自己编成的关系网对你是有利还是妨碍吗?下面的题会帮你测试一下。
请选择最适合自己情形的答案。
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,还有很多地方了
解的不够深刻,望广大读者多指教。