[Linux network programming learning] preliminary knowledge (network byte order, IP address conversion function, sockaddr data structure)

Posted by Tsukiyomi on Tue, 09 Nov 2021 21:58:18 +0100

This is Niuke Linux C + + course and dark horse Linux system programming notes.

1. Network byte order

We already know that multi byte data in memory can be divided into large end and small end relative to memory address.

The multi byte data in the disk file can also be divided into large end and small end relative to the offset address in the file. Network data flow can also be divided into large end and small end, so how to define the address of network data flow? The sending host usually sends the data in the sending buffer in the order of memory address from low to high, and the receiving host saves the bytes received from the network in the receiving buffer in the order of memory address from low to high. Therefore, the address of the network data stream should be specified as follows: the data sent first is the low address, and the data sent later is the high address.

TCP/IP protocol stipulates that network data flow shall adopt large end byte order, that is, low address and high byte. For example, address 0-1 in the sending host stores a 16 bit source port number. If the port number is 1000 (0x3e8), address 0 is 0x03 and address 1 is 0xe8, that is, 0x03 is sent first and then 0xe8. These 16 bits should also be 0x03 in the low address and 0xe8 in the buffer of the sending host. However, if the sending host is in small end byte order, address 0 stores 0xe8 and address 1 stores 0x03, and the port number received by the receiver becomes 59395 (0xe803) instead of 1000. Therefore, the sending host needs to convert the byte order before filling 1000 into the sending buffer. Similarly, if the receiving host is in small end byte order, the 16 bit source port number also needs to be converted in byte order. If the host is in large byte order, no conversion is required for sending and receiving. Similarly, network byte order and host byte order should also be considered for 32-bit IP addresses.

In order to make the network program portable and make the same C code run normally after being compiled on large-end and small-end computers, the following library functions can be called to convert the network byte order and host byte order.

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);  // 32-bit, from host to net, used for conversion when the host sends IP to the network
uint16_t htons(uint16_t hostshort); // 16 bit, from host to net, used for conversion when the host sends IP to the network
uint32_t ntohl(uint32_t netlong);   // 32-bit, net to host, used for conversion when the host sends the port number to the network
uint16_t ntohs(uint16_t netshort);  // 16 bit, net to host, used for conversion when the host sends the port number to the network

h represents host, n represents network, l represents 32-bit long integer (IP address is 32 bits), and s represents 16 bit short integer (port number is 16 bits).

If the host is of small endian byte order, these functions convert the parameters to large and small endian and then return them. If the host is of large endian byte order, these functions do not convert and return the parameters intact.

2. IP address translation function

Usually, people are used to using readable strings to represent IP addresses, such as dotted decimal strings to represent IPv4 addresses, and
The hexadecimal string represents the IPv6 address. But in programming, we need to convert them into integers (binary numbers) before we can use them. And record
When logging, on the contrary, we need to convert the IP address represented by an integer into a readable string.

#include <arpa/inet.h>
// p: Dotted decimal IP string, n: represents network, an integer of network byte order
int inet_pton(int af, const char *src, void *dst);

af: address family: AF_INET (for ipv4) AF_INET6 (for ipv6)
src: dotted decimal IP string to be converted
dst: the converted results are saved in this

#include <arpa/inet.h>
// Convert the integer of network byte order into dotted decimal IP address string
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

af: address family: AF_INET (for ipv4) AF_INET6 (for ipv6)
src: the address of the integer of the ip to be converted
dst: the place where the converted IP address string is saved
Size: the size of the third parameter (the size of the array)
Return value: returns the address (string) of the converted data, which is the same as dst

3. socket address

In the socket network programming interface, the socket address is represented by the structure sockaddr, which is defined as follows:

#include <bits/socket.h>
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
typedef unsigned short int sa_family_t;

Many network programming functions were born earlier than the IPv4 protocol. At that time, the struct sockaddr structure was used. In order to be forward compatible, sockaddr now degenerates into a function similar to (void *), passing an address to the function. As for this function, it is sockaddr_in or sockaddr_in6, determined by the address family, and then the internal type of the function is forced to be converted to the required address type

The TCP/IP protocol family has sockaddr_in and sockaddr_in6 two dedicated socket address structures, which are used for IPv4 and IPv4 respectively
IPv6:

#include <netinet/in.h>
struct sockaddr_in
{
sa_family_t sin_family; /* __SOCKADDR_COMMON(sin_) */
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) - sizeof (struct in_addr)];
};
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in6
{
sa_family_t sin6_family;
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

All variables of special socket address (and sockaddr_storage) type need to be converted to general socket address type SOCKADDR (forced conversion), because the address parameter type used by all socket programming interfaces is SOCKADDR.

Topics: Linux TCP/IP