3.1 分配给套接字的IP地址与端口号
3.1.1 网络地址
- IPv4(Internet Protocol version 4)4字节地址组
- IPv6(Internet Protocol version 6)16字节地址组
3.1.2 用于区分套接字的端口号
因为在浏览视频网页时,一边播放视频一边有网页,所以需要两个不同的套接字来接收。
如何区分二者提出以下一些建议和方法:开发收发数据的P2P程序,该程序用块单位分割一个文件,从多台计算机接收数据。
计算机中一般配有NIC(Network Interface Card,网络接口卡)数据传输设备。通过NIC向计算机内部传输数据时会用到IP。操作系统负责把传递到内部的数据分配给套接字,所以需要端口号。通过NIC设备接收的数据内有端口号,操作系统正是参考此端口号把数据传输给相应端口的套接字。(端口号由16位构成,可分配的端口号范围是065535,其中11023是知名端口,一般分配给特定的应用程序。端口号不能重复,但是TCP套接字和UDP套接字不会共用端口号,所以允许重复。例如:某TCP套接字用9190号端口,其他TCP套接字不能使用,但是UDP套接字可以使用)
3.2 地址信息的表示
3.2.1 表示IPv4地址的结构体
struct sockaddr_in
{
sa_family_t sin_family; //地址族(Address Family)
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr;//32位IP地址
char sin_zero[8];//不使用
};
struct in_addr
{
In_addr_t s_addr;//32位IPv4地址
};
struct sockaddr
{
sa_family_t sin_family;//地址族
char sa_data[14];//地址信息
};
3.2.2 POSIX中定义的数据类型
数据类型名称 | 数据类型说明 | 声明的头文件 |
---|---|---|
int8_t | signed 8-bit int | sys/types.h |
uint8_t | unsigned 8-bit int(unsigned char) | sys/types.h |
int16_t | signed 16-bit int | sys/types.h |
uint16_t | unsigned 16-bit int(unsigned short) | sys/types.h |
int32_t | signed 32-bit int | sys/types.h |
uint32_t | unsigned 32-bit int(unsigned long) | sys/types.h |
sa_family_t | 地址族(address family) | sys/socket.h |
socklen_t | 长度(length of struct) | sys/socket.h |
in_addr_t | IP地址,声明为uint32_t | netinet/in.h |
in_port_t | 端口号,声明为uint16_t | netinet/in.h |
3.2.3 结构体sockaddr_in的成员分析
3.2.3.1 成员sin_family
地址族 | 含义 |
---|---|
AF_INET | IPv4网络协议中使用的地址族 |
AF_INET6 | IPv6网络协议中使用的地址族 |
AF_LOCAL | 本地通信中采用的UNIX协议的地址族 |
3.2.3.2 成员sin_port
该成员保存16位端口号,以网络字节序保存。
3.2.3.3 成员sin_addr
该成员保存32位IP地址信息,同样以网络字节序保存。
3.2.3.4 成员sin_zero
无特殊含义。为了使结构体sockaddr_in的大小与sockaddr结构体保持一致而插入的成员。
3.3 网络字节序与地址变换
3.3.1 字节序于网络字节序
- 大端模式:按照人类固有的思维顺序存储
- 小端模式:先处理低字节的数据也就是倒着存储,因为对于计算机来说处理小的数据更方便
由于不同的模式处理的顺序不同,我们需要区分模式,这样可以应对不同CPU下的处理方案。所以说,在通过网络传输数据时约定统一方式,这种约定称为网络字节序,非常简单统一为大端序。(无论要传递什么信息,都应该在传输前转换为大段模式进行存储)
3.3.2 字节序转换
相关函数如下:
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
//htons h代表主机的字节序
//htons n代表网络的字节序
//所以htons就是把short型数据从主机转换为网络字节序
//ntohs就是把网络字节序转换成主机的顺序
程序如下:
//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;
}
zhang@zhang-virtual-machine:~/Desktop/Ctest$ gcc endian_conv.c -o conv.c
zhang@zhang-virtual-machine:~/Desktop/Ctest$ ./conv.c
Host ordered port:0x1234
Network ordered port:0x3412
Host ordered address:0x12345678
Network ordered address:0x78563412
#Intel和AMD都是小端模式
3.4 网络地址的初始化与分配
3.4.1 将字符串信息转换为网络字节序的整数型
3.4.1.1 inet_addr函数
#include<arpa/inet.h>
//改函数可以将字符串形式的IP地址转换成32位整数型数据,那么此函数在转换类型的同时进行网络字节序转换,还能无效的IP地址
int_addr_t inet_addr(const char *string);
//成功时返回32位大端序整数型值,失败时返回INADDR_NONE
//inet_addr.c
#include<stdio.h>
#include<arpa/inet.h>
int main(int argc, char *agv[])
{
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 occureded \n");
else
printf("Network ordered integer addr:%#lx \n\n", conv_addr);
return 0;
}
zhang@zhang-virtual-machine:~/Desktop/Ctest$ gcc inet_addr.c -o addr
zhang@zhang-virtual-machine:~/Desktop/Ctest$ ./addr
Network ordered integer addr:0x4030201
Error occureded
3.4.1.2 inet_aton函数
首先强调inet_aton函数与inet_addr函数在功能上完全相同,只不过利用了in_addr结构体,且使用频率更高。
#include<arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr);
//成功时返回1(true),失败时返回0(false)
//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);
}
zhang@zhang-virtual-machine:~/Desktop/Ctest$ gcc inet_aton.c -o aton
zhang@zhang-virtual-machine:~/Desktop/Ctest$ ./aton
Network ordered integer addr:0x4f7ce87f
3.4.1.3 inet_ntoa函数
改函数可以将网络字节序整数型IP地址转换成我们熟悉的字符串形式。
#include<arpa/inet.h>
char *inet_ntoa(struct in_addr adr);
//成功时返回转换的字符串地址值,失败时返回-1
//若要长期保存需要将字符串复制到其他的内存空间
//inet_ntoa.c
#include<stdio.h>
#include<string.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;
}
zhang@zhang-virtual-machine:~/Desktop/Ctest$ gcc inet_ntoa.c -o ntoa
zhang@zhang-virtual-machine:~/Desktop/Ctest$ ./ntoa
Dotted-Decimal notation1: 1.2.3.4
Dotted-Decimal notation2: 1.1.1.1
Dotted-Decimal notation3: 1.2.3.4
3.4.2 网络地址初始化
struct sockaddr_in addr;
char *serv_ip="127.0.0.1"; //声明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));//基于字符串的端口号初始化