2009年6月11日星期四

linux下的数据同步

在数据日益重要,硬件日益廉价的今天,数据的备份就显得尤为重要。
然而数据备份有一个问题就是冗余,尤其对于视频、音频文件等。这个时候,就需要同步备份。
Linux下强大的同步工具-rsync闪亮出场。他能够自动判断文件是否变化,从而同步当前数据和备份数据。
首先安装
sudo apt-get install rsync
一条命令搞定
rsync -raz --progress --size-only --delete srcdir
server.example.com:/dstdir
参数说明:
-r:递归
-a:archive,保留文件的所有属性(权限等)
-z:压缩文件传输,但是增加cpu工作量。因为我备份到usb移动硬盘,贷款不是问题,故不用。
--progress:显示进度,我不需要
--size-only:通过文件大小判定文件是否改变,能够提高速度
--delete:如果本地删除了文件,备份数据同样删除。

系统安装和配置

笔记本型号是IBM X40。
下面是安装步骤:
第一步,备份数据!
第二步,安装系统,没什么好说的。
第三步,配置系统。(参见 http://wiki.ubuntu.org.cn/Qref/Hardy
加入中文源到/etc/apt/source.list
deb http://archive.ubuntu.org.cn/ubuntu-cn/ hardy main restricted
universe multiverse
更新系统
sudo apt-get update (更新源数据库,将中文源的安装包信息加入系统)
sudo apt-get upgrade (更新软件到最新版本)
添加中文支持
sudo apt-get install scim-qtimm (支持qt软件)
sudo vi /etc/X11/xinit/xinput.d/scim (replace xim with scim)
sudo update-alternatives --install /etc/X11/xinit/xinput.d/all_ALL
xinput-all_ALL /etc/X11/xinit/xinput.d/scim 30
sudo apt-get install xpdf-chinese-simplified xpdf-chinese-traditional
poppler-data (支持pdf中文等)
Thinkpad 相关配置
硬件都驱动起来了。只有forward, backward,
AccessIBM不能用。Fn+F3刚装上能用,后来又不能用了。
可以安装tsb,这样Fn功能有屏幕指示了,但是后来安装了xfce,冲突,只好卸载了。
禁用系统bell,免得烦人
sudo rmmod pcspkr (modprobe pcspkr重新启用)
在/etc/modprobe.d/blacklist里加入 blacklist pcspkr
UTF8 Encoding (xp采用gbk编码,在ubuntu下是乱码,需要转换)
sudo apt-get install convmv (encoding转换工具)
convmv -r -f cp936 -t utf8 --notest * (将文件名从gbk转换成utf8)
iconv -f gbk -t utf8 $file -o
$newfile(从windows拷贝过来的文本文件可能是乱码,将文件转换成utf8格式。)
批量转换脚本:(写成bash脚本,放到~/bin目录)
find dir -type d -exec mkdir -p utf8/{} \; (备份目录)
find dir -type f -exec iconv -f GBK -t UTF-8 {} -o utf8/{} \;
(转换各个文件)
mv utf8/* dir
rm -fr utf8
安装xfce桌面环境。(gnome很炫,尤其是3d,但是还是喜欢简洁一些的。Awesome更符合我的要求,但是一来屏幕小,二来没有Windows键,所以没有使用。)
sudo apt-get install xubuntu-desktop
更改 .config/Terminal/terminalrc
文件中xfce4-terminal的颜色。我喜欢白底黑字。
xfce4-terminal很好用,就是tab的边框有些宽。xterm显示中文不好看。
使用性感的登录界面
wget http://www.linuxidc.com/upload/2008_10/08101320217736.zip
unzip and install the gz file
参见http://www.linuxidc.com/Linux/2008-10/16567.htm

第四步,安装软件
安装开发环境
sudo apt-get install build-essential autoconf automake1.9 cvs subversion
(准备转用svn了)
sudo apt-get install sun-java6-jdk sun-java6-plugin
(安装java,不要用系统的)
sudo update-alternatives --config java (选择sun java)
sudo apt-get install eclipse eclipse-cdt eclipse-pydev
(安装java开发环境eclipse)
删除无用软件
sudo apt-get remove evolution evolution-webcal
(有gmail,evolution处处捣乱)
sudo apt-get remove thunderbird (类似道理)
安装系统软件
sudo apt-get install rar unrar p7zip-full cabextract (压缩软件)
sudo apt-get install xtightvncviewer (连接学校机器)
sudo apt-get install tofrodos (unix dos下文件格式互换)
sudo apt-get install xpdf (evince足够用了,不喜欢acroread)
sudo apt-get install gawk (系统默认只有awk,装matlab需要)
sudo apt-get install htop (比top更好用)
sudo apt-get install wine (运行windows软件)
安装多媒体软件
sudo apt-get install mplayer smplayer mplayer-fonts qt4-qtconfig
smplayer-themes (mplayer, smplayer播放器,全能。)
sudo apt-get install libdvdcss2 libxine1-ffmpeg libxine1-plugins w32codecs
(视频解码器)
sudo apt-get install gstreamer0.10-plugins-ugly gstreamer0.10-ffmpeg
gstreamer0.10-fluendo-mpegdemux gstreamer0.10-gnonlin (音频解码器)
sudo --purge remove totem-mozilla (删除默认的totem插件,不喜欢totem)
sudo apt-get install mozilla-mplayer(使用mplayer的插件)
sudo apt-get install amarok
(Linux版本的itune,可以在线听。不过我喜欢自己管理音乐列表,用mplayer播放)
sudo apt-get install digkam (图片管理器)
sudo apt-get remove f-spot (删除默认的图片管理器,导入大量图片时候crash)
安装网络软件
sudo apt-get install filezilla (最好的ftp客户端)
sudo apt-get install opera (速度比firefox快)
sudo apt-get install amule (电驴)
安装sopcast (Linux下的ppstream) (参见
http://www.blog.hyperend.info/installing-sopcast-on-ubuntu/
wget http://download.sopcast.cn/download/sp-auth.tgz
tar zxvf sp-auth.tgz
cd sp-auth
sudo cp sp-sc-auth /usr/bin/sp-sc (安装sopcast)
wget http://linuxtoy.org/files/deb/gtk-sopcast_0.2.8-1_i386.deb
双击deb包安装sopcast的gui界面
安装生活软件
sudo apt-get install gnucash (管好你的钱袋)
sudo apt-get install kaddressbook (和好朋友保持联系)
sudo apt-get install KOrganizer (日历软件,用google?)
sudo apt-get install googleearth (看看地球)
sudo apt-get install stellarium (还有头上的天空)

系统安装和配置

笔记本型号是IBM X40。
下面是安装步骤:
第一步,备份数据!
第二步,安装系统,没什么好说的。
第三步,配置系统。(参见 http://wiki.ubuntu.org.cn/Qref/Hardy
加入中文源到/etc/apt/source.list
deb http://archive.ubuntu.org.cn/ubuntu-cn/ hardy main restricted
universe multiverse
更新系统
sudo apt-get update (更新源数据库,将中文源的安装包信息加入系统)
sudo apt-get upgrade (更新软件到最新版本)
添加中文支持
sudo apt-get install scim-qtimm (支持qt软件)
sudo vi /etc/X11/xinit/xinput.d/scim (replace xim with scim)
sudo update-alternatives --install /etc/X11/xinit/xinput.d/all_ALL
xinput-all_ALL /etc/X11/xinit/xinput.d/scim 30
sudo apt-get install xpdf-chinese-simplified xpdf-chinese-traditional
poppler-data (支持pdf中文等)
Thinkpad 相关配置
硬件都驱动起来了。只有forward, backward,
AccessIBM不能用。Fn+F3刚装上能用,后来又不能用了。
可以安装tsb,这样Fn功能有屏幕指示了,但是后来安装了xfce,冲突,只好卸载了。
禁用系统bell,免得烦人
sudo rmmod pcspkr (modprobe pcspkr重新启用)
在/etc/modprobe.d/blacklist里加入 blacklist pcspkr
UTF8 Encoding (xp采用gbk编码,在ubuntu下是乱码,需要转换)
sudo apt-get install convmv (encoding转换工具)
convmv -r -f cp936 -t utf8 --notest * (将文件名从gbk转换成utf8)
iconv -f gbk -t utf8 $file -o
$newfile(从windows拷贝过来的文本文件可能是乱码,将文件转换成utf8格式。)
批量转换脚本:(写成bash脚本,放到~/bin目录)
find dir -type d -exec mkdir -p utf8/{} \; (备份目录)
find dir -type f -exec iconv -f GBK -t UTF-8 {} -o utf8/{} \;
(转换各个文件)
mv utf8/* dir
rm -fr utf8
安装xfce桌面环境。(gnome很炫,尤其是3d,但是还是喜欢简洁一些的。Awesome更符合我的要求,但是一来屏幕小,二来没有Windows键,所以没有使用。)
sudo apt-get install xubuntu-desktop
更改 .config/Terminal/terminalrc
文件中xfce4-terminal的颜色。我喜欢白底黑字。
xfce4-terminal很好用,就是tab的边框有些宽。xterm显示中文不好看。
使用性感的登录界面
wget http://www.linuxidc.com/upload/2008_10/08101320217736.zip
unzip and install the gz file
参见http://www.linuxidc.com/Linux/2008-10/16567.htm

第四步,安装软件
安装开发环境
sudo apt-get install build-essential autoconf automake1.9 cvs subversion
(准备转用svn了)
sudo apt-get install sun-java6-jdk sun-java6-plugin
(安装java,不要用系统的)
sudo update-alternatives --config java (选择sun java)
sudo apt-get install eclipse eclipse-cdt eclipse-pydev
(安装java开发环境eclipse)
删除无用软件
sudo apt-get remove evolution evolution-webcal
(有gmail,evolution处处捣乱)
sudo apt-get remove thunderbird (类似道理)
安装系统软件
sudo apt-get install rar unrar p7zip-full cabextract (压缩软件)
sudo apt-get install xtightvncviewer (连接学校机器)
sudo apt-get install tofrodos (unix dos下文件格式互换)
sudo apt-get install xpdf (evince足够用了,不喜欢acroread)
sudo apt-get install gawk (系统默认只有awk,装matlab需要)
sudo apt-get install htop (比top更好用)
sudo apt-get install wine (运行windows软件)
安装多媒体软件
sudo apt-get install mplayer smplayer mplayer-fonts qt4-qtconfig
smplayer-themes (mplayer, smplayer播放器,全能。)
sudo apt-get install libdvdcss2 libxine1-ffmpeg libxine1-plugins w32codecs
(视频解码器)
sudo apt-get install gstreamer0.10-plugins-ugly gstreamer0.10-ffmpeg
gstreamer0.10-fluendo-mpegdemux gstreamer0.10-gnonlin (音频解码器)
sudo --purge remove totem-mozilla (删除默认的totem插件,不喜欢totem)
sudo apt-get install mozilla-mplayer(使用mplayer的插件)
sudo apt-get install amarok
(Linux版本的itune,可以在线听。不过我喜欢自己管理音乐列表,用mplayer播放)
sudo apt-get install digkam (图片管理器)
sudo apt-get remove f-spot (删除默认的图片管理器,导入大量图片时候crash)
安装网络软件
sudo apt-get install filezilla (最好的ftp客户端)
sudo apt-get install opera (速度比firefox快)
sudo apt-get install amule (电驴)
安装sopcast (Linux下的ppstream) (参见
http://www.blog.hyperend.info/installing-sopcast-on-ubuntu/
wget http://download.sopcast.cn/download/sp-auth.tgz
tar zxvf sp-auth.tgz
cd sp-auth
sudo cp sp-sc-auth /usr/bin/sp-sc (安装sopcast)
wget http://linuxtoy.org/files/deb/gtk-sopcast_0.2.8-1_i386.deb
双击deb包安装sopcast的gui界面
安装生活软件
sudo apt-get install gnucash (管好你的钱袋)
sudo apt-get install kaddressbook (和好朋友保持联系)
sudo apt-get install KOrganizer (日历软件,用google?)
sudo apt-get install googleearth (看看地球)
sudo apt-get install stellarium (还有头上的天空)

人生若只如初见

▷木兰辞 拟古决绝词柬友
纳兰性德

人生若只如初见,
何事秋风悲画扇。
等闲变却故人心,
却道故人心易变。
骊山语罢清宵半,
夜雨霖铃终不怨。
何如薄幸锦衣郎,
比翼连枝当日愿。

人生若只如初见:意思是说与意中人相处应当总像刚刚相识的时候,那样地甜蜜,那样地温馨,那样地深情和快乐。
何事秋风悲画扇:此用汉班婕妤被弃典故。班婕妤为汉成帝妃,被赵飞燕谗害,退居冷宫,后有诗《怨歌行》,以秋扇为喻抒发被弃之怨情。南北朝梁刘孝绰《班婕妤怨》诗又点明"妾身似秋扇",后遂以秋扇见捐喻女子被弃。这里是说本应当相亲相爱,但却成了今日的相离相弃。
等闲变却故人心,却道故人心易变:意思是说如今轻易地变了心,却反而说情人间就是容易变心的。故人,指情人。
骊山语罢清宵半,夜雨霖铃终不怨:《太真外传》载,唐明皇与杨玉环曾于七月七日夜,在骊山华清宫长生殿里盟誓,愿世世为夫妻。白居易《长恨歌》:"在天愿作比翼鸟,在地愿作连理枝。"对此作了生动的描写。后安史乱起,明皇入蜀,于马嵬坡赐死杨玉环。杨死前云:"妾诚负国恩,死无恨矣。"又,明皇此后于途中闻雨声、铃声而悲伤,遂作《雨霖铃》曲以寄哀思。这里借用此典说即使是最后作决绝之别,也不生怨。
何如薄幸锦衣郎,比翼连枝当日愿:化用唐李商隐《马嵬》:"如何四纪为天子,不及卢家有莫愁"之句意。薄幸,薄情。锦衣郎,指唐明皇。又,意谓怎比得上当年的唐明皇呢,他总还是与杨玉环有过比翼鸟、连理枝的誓愿!意思是纵死而分离,也还是刻骨地念念不忘旧情。
连起来就是:与意中人相处应当总像刚刚相识的时候,是那样的甜蜜,那样的温馨,那样的深情和快乐。但你我本应当相亲相爱,却为何成了今日的相离相弃?如今轻易地变了心,你却反而说情人间就是容易变心的。我与你就像唐明皇与杨玉环那样,在长生殿起过生死不相离的誓言,却又最终作决绝之别,也不生怨。但你又怎比得上当年的唐明皇呢,他总还是与杨玉环有过比翼鸟、连理枝的誓愿。

2009年6月10日星期三

linux select 解释

select系统调用是用来让我们的程序监视多个文件句柄(file
descrīptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。

文件在句柄在Linux里
很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man
socket可以看到"On success, a file descrīptor for the new socket is
returned."而man 2 open可以看到"open() and creat() return the new file
descrīptor",其实文件句柄就是一个整数,看socket函数的声明就明白了:
int socket(int domain, int type, int protocol);
当然,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE
*结构的表示就是stdin、stdout、stderr,0就是stdin,1就是stdout,2就是stderr。
比如下面这两段代码都是从标准输入读入9个字节字符:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
char buf[10] = "";
read(0, buf, 9); /* 从标准输入 0 读入字符 */
fprintf(stdout, "%s\n", buf); /* 向标准输出 stdout 写字符 */
return 0;
}
/* **上面和下面的代码都可以用来从标准输入读用户输入的9个字符** */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
char buf[10] = "";
fread(buf, 9, 1, stdin); /* 从标准输入 stdin 读入字符 */
write(1, buf, strlen(buf));
return 0;
}

