明天晴天

^-^『把天涂成蓝色调』^-^

ntohs, ntohl, htons,htonl的比较

October22

最近的工作是进行程序由 SUN SPARC 向 Intel X86 移植的工作,多数问题都出现在网络字节序的部分,所以拿出来在这部分工作中使用频率较高的几个接口区分比较下:)

ntohs =net to host short int 16位

htons=host to net short int 16位

ntohl =net to host long int 32位

htonl=host to net   long int   32位

ntohs 简述:

将一个无符号短整形数从网络字节顺序转换为主机字节顺序。
    #include
    u_short PASCAL FAR ntohs( u_short netshort);
    netshort:一个以网络字节顺序表达的16位数。
注释:
    本函数将一个16位数由网络字节顺序转换为主机字节顺序。
返回值:ntohs()返回一个以主机字节顺序表达的数。

htons 简述:

将主机的无符号短整形数转换成网络字节顺序。
    #include
    u_short PASCAL FAR htons( u_short hostshort);
    hostshort:主机字节顺序表达的16位数。
注释:
    本函数将一个16位数从主机字节顺序转换成网络字节顺序。
返回值: htons()返回一个网络字节顺序的值。

=================================================

这2个函数提供了主机字节顺序与网络字节顺序的转换

比如网络字节 为 00 01

u_short    a;如何直接对应的话    a=0100; 为什么呢?因为主机是从高字节到低字节的,所以应该转化后

a=ntohs(0001); 这样 a=0001;

首先,假设你已经有了一个sockaddr_in结构体ina,你有一个IP地址”132.241.5.10″ 要储存在其中,你就要用到函数inet_addr(),将IP地址从 点数格式转换成无符号长整型。使用方法如下:
ina.sin_addr.s_addr = inet_addr(“132.241.5.10″);
注意,inet_addr()返回的地址已经是网络字节格式,所以你无需再调用 函数htonl()。
我们现在发现上面的代码片断不是十分完整的,因为它没有错误检查。 显而易见,当inet_addr()发生错误时返回-1。记住这些二进制数字?(无符 号数)-1仅和IP地址255.255.255.255相符合!这可是广播地址!大错特 错!记住要先进行错误检查。
好了,现在你可以将IP地址转换成长整型了。有没有其相反的方法呢? 它可以将一个in_addr结构体输出成点数格式?这样的话,你就要用到函数 inet_ntoa()(“ntoa”的含义是”network to ascii”),就像这样:
printf(“%s”,inet_ntoa(ina.sin_addr));
它将输出IP地址。需要注意的是inet_ntoa()将结构体in-addr作为一 个参数,不是长整形。同样需要注意的是它返回的是一个指向一个字符的 指针。它是一个由inet_ntoa()控制的静态的固定的指针,所以每次调用 inet_ntoa(),它就将覆盖上次调用时所得的IP地址。例如:
char *a1, *a2;
a1 = inet_ntoa(ina1.sin_addr); /* 这是198.92.129.1 */
a2 = inet_ntoa(ina2.sin_addr); /* 这是132.241.5.10 */
printf(“address 1: %s “,a1);
printf(“address 2: %s “,a2);
输出如下:
address 1: 132.241.5.10
address 2: 132.241.5.10
假如你需要保存这个IP地址,使用strcopy()函数来指向你自己的字符指针。

==================================================

htonl()表示将32位的主机字节顺序转化为32位的网络字节顺序 htons()表示将16位的主机字节顺序转化为16位的网络字节顺序(ip地址是32位的端口号是16位的 )

inet_ntoa()
简述:
    将网络地址转换成“.”点隔的字符串格式。

    #include

    char FAR* PASCAL FAR inet_ntoa( struct in_addr in);

    in:一个表示Internet主机地址的结构。

注释:
    本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设 该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。

返回值:
    若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NVLL。其中的数据应在下一个WINDOWS套接口调用前复制出来。

参见:
    inet_addr().

测试代码如下
include
#include
#include
#include
#include
int main(int aargc, char* argv[])
{
         struct in_addr addr1,addr2;
         ulong   l1,l2;
         l1= inet_addr(“192.168.0.74″);
         l2 = inet_addr(“211.100.21.179″);
         memcpy(&addr1, &l1, 4);
         memcpy(&addr2, &l2, 4);
         printf(“%s : %s “, inet_ntoa(addr1), inet_ntoa(addr2));    //注意这一句的运行结果
         printf(“%s “, inet_ntoa(addr1));
         printf(“%s “, inet_ntoa(addr2));
         return 0;
}
实际运行结果如下:
192.168.0.74 : 192.168.0.74       //从这里可以看出,printf里的inet_ntoa只运行了一次。
192.168.0.74
211.100.21.179

