地址族与数据序列----------网络编程(linux----C)

地址族与数据序列----------网络编程(linux----C)

1、分配给套接字的IP地址与端口号

(1)网络地址

IPv4:4字节地址族。

IPv6:16字节地址族。

IPv4标准的4字节IP地址分为网络地址和主机(指计算机)地址,分为A、B、C、D、E等类型。


(2)用于区分套接字的端口号

端口号就是在同一操作系统内为区分不同套接字而设置的,因此无法将1个端口号分配给不同套接字。端口号由16位构成,可分配的端口号范围为0-65535.

虽然端口号不能重复,但TCP套接字和UDP套接字不会共用端口号,所以允许重复。例如:如果某TCP套接字使用9190号端口,则其他TCP套接字就无法使用该端口号,但UDP套接字可以使用。

2、地址信息的表示

应用程序中使用的IP地址和端口号以结构体的形式给出了定义。

(1)表示IPv4地址的结构体

struct sockaddr_in
{
		sa_family_t  sin_family;		//地址族(Address Family)
	uint16_t	sin_port;		//16位TCP/UDP端口号
	struct		sin_addr;		//32位IP地址
	char 		sin_zero[8];		//不使用
};

//结构体in_addr用来存放32位IP地址
struct in_addr
{
In_addr_t s_addr; //32位IPv4地址
};


sockaddr_in成员分析:

3、网络字节序与地址变换

(1)字节序与网络字节序

大端序:高位字节存放到低位地址。

小端序:高位字节存放到高位地址。

在0x20号开始的地址中保存4字节int类型数0x12345678。整数0x12345678中,0x12是最高位字节,0x78是最低位字节。



(2)字节序转换

转换字节序函数

  • unsigned short htons(unsigned short);
  • unsigned short ntohs(unsigned short);
  • unsigned long htonl(unsigned long);
  • unsigned long ntohl(unsigned long);

htons中的h代表主机(host)字节序。

htons中的n代表网络(network)字节序。

s表示short,l指的是long。htons是h、to、n、s的组合,解释为“把short型数据从主机字节序转化为网络字节序”。

ntohs解释为“把short型数据从网络字节序转化为主机字节序”。

实例1、

endian_conv.c代码:

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
	unsigned short host_port=0x1234;
	unsigned short net_port;
	unsigned long host_addr=0x12345678;
	unsigned long net_addr;
	
	net_port=htons(host_port);
	net_addr=htonl(host_addr);
	printf("Host ordered port:%#x \n",host_port);
	printf("Network ordered port:%#x \n",net_port);
	printf("Host ordered address:%#lx \n",host_addr);
	printf("Network ordered address:%#lx \n",net_addr);
	return 0;
}

编译:gcc endian_conv.c -o conv

执行:./conv

结果:



4、网络地址的初始化与分配

(1)将字符串信息转换为网络字节序的整数型

sockaddr_in中保存地址信息的成员为32位整数型。为了分配IP地址,需要将其表示为32位整数型数据。

对于IP地址的表示,我们熟悉的是点分十进制表示法,而非整数型数据表示法。

函数1#include  <arpa/inet.h>

in_addr_t  inet_addr(const  char *string);

成功时返回32位大端序整数型值,失败时返回INADDR_NONE。

实例2、函数调用过程

inet.addr.c代码:

#include <stdio.h>
#include <arpa/inet.h>
int main(int argc,char *argv[])
{
	char *addr1="1.2.3.4";
	char *addr2="1.2.3.256";
	
	unsigned long conv_addr=inet_addr(addr1);
	if(conv_addr==INADDR_NONE)
		printf("Error occured!\n");
	else
		printf("Network ordered integer addr:%#lx\n",conv_addr);
	conv_addr=inet_addr(addr2);
	if(conv_addr==INADDR_NONE)
		printf("Error occured!\n");
	else
		printf("Network ordered integer addr:%#lx\n",conv_addr);
	return 0;
}

编译:gcc inet_addr.c -o addr

执行:./addr


不仅可以把IP地址转成32位整数型,而且可以检测无效的IP地址。


函数2#include <arpa/inet.h>

int  inet_aton(const  char *sting ,  struct  in_addr *addr);

成功返回1(true),失败返回0(false)。

参数:string:含有需转换的IP地址信息的字符串地址值。

addr:将保存转换结果的in_addr结构体变量的地址值。

inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式的IP地址转换为32位网络字节序整数并返回,只不过该函数利用了in_addr结构体,且使用频率更高。

实例3、函数调用过程

inet_aton.c代码:

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);