继续上面说的select,就是用来监视某个或某些句柄的状态变化的。select函数原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
函数的最后一个参数timeout显然是一个超时时间值,其类型是struct timeval
*,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct
timeval tv;然后把变量tv的地址&tv传递给select函数。struct timeval结构如下:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};

第2、3、4三个参数是一样的类型: fd_set
*,即我们在程序里要申明几个fd_set类型的变量,比如rdfds, wtfds,
exfds,然后把这个变量的地址&rdfds, &wtfds, &exfds
传递给select函数。这三个参数都是一个句柄的集合,第一个rdfds是用来保存这样的句柄的:当句柄的状态变成可读的时系统就会告诉select函
数返回,同理第二个wtfds是指有句柄状态变成可写的时系统就会告诉select函数返回,同理第三个参数exfds是特殊情况,即句柄上有特殊情况发
生时系统会告诉select函数返回。特殊情况比如对方通过一个socket句柄发来了紧急数据。如果我们程序里只想检测某个socket是否有数据可
读,我们可以这样:fd_set rdfds; /* 先申明一个 fd_set
集合来保存我们要检测的 socket句柄 */
struct timeval tv; /* 申明一个时间变量来保存时间 */
int ret; /* 保存返回值 */
FD_ZERO(&rdfds); /* 用select函数之前先把集合清零 */
FD_SET(socket, &rdfds); /* 把要检测的句柄socket加入到集合里 */
tv.tv_sec = 1;
tv.tv_usec = 500; /* 设置select等待的最大时间为1秒加500毫秒 */
ret = select(socket + 1, &rdfds, NULL, NULL, &tv); /*
检测我们上面设置到集合rdfds里的句柄是否有可读信息 */
if(ret < 0) perror("select");/* 这说明select函数出错 */
else if(ret == 0) printf("超时\n"); /*
说明在我们设定的时间值1秒加500毫秒的时间内,socket的状态没有发生变化 */
else { /* 说明等待时间还未到1秒加500毫秒,socket的状态发生了变化 */
printf("ret=%d\n", ret); /*
ret这个返回值记录了发生状态变化的句柄的数目,由于我们只监视了socket这一个句柄,所以这里一定ret=1,如果同时有多个句柄发生变化返回的
就是句柄的总和了 */
/*
这里我们就应该从socket这个句柄里读取数据了,因为select函数已经告诉我们这个句柄里有数据可读
*/
if(FD_ISSET(socket, &rdfds)) { /*
先判断一下socket这外被监视的句柄是否真的变成可读的了 */
/* 读取socket句柄里的数据 */
recv(...);
}
}