inet_ntoa返回一个char *,而这个char *的空间是在inet_ntoa里面静态分配的,所以inet_ntoa后面的调用会覆盖上一次的调用。第一句printf的结果只能说明在printf里面的可变参数的求值是从右到左的,仅此而已。

部分内容来自:blog.chinaunix.net/

SVN 使用 Repositery Browser 打标签

February10

  我得blog似乎真的成了月季,不是玫瑰花…这笑话真寒 -_-!

  最近白天每天都要跟bug作斗争,gdb技术倒是娴熟了不少,以后debug要逼迫自己不添加打印信息而是使用gdb ^_^

  今天用SVN打标签,发觉本地windows上的代码似乎不是要打标签的代码的全部,所以也就不能用 Branch/Tag 选项了,因为这个功能的 “From WC at URL”是不能更改的,这样如果你要打标签的代码只有部分在你的windows上,就糟糕了,你所打的tag必然会有部分代码缺失…

  不过也不用很麻烦的去把你要打标签的所有代码都co到本地,你可以使用 Repositery Browser 功能。

  步骤如下:

  1、SVN 右键选择 Repositery Browser,这里你可以看到版本库中所有的文件夹
  2、选择你要打标签的文件夹
  3、右键选择 copy to… 选项
  4、在 New Name 输入框中输入你要打的新标签的名字
  5、确认后OK,恭喜新标签生成!

  注:对于版本名称要求比较规范的小平友们,可以在第2步之前到 tags 的文件夹下选择一个类似的tag name的URL,这样在步骤4的时候就便于统一啦o(∩_∩)o…

  好啦,嘿嘿,又不知道今天晚上吃啥呢!哼哼~~继续debug~~晕乎乎~~

没有服务器的程序员

September23

  今天公司的数据库被搞挂了,所以我的单元测试也无法进行了…

  不过利用今天一天的时间对所理解的Common A**做了一边回顾,发现经过这次的程序调试,自己居然对C A 的认识有了一个质的飞跃哦!基本上对MSG的流程有了一个清晰的思路,对多进程编程也有了很多的了解。总得来说,还是很有成就感的啦!o(∩_∩)o…哈哈

  对于一个1W+行的code,附加复杂的业务流程,经过单元测试,再调试,对于一个新手儿来说无疑是受益匪浅啊!导师说,其实我这个任务,不能简单的说成完全的单元测试,而应该是程序的开发,并行单元测试。^_^让我很有面子的说法咧~

  昨天买了双红色漆皮的路伴,恩,个人还是很喜欢的啦,红色拉带,还有手工皮花,看起来很卡的感觉,hoho~感觉就像是小时候穿的那种小皮鞋一样,圆圆的小头,有可爱的带子:)

  这周要上7天的班咧,加油,加油,还有股市加油,加油,争取7天涨停,恩,对!!这样才能弥补广大人民的伤痛啊~~

与BUG做斗争的日子很充实啊~

September18

  看来以后我都是阶段性的写写blog了…哎~

  最近一直都沉浸在与BUG的明争暗斗中,昨天下午发现了个很阴险的bug,系统既不报错,也不出core,就是在那傻傻的wait…我这个汗啊~

  调吧,按着设计思路一点一点查,转眼到了今天,早上继续查,迷迷糊糊的就感觉不对劲,再仔细一看,我都从我的功能模块查到了别的模块里面了…囧…

  这可怎么办啊,就是什么也没发现啊~跟踪调试吧,一条一条的打印,管他什么过程的,所有函数都加上debug_msg,一个来回下来,就只见满屏的log啊…慢慢看吧,突然在不打眼的地方发现了一个状态值很诡异的从2转变成了0,让我这个乐和啊,乐和~可再一看,这两个函数动作之间好像没有其它行为了啊…再囧…

  找来牛人问,寻思着,我也不能老师闭门造车啊,向先进文化学习吧,没想到,牛人说,“恩,很奇怪,很奇怪,真的没有动作了啊,怎么就变了呢?”还满脸狐疑的看着我…我只能遥遥头,说:恩,真奇怪,真奇怪…

  后来干脆,不调了,把程序重头到尾重新屡屡思路,看看能不能像 柯南,包拯 啥的,不经意之间发现了真相!可惜结果很令我失望啊….

  哎~看来这无尽的叹息只能交给明天的太阳了~