int main(int argc,char *argv[])
{
	char *addr="127.232.124.79";
	struct sockaddr_in addr_inet;
	if(!inet_aton(addr,&addr_inet.sin_addr))
		error_handling("Conversion error");
	else
		printf("Network ordered integer addr:%#x\n",addr_inet.sin_addr.s_addr);
	return 0;
}
void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

编译:gcc inet_aton.c -o aton

执行:./aton



函数3:#include <arpa/inet.h>

char *inet_ntoa(struct  in_addr  adr);

成功时返回转换的字符串地址值,失败时返回-1.

实例4、

inet_ntoa.c代码:

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>


int main(int argc,char *argv[])
{
struct sockaddr_in addr1,addr2;
char *str_ptr;
char str_arr[20];

addr1.sin_addr.s_addr=htonl(0x1020304);
addr2.sin_addr.s_addr=htonl(0x1010101);

str_ptr=inet_ntoa(addr1.sin_addr);
strcpy(str_arr,str_ptr);
printf("Dotted-Decimal notation1:%s\n",str_ptr);

inet_ntoa(addr2.sin_addr);
printf("Dotted-Decimal notation2:%s\n",str_ptr);
printf("Dotted-Decimal notation3:%s\n",str_arr);
return 0;

}

编译:gcc inet_ntoa.c -o ntoa

执行:./ntoa



(2)网络地址初始化

struct sockaddr_in  addr;
char *serv_ip="211.217.168.13";		//声明IP地址字符串
char *serv_port="9190";			//声明端口号字符串
memset(&addr,0,sizeof(addr));		//结构体变量addr的所有成员初始化为0
addr.sin_family=AF_INET;		//指定地址族
addr.sin_addr.s_addr=inet_addr(serv_ip);	//基于字符串的IP地址初始化
addr.sin_port=htons(atoi(serv_port));		//基于字符串的端口号初始化

(3)客户端地址信息初始化

上述的网络地址信息初始化过程主要针对服务器端而非客户端。服务器端的准备工作通过bind函数完成,而客户端则通过connect函数完成。因此,函数调用前需准备的地址值类型也不同。

  • 服务器端声明sockaddr_in结构体变量,将其初始化为赋予服务器端IP和套接字端口号,然后调用bind函数;
  • 而客户端则声明sockaddr_in结构体,并初始化为要与之连接的服务器端套接字的IP和端口号,然后调用connect函数。

INADDR_ANY

每次创建服务器端套接字都要输入IP地址会有些繁琐,此时可如下初始化地址信息:

struct  sockaddr_in  addr;
char *serv_port="9190";			//声明端口号字符串
memset(&addr,0,sizeof(addr));		//结构体变量addr的所有成员初始化为0
addr.sin_family=AF_INET;		//指定地址族
addr.sin_addr.s_addr=htonl(INADDR_ANY);	
addr.sin_port=htons(atoi(serv_port));		//基于字符串的端口号初始化

利用常数INADDR_ANY分配服务器端的IP地址,若采用这种方式,则可自动获取运行服务器端的计算机IP地址,不必亲自输入。若同一计算机中已分配多个IP地址,则只要端口号一致,就可以从不同的IP地址接收数据。

因此,服务器端中优先考虑这种方式。客户端中除非带有一部分服务器端功能,否则不会采用。

示例:hello_server.c和hello_client.c运行过程

  • ./hserver  9190

分析可知,向main函数传递的9190端口号。通过此端口创建服务器套接字并运行程序,但未传送IP地址,因为可以通过INADDR_ANY指定IP地址。

  • ./hclient  127.0.0.1  9190
(4)向套接字分配网络地址

函数:#include <sys/socket.h>

int  bind (int  sockfd,struct  sockaddr*myaddr,socklen_t  addrlen);

成功时返回0,失败时返回-1。

参数:

  • sockfd:要分配地址信息(IP地址和端口号)的套接字文件描述符。
  • myaddr:存有地址信息的结构体变量地址值。
  • addrlen:第二个结构体变量的长度。
服务器端常见套接字初始化过程:

int 	serv_sock;
struct 	sockaddr_in 	serv_addr;
char *serv_port="9190";
/*创建服务器端套接字(监听套接字)*/
serv_sock=socket(PF_INET,SOCK_STREAM,0);

/*地址信息初始化*/
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADRR_ANY);
serv_addr.sin_port=htons(atoi(serv_port));
/*分配地址信息*/
bind(serv_sock,(struct  sockaddr*)&serv_addr,sizeof(serv_addr));



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值