注意select函数的第一个参数,是所有加入集合的句柄值的最大那个值还要加1。比如我们创建了3个句柄:
int sa, sb, sc;
sa = socket(...); /* 分别创建3个句柄并连接到服务器上 */
connect(sa,...);
sb = socket(...);
connect(sb,...);
sc = socket(...);
connect(sc,...);

FD_SET(sa, &rdfds);/* 分别把3个句柄加入读监视集合里去 */
FD_SET(sb, &rdfds);
FD_SET(sc, &rdfds);

在使用select函数之前,一定要找到3个句柄中的最大值是哪个,我们一般定义一个变量来保存最大值,取得最大socket值如下:int
maxfd = 0;
if(sa > maxfd) maxfd = sa;
if(sb > maxfd) maxfd = sb;
if(sc > maxfd) maxfd = sc;

然后调用select函数:ret = select(maxfd + 1, &rdfds, NULL, NULL, &tv); /*
注意是最大值还要加1 */

同样的道理,如果我们要检测用户是否按了键盘进行输入,我们就应该把标准输入0这个句柄放到select里来检测,如下:
FD_ZERO(&rdfds);
FD_SET(0, &rdfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select(1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */
if(ret < 0) perror("select");/* 出错 */
else if(ret == 0) printf("超时\n"); /*
在我们设定的时间tv内,用户没有按键盘 */
else { /* 用户有按键盘,要读取用户的输入 */
scanf("%s", buf);
}

如何设置socket的Connect超时

1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数
2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。
3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set
wset)中将当前套接口置位(用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct
timeval *timeout)
4.调用select( socket, &rset, &wset, NULL, timeout )
返回0表示connect超时
如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。

网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注:
select函数并不是只用于套接口的,但是套接口编程中用的比较多),在connect到目标主机的时候,这个超时是不由我们来设置的。不过正常情况下这个超时都很长,并且connect又是一个阻塞方法,一个主机不能连接,等着connect返回还能忍受,你的程序要是要试图连接多个主机,恐怕遇到多个不能连接的主机的时候,会塞得你受不了的。我也废话少说,先说说我的方法,如果你觉得你已掌握这种方法,你就不用再看下去了,如果你还不了解,我愿意与你分享。本文是已在Linux下的程序为例子,不过拿到Windows中方法也是一样,无非是换几个函数名字罢了。
Linux中要给connect设置超时,应该是有两种方法的。一种是该系统的一些参数,这个方法我不讲,因为我讲不清楚:P,它也不是编程实现的。另外一种方法就是变相的实现connect的超时,我要讲的就是这个方法,原理上是这样的:
1.建立socket
2.将该socket设置为非阻塞模式
3.调用connect()
4.使用select()检查该socket描述符是否可写(注意,是可写)
5.根据select()返回的结果判断connect()结果
6.将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。
/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect超时时间20秒
int main(int argc , char **argv)
{
………………
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以服务器地址填充结构serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) ==
-1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
{
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if(error == 0) ret = true;
else ret = false;
} else ret = false;
}
else ret = true;
ul = 0;
ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式
if(!ret)
{
close( sockfd );
fprintf(stderr , "Cannot Connect the server!n");
return;
}
fprintf( stderr , "Connected!n");
//下面还可以进行发包收包操作
……………
}