我是程序员,不论男女~

September9

  也不知道为什么,今天的情绪特别的低落~

  可能是不喜欢被别人落在后面的感觉…有的时候女生就是有失落的特权吧,很庆幸自己是一个女生,这样我可以给自己一个理由,告诉自己不要太为难自己~

  然而,在一个竞争激烈的年代,一个日益发展的IT行业,谁又会在乎你是男是女呢?不过就是一个code的机器,还有什么性别而言呢?所以,在这样一个行业,我又记恨我是一个女孩,因为在世俗的眼光下,你就是不如男生的逻辑力强,人们就是这样,看轻你,却又允许你落后…这也许就是女程序员的悲哀,也许注定这就是一个错误的选择,可是已经没有了退路,只能硬着头皮向前冲~

  不把自己当女人看,不把自己当人看,放弃现在的生活习惯,完全忘记自己,重新开始,我是不是才会成为一个合格的程序员呢?

  至少我要试着努力的做一做,不过给自己一个期限,一年以后的我如果还是没有成为真正合格的程序员,也许那时,我已经疯狂的喜爱这个职业,也许那时……

  还有,谢谢昨天一同座公交车的很阳光的GG,也许这是我们生平唯一的相遇,可是你的话却让我的心情一下子明亮了许多,谢谢你,同样的话也送给你“能和你一同,是我今生的荣幸!”

看别人的程序简直是种折磨啊~

August14

  300多KL的代码啊,没有命名对照表,体系结构设计文档也不完全,我就硬着头皮,耗着心血,一行一行的读,嘟啊嘟~函数的引用,居然可恶到这种程度!就像个万丈深渊,让我看不到边际~

  谁来帮帮我啊~我要昏迷啦!!!

Linux C编程—网络编程 client-server

April3

网络编程,一定离不开套接口;那什么是套接口呢?在Linux下,所有的I/O操作都是通过读写文件描述符而产生的,文件描述符是一个和打开的文件相关联的整数,这个文件并不只包括真正存储在磁盘上的文件,还包括一个网络连接、一个命名管道、一个终端等,而套接口就是系统进程和文件描述符通信的一种方法。目前最常用的套接口是字:字节流套接口(基于TCP)和数据报套接口(基于UDP),当然还有原始套接口(原始套接口提供TCP套接口和UDP套接口所不提供的功能,如构造自己的TCP或UDP分组)等,我们这里主要介绍字节流套接口和数据报套接口。
要学习网络编程,一定离不开网络库的函数,在Linux系统下,可以用"man 函数名"来得到这个函数的帮助,不过为了照顾E文不大好的朋友,下面就将常用的网络函数和用法列出来供大家参考:
1、socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。
————————————————————————————-
#include
#include                           
int socket(int family,int type,int protocol);                
返回:非负描述字---成功   -1---失败
————————————————————————————-
第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。

2、connect函数:当用socket建立了套接口后,可以调用connect为这个套接字指明远程端的地址;如果是字节流套接口,connect就使用三次握手建立一个连接;如果是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据。
————————————————————————————-
#include
#include                           
int connect(int sockfd,const struct sockaddr * serv_addr,int addrlen);  
返回:0---成功   -1---失败
————————————————————————————-
第一个参数是socket函数返回的套接口描述字;第二和第三个参数分别是一个指向套接口地址结构的指针和该结构的大小。
这些地址结构的名字均已“sockaddr_”开头,并以对应每个协议族的唯一后缀结束。以IPv4套接口地址结构为例,它以“sockaddr_in”命名,定义在头文件;以下是结构体的内容:
————————————————————————————-
struct in_addr {
unsigned long s_addr;      /* IPv4地址 */
};              
struct sockaddr_in {
short int sin_family; /* 套接口地址结构的地址簇,这里为AF_INET */
unsigned short int sin_port; /* TCP或UDP端口 */
struct in_addr sin_addr;/*存放ip地址的结构*/
unsigned char sin_zero; /* 无符号的8位整数 */
};              
————————————————————————————-

3、bind函数:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。
————————————————————————————-
#include
#include            
int bind(int sockfd,const struct sockaddr *myaddr,int addrlen);
返回:0---成功   -1---失败 
————————————————————————————-
第一个参数是socket函数返回的套接口描述字;第二和第第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。

