TCP IP network programming (3) Address family and data sequence

The IP address and port number assigned to the socket

website address

IP addresses are divided into two categories:

  • IPv4 4-byte address family
  • IPv6 16-byte address family

The difference between IPv4 and IPv6 mainly refers to the number of bytes used in the IP address. The current common address family is IPv4, while IPv6 is a standard proposed to deal with the problem of IP address exhaustion. Currently, IPv4 is mainly used.

The 4-byte IP address of the IPv4 standard is divided into network address and host address, and is divided into A, B, C, D and other types.

A类		网络ID 主机ID 主机ID 主机ID
B类		网络ID 网络ID 主机ID 主机ID
C类		网络ID 网络ID 网络ID 主机ID
D类		网络ID 网络ID 网络ID 网络ID (多播IP地址)

Network address classification and host address boundaries

The number of bytes occupied by the network address can be determined by the first byte of the IP address.

  • The first byte range of class A address: 0-127
  • The first byte range of Class B addresses: 128-191
  • The first byte range of Class C addresses: 191-223

There is another way of expressing it

  • Class A addresses start with 0
  • Class B addresses start with 10
  • Class C addresses start with 110

Representation of address information

A structure representing an IPv4 address

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

This structure mentions another structure in_addr defined as:

struct in_addr
{
    in_addr_t        s_addr;     //32位IPv4地址
}

The above two structures contain some data types. For uint16_t, in_addr_t and other types, please refer to POSIX. POSIX is the standard for operating UNIX systems

​Data types defined in POSIX

Data type name Data type description Declaration header file
int8_t signed 8-bit int sys/types.h
uint8_t unsigned 8-bit int sys/types.h
int16_t signed 16-bit int sys/types.h
uint16_t unsigned 16-bit int sys/types.h
int32_t signed 32-bit int sys/types.h
uint32_t unsigned 32-bit int sys/types.h
sa_family_t address family sys/socket.h
socklen_t length(length of struct) sys/socket.h
in_addr_t IP address, uint32_t netinet/in.h
in_port_t Port number, uint16_t netinet/in.h

Member analysis of structure sockaddr_in

member sin_family

Each protocol family is applicable to different protocol families. For example, IPv4 uses a 4-byte address family and IPv6 uses a 16-byte address family.

Address family

address family meaning
OF_INET Address family used in IPv4 network protocol
AF_INET6 Address family used in IPv6 network protocol
AF_LOCAL Address family of UNIX protocol used in local communication

AF_LOCAL was added to illustrate that there are multiple address families

member sin_port

This member stores the 16-bit port number. The important thing is that it is stored in network byte order.

member sin_addr

This member saves 32-bit IP address information, also in network byte order, and manages the structure in_addr.

Member sin_zero

No special meaning. It is just a member inserted to make the size of the structure sockaddr_in consistent with the storage of the sockaddr structure. It must be filled with 0.

Network byte order and address conversion

Byte order and network byte order

There are two ways for the CPU to save data, which means there are two ways for the CPU to parse the data.

  • Big endian: the high-order byte is stored in the low-order address
  • Little endian: the high-order byte is stored in the high-order address

Agree on a unified method when transmitting data over the network. This agreement is called network byte order and is unified into big endian order.

In summary, forward the data array into big-endian format before transmitting it over the network. When receiving the data, all computers should recognize that the data is in network byte-order format. When transmitting the data in a little-endian system, it should be converted into big-endian arrangement.

Byte order conversion

Convert the data into network byte order before filling the structure sockadr_in, and introduce the function that helps convert byte order

unsigned short htons(unsigned short)
unsigned short ntohs(unsigned short)
unsigned long htonl(unsigned short)
unsined long ntohl(unsigned long)

The meaning of the function name

  • The h in htons represents host byte order
  • The n in htons represents network byte order
  • The s in htons refers to short
  • The l in htons refers to long (the long type in Linux occupies 4 bytes)
#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;
}

Below is the result of running on a little-endian CPU. If running on a big-endian CPU, the variable values ​​will not change. Most friends will get similar running results, because both Intel and AMD series CPUs adopt the little-endian standard.