以上代码片段,仅供参考,也是为初学者提供一些提示,主要用到的几个函数,select,
ioctl,
getsockopt都可以找到相关资料,具体用法我这里就不赘述了,你只需要在linux中轻轻的敲一个man
<函数名>就能够看到它的用法。
此外我需要说明的几点是,虽然我们用ioctl把套接口设置为非阻塞模式,不过select本身是阻塞的,阻塞的时间就是其超时的时间由调用select
的时候的最后一个参数timeval类型的变量指针指向的timeval结构变量来决定的,timeval结构由一个表示秒数的和一个表示微秒数(long类型)的成员组成,一般我们设置了秒数就行了,把微妙数设为0(注:1秒等于100万微秒)。而select函数另一个值得一提的参数就是上面我们用到的fd_set类型的变量指针。调用之前,这个变量里面存了要用select来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏FD_ISSET来检查某个描述符是否在其中。由于我这里只有一个套接口描述符,我就没有使用FD_ISSET宏来检查调用select之后这个sockfd是否在set里面,其实是需要加上这个判断的。不过我用了getsockopt来检查,这样才可以判断出这个套接口是否是真的连接上了,因为我们只是变相的用select来检查它是否连接上了,实际上select检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:
1)套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者i)套接口已连接,或者ii)套接口不要求连接(UDP方式的)
2)连接的写这一半关闭。
3)有一个套接口错误待处理。
这样,我们就需要用getsockopt函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,当然我程序中并没有标出那么多状态,只是简单的表示可连接/不可连接。
下面我来谈谈对这个程序测试的结果。我针对3种情形做了测试:
1. 目标机器网络正常的情况
可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。
2. 目标机器网络断开的情况
在等待设置的超时时间(上面的程序中为20秒)后,显示目标主机不能连接。
3. 程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络
在恢复目标主机网络连接之前,程序一只等待,恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。
以上各种情况的测试结果表明,这种设置connect超时的方法是完全可行的。我自己是把这种设置了超时的connect封装到了自己的类库,用在一套监控系统中,到目前为止,运行还算正常。这种编程实现的connect超时比起修改系统参数的那种方法的有点就在于它只用于你的程序之中而不影响系统。