4、listen函数:listen函数仅被TCP服务器调用,它的作用是将用sock创建的主动套接口转换成被动套接口,并等待来自客户端的连接请求。
————————————————————————————-
#include
#include       
int listen(int sockfd,int backlog);   
返回:0---成功   -1---失败
————————————————————————————-
  第一个参数是socket函数返回的套接口描述字;第二个参数规定了内核为此套接口排队的最大连接个数。由于listen函数第二个参数的原因,内核要维护两个队列:以完成连接队列和未完成连接队列。未完成队列中存放的是TCP连接的三路握手为完成的连接,accept函数是从以连接队列中取连接返回给进程;当以连接队列为空时,进程将进入睡眠状态。

5、accept函数:accept函数由TCP服务器调用,从已完成连接队列头返回一个已完成连接,如果完成连接队列为空,则进程进入睡眠状态。
————————————————————————————-
#include
#include      
int accept(int sockfd,struct sockaddr *iaddr,int *addrlen);     
返回:非负描述字---成功   -1---失败
————————————————————————————-
第一个参数是socket函数返回的套接口描述字;第二个和第三个参数分别是一个指向连接方的套接口地址结构和该地址结构的长度;该函数返回的是一个全新的套接口描述字;如果对客户段的信息不感兴趣,可以将第二和第三个参数置为空。

6、inet_pton函数:将点分十进制串转换成网络字节序二进制值,此函数对IPv4地址和IPv6地址都能处理。
————————————————————————————-
#include                          
int inet_pton(int family,const char * strptr,void * addrptr);        
返回:1---成功   0---输入不是有效的表达格式   -1---失败
————————————————————————————-
第一个参数可以是AF_INET或AF_INET6:第二个参数是一个指向点分十进制串的指针:第三个参数是一个指向转换后的网络字节序的二进制值的指针。

7、inet_ntop函数:和inet_pton函数正好相反,inet_ntop函数是将网络字节序二进制值转换成点分十进制串。
————————————————————————————-
#include  
const char * inet_ntop(int family,const void * addrptr,char * strptr,size_t len);  
返回:指向结果的指针---成功   NULL---失败
————————————————————————————-
第一个参数可以是AF_INET或AF_INET6:第二个参数是一个指向网络字节序的二进制值的指针;第三个参数是一个指向转换后的点分十进制串的指针;第四个参数是目标的大小,以免函数溢出其调用者的缓冲区。

8、fock函数:在网络服务器中,一个服务端口可以允许一定数量的客户端同时连接,这时单进程是不可能实现的,而fock就分配一个子进程和客户端会话,当然,这只是fock的一个典型应用。
————————————————————————————-
#include  
pid_t fock(void);                                 返回:在子进程中为0,在父进程中为子进程ID   -1---失败
————————————————————————————-
fock函数调用后返回两次,父进程返回子进程ID,子进程返回0。

有了上面的基础知识,我们就可以进一步了解TCP套接口和UDP套接口

1、TCP套接口
  TCP套接口使用TCP建立连接,建立一个TCP连接需要三次握手,基本过程是服务器先建立一个套接口并等待客户端的连接请求;当客户端调用connect进行主动连接请求时,客户端TCP发送一个SYN,告诉服务器客户端将在连接中发送的数据的初始序列号;当服务器收到这个SYN后也给客户端发一个SYN,里面包含了服务器将在同一连接中发送的数据的初始序列号;最后客户在确认服务器发的SYN。到此为止,一个TCP连接被建立。
  下面就用一个例子来说明服务器和客户是怎么连接的
————————————————————————————-
/* client.c */
#include
#include
#include
#include
#include
#include /*struct sockaddr_in 结构定义在这里*/
#include
#include
#include
#include
#define PORT 3049 /*服务器端口监听号*/

void doclient(FILE *fp,int sockfd);
main(int argc,char*argv[])
{
int sockfd,n;
struct hostent *he;/*???????*/
struct sockaddr_in server_addr;/*connector's address*/
if (argc!=2)
{
fprintf(stderr,"usage:client hostname \n");
exit(1);
}

printf("gethostbyname begin\n");
if ((he =(struct hostent*)gethostbyname(argv[1]))==NULL)
{
herror("gethostbyname");
exit(1);
}
printf("gethostbyname end\n");
/*建立与服务器的连接*/
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(1);
}
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
//server_addr.sin_addr=((struct in_addr*)he->h_addr);
inet_pton(AF_INET,argv[1],&server_addr.sin_addr);
if (connect(sockfd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr))==-1)
{
perror("connect");
exit(1);
}
doclient(stdin,sockfd);
return(0);
}
void doclient(FILE *fp,int sockfd)
{
char sendline[2047],recvline[2048];
int n;
/*下面进入处理循环,当接收到文件结束符Ctrl+D时跳出*/
do{
if ((n=read(sockfd,recvline,2047))==0)
{
perror("read");
exit(1);
}
recvline[n]=0;/*给结尾加上0*/
fputs(recvline,stdout);
if (fgets(sendline,2048,fp)!=NULL)
{
write(sockfd,sendline,strlen(sendline));
}
else
{
break;
                                }
}while(1);

}
————————————————————————————-
/* server.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MYPORT 3049 /*服务器端口监听号*/
#define BACKLOG 10 /*最大同时连接请求数*/
void sig_chld();
void echoclient(int);