gcc endian_conv.c -o conv
./conv
输出:
Host ordered port : 0x1234
Network ordered port : 0x3412
Hostordered address : 0x12345678
Network ordered address : 0x78563412

Initialization and allocation of network addresses

Convert string information to network byte order integer type

​ For the representation of IP addresses, we are familiar with dotted decimal notation rather than integer data representation. Fortunately, there are functions that will help us convert the IP address in string form into 32-bit integer data, and perform network byte order conversion while converting the type.

#include<arpa/inet.h>
in_addr_t inet_addr(const char * string);
		//成功时返回32位大端序整数型值,失败时返回INADDR_NONE。

The calling process of this function

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

int main(int argc, char *argv[])
{
	char *addr1="127.212.124.78";
	char *addr2="127.212.124.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;
}

It can be seen from the results that the inet_addr function can not only convert the IP address into a 32-bit integer type, but also detect invalid IP addresses.

The inet_aton function is functionally identical to the inet_addr function. It also converts the string IP address into a 32-bit network byte order integer and returns it. The difference is that this function uses the in_addr structure and is used more frequently.

#include<arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr);
		成功时返回1,失败时返回0
		参数1:string,含有需转换的IP地址信息的字符串地址值。
		参数2:addr,将保存转换结果的in_addr结构体变量的地址值。

The calling process of this function

#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);
}

Call the inet_addr function to return the converted IP address information and save it to the in_addr structure variable declared in the sockaddr_in structure. The inet_aton function does not require this process, because the function will automatically store the result into the structure variable.

There is also a function, just the opposite of inet_aton(), which can convert the network byte order integer IP address into the familiar string form

#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);
	成功返回转换的字符串地址值,失败返回-1
  • This function converts the integer IP address passed in through the parameter into string format and returns

  • But be careful, the return value is a char pointer, and returning the string address means that the string has been saved in the memory space. However, this function does not ask the programmer to allocate memory, but instead applies for memory internally to save the string. That is to say, when this function is called, the information must be copied to other memory spaces immediately. Because, if the inet_ntoa function is called again, the previously saved string information may be overwritten.

  • In short, the string address returned before calling the inet_ntoa function again is valid. If long-term storage is required, the string should be copied to other memory space

Give an example of calling this function

#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;
}

Network address initialization

Common network address information initialization methods during server-side socket creation:

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

INADDR_ANY

Initialize address information

struct sockaddr_in addr;
char * serv_port = "9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
add.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(atoi(serv_port));

The biggest difference from the previous method is that if you use INADDR_ANY to allocate the server-side IP address, you can automatically obtain the IP address of the computer running the server without having to enter it yourself. Moreover, if multiple IP addresses have been assigned to the same computer, data can be received from different IP addresses as long as the port numbers are consistent.

Assign a network address to a socket

We have learned about the initialization method of the sockaddr_in structure before, and then assign the initialized address information to the socket. The bind function is responsible for this operation

#include<sys/socket.h>
int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen);
	成功时返回0,失败时返回-1。
	参数1:sockfd,要分配地址信息(IP地址和端口号)的套接字文件描述符
	参数2:myaddr,存有地址信息的结构体变量地址值。
	参数3:addrlen,第二个结构体变量的长度。

If this function call is successful, the address information specified by the second parameter is assigned to the corresponding socket in the first parameter.

int serv_sock;
struct sockaddr_in serv_addr;
char * srev_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(INADDR_ANY);
serv_addr.sin_port = htons(atoi(serv_port));/* 分配地址信息 */
bind(serv_sock, (struct sockaddr * )&serv_addr, sizeof(serv_addr));

The server-side code structure is as above

Summarize

This is the third article in the "TCP/IP Network Programming" column. Readers are welcome to subscribe!

For more information, click GitHub . Welcome readers to Star

⭐Academic exchange group Q 754410389 is being updated~~~

Guess you like

Origin blog.csdn.net/m0_63743577/article/details/132651152