HardBirch

再看一遍网络Socket——温故

时间:09-10-29 栏目:系统技术篇 作者:鲁智森也有文化 评论:0 点击: 1,341 次

 

在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(&timestamp));


             
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;


}

 


htonl ntohl htons ntohs 函数解释

 

htonl函数是一个网络转换的函数,他把不确定的一个32位数字,转换成网络通用的32位数字,即大头在后的方式。

我们一般使用的计算机都是大头在后,也就是高地址在后面的形式。
比如你定义一个整型变量
int i = 0x12345678
然后在定义一个整型指针
int *p = &i;

假设p的地址是0x0012f2ec
那么在这个地址后4个字节都是这个整数,他的存放形式为

如果是大头前,也就是高地址在前,则存放形式为
0x0012f2ec:12 34 56 78

使用htonl函数后,他的存放形式就便为
0x0012f2ec:78 56 34 12

需要根据操作系统的不同确定你是否需要使用。

****************************************************************************

htonl = Host To Network, type Long
ntohl = Network To Host, type Long
同理,还有 htons 和 ntohs,这里的 s 是指 short

/***************************************************************************/

x86系统的数据存放都是按照little-endian的格式(硬件层没有对字节序的区分机制)。
通过对Windows平台的字节序转换函数(htons/ntohs(16bit),htonl /ntohl(32bit))的代码的分析,发现Windows对这样的转换没有进行特别的字节序检测,仅仅简单的进行了高低位的转换。

至于为什么没有做检测。我的理解是些转换函数已经默认了参数的字节序(MSDN)。
对于htons,htonl的参数为host byte order(小头);
对于ntohs,ntohl的参数为network byte order(大头)

其实,在调用函数时,我们就隐含指定了数值(注意,可能并非我们定义的数值)。
上面的程序中,可以这样理解。
d = htonl(c);
将小头存放(78’56’34’12)的数值0x12345678 转换为大头存放(12’34’56’78),并附给d。
注意:虽然这时d在小头系统中为0x78563412,但真实的数值是大头序的0x12345678。
e = ntohl(c);
将大头存放(78’56’34’12)的数值0x78563412 转换为小头存放(12’34’56’78),并附给e。

综上所述,导致这个疑问有以下几点。
1.字节序转换函数默认了被转换参数的字节序,所以没有必要再进行检测。
2.对转换后的数值的误解。(d的数值应为0x12345678,而非0x78563412)
3.调用ntohl函数时,对于参数的指定存在误解。
e = ntohl(0x78563412); /*数值0x12345678应该这样指定*/

声明: 本文由( 鲁智森也有文化 )原创编译,转载请保留链接: 再看一遍网络Socket——温故

再看一遍网络Socket——温故:等您坐沙发呢!

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