int main()
{
int listenfd,new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size;

/*建立监听套接口*/
if ((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(1);
}
bzero(&(my_addr),sizeof(my_addr));
        /*void bzero(void *s, int n);置字节字符串s的前n个字节为零*/
my_addr.sin_family=AF_INET;/*internet域的地址族*/
my_addr.sin_port=htons(MYPORT);/*hostshort 转换成网络字节顺序 */
my_addr.sin_addr.s_addr=INADDR_ANY;
        /*通配ip地址,告诉系统选择本地机的地址*/

if (bind(listenfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))==-1)
{
perror("blind");
exit(1);
}
if (listen(listenfd,BACKLOG)==-1)
{
perror("listen");
exit(1);
}
signal(SIGCHLD,sig_chld);/*处理子进程终止信号,避免僵尸进程*/
/*void (*signal(int,void(*)(int))(int);*/

for(;;)/* accept loop,循环处理listen中的客户请求队列*/
{
sin_size = sizeof(struct sockaddr_in);
if ((new_fd=accept(listenfd,(struct sockaddr*)&their_addr,&sin_size))==-1)
{
if (errno==EINTR)/*EINTR:当睡眠时接收到其他信号*/
{
continue;/*accept 调用可能被信号打断*/
}
perror("accept");
continue;
}
printf("server:got connection from%s\n",inet_ntoa(their_addr.sin_addr));
/*char * inet_ntoa( struct in_addr in); 将网络地址转换成“.”点隔的字符串格式*/

if (!fork())/* fork出子进程处理客户请求*/
{
close(listenfd);
if (send(new_fd,"Hello world!",13,0)==-1)
{
perror("send");
}
echoclient(new_fd);/*调用函数,信息返回给客户*/
exit(0);/*0时正常退出,1时因错误退出*/
}
close(new_fd);
                /*父进程关闭刚才的套接口,并继续处理其他的客户请求*/
}
}
void sig_chld()/*SIGHLD 信号捕捉函数*/
{
pid_t pid;
        /*:typedef short pid_t;表示的是内核中的进程表的索引 */
int stat;

while((pid=waitpid(-1,&stat,WNOHANG))>0)
        /*处理已经结束的子进程,避免僵尸进程*/
/*
pid_t waitpid(pid_t pid, int *statloc, int options);
正常情况下返回pid,
      或者0(waitpid在非block模式下才有可能返回),-1代表错误
WNOHANG:如果还没有退出,不block,返回0;
statloc参数保存了退出进程的状态*/
printf("chile %d terminated\n",pid);/*打印“进程终止”*/
return;
}
void echoclient(int sockfd)
{
int n;
char buf[2048];
for(;;)
{
if ((n=read(sockfd,buf,sizeof(buf)))<=0)
{
break;/*对方关闭连接时,跳出循环*/
}
write(sockfd,buf,n);
                /*把接收自客户的数据,原本不动的发送给客户*/
}
close(sockfd);
}
————————————————————————————-
现在让我们来编译这两个程序:
root@linuxaid#gcc -o server server.c
root@linuxaid#gcc -o client client.c
然后在一台计算机上先运行服务器程序,再在另一个终端上运行客户端就会看到结果。
————————————————————————————-
  建立一个TCP连接需要三次握手,而断开一个TCP则需要四个分节。当某个应用进程调用close(主动端)后(可以是服务器端,也可以是客户端),这一端的TCP发送一个FIN,表示数据发送完毕;另一端(被动端)发送一个确认,当被动端待处理的应用进程都处理完毕后,发送一个FIN到主动端,并关闭套接口,主动端接收到这个FIN后再发送一个确认,到此为止这个TCP连接被断开。

2、UDP套接口 (因为俺不学介个,所以略掉咯:)见谅哦)