linux中errno使用

当linux中的C api函数发生异常时,一般会将errno变量(需include
errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因,在实际编程中用这一招解决了不少原本看来莫名其妙的问题。但是errno是一个数字,代表的具体含义还要到errno.h中去阅读宏定义,而每次查阅是一件很繁琐的事情。有下面几种方法可以方便的得到错误信息
(1)void perror(const char *s)
函数说明
perror ( )用来将上一个函数发生错误的原因输出到标准错误(stderr),参数s
所指的字符串会先打印出,后面再加上错误原因 字符串。此错误原因依照全局变量
errno 的值来决定要输出的字符串。
(2) char *strerror(int errno)
将错误代码转换为字符串错误信息,可以将该字符串和其它的信息组合输出到用户界面例如
fprintf(stderr,"error in CreateProcess %s, Process ID %d
",strerror(errno),processID)
注:假设processID是一个已经获取了的整形ID
(3)printf("%m", errno);
另外不是所有的地方发生错误的时候都可以通过error获取错误代码,例如下面的代码段

#include"stdio.h"
#include "stdlib.h"
#include "errno.h"
#include "netdb.h"
#include "sys/types.h"
#include "netinet/in.h"
int main (int argc, char *argv[])
{
struct hostent *h;
if (argc != 2)
{
fprintf (stderr ,"usage: getip address\n");
exit(1);
}

if((h=gethostbyname(argv[1])) == NULL)
{

herror("gethostbyname");
exit(1);
}

printf("Host name : %s\n", h->h_name);
printf("IP Address : %s\n", inet_ntoa (*((struct in_addr *)h->h_addr)));
return 0;
}


通过上面的代码可以看到:使用gethostbyname()函数,你不能使用perror()来输出错误信息(因为错误代码存储在
h_errno 中而不是errno 中。所以,你需要调用herror()函数。
你简单的传给gethostbyname()
一个机器名("bbs.tsinghua.edu.cn"),然后就从返回的结构struct hostent
中得到了IP 等其他信息.程序中输出IP 地址的程序需要解释一下:h->h_addr
是一个char*,但是inet_ntoa()函数需要传递的是一个struct in_addr
结构。所以上面将h->h_addr 强制转换为struct
in_addr*,然后通过它得到了所有数据。