Programación de red TCP IP (3) Familia de direcciones y secuencia de datos

La dirección IP y el número de puerto asignado al socket.

dirección web

Las direcciones IP se dividen en dos categorías:

  • Familia de direcciones IPv4 de 4 bytes
  • Familia de direcciones IPv6 de 16 bytes

La diferencia entre IPv4 e IPv6 se refiere principalmente al número de bytes utilizados en la dirección IP. La familia de direcciones común actual es IPv4, mientras que IPv6 es un estándar propuesto para abordar el problema del agotamiento de las direcciones IP. Actualmente, se utiliza principalmente IPv4.

La dirección IP de 4 bytes del estándar IPv4 se divide en dirección de red y dirección de host, y se divide en A, B, C, D y otros tipos.

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

Clasificación de direcciones de red y límites de direcciones de host

El número de bytes ocupados por la dirección de red se puede determinar mediante el primer byte de la dirección IP.

  • El primer rango de bytes de la dirección de clase A: 0-127
  • El primer rango de bytes de direcciones de Clase B: 128-191
  • El primer rango de bytes de direcciones de Clase C: 191-223

Hay otra manera de expresarlo.

  • Las direcciones de clase A comienzan con 0
  • Las direcciones de clase B comienzan con 10
  • Las direcciones de clase C comienzan con 110

Representación de información de dirección.

Una estructura que representa una dirección IPv4.

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]; //不使用
};

Esta estructura menciona otra estructura in_addr definida como:

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

Las dos estructuras anteriores contienen algunos tipos de datos. Para uint16_t, in_addr_t y otros tipos, consulte POSIX. POSIX es el estándar para operar sistemas UNIX

​Tipos de datos definidos en POSIX

Nombre del tipo de datos Descripción del tipo de datos Archivo de encabezado de declaración
int8_t int de 8 bits con signo sistema/tipos.h
uint8_t entero de 8 bits sin firmar sistema/tipos.h
int16_t int de 16 bits con signo sistema/tipos.h
uint16_t entero de 16 bits sin firmar sistema/tipos.h
int32_t int de 32 bits con signo sistema/tipos.h
uint32_t entero de 32 bits sin firmar sistema/tipos.h
sa_familia_t dirección familia sistema/socket.h
calcetín_t longitud (longitud de la estructura) sistema/socket.h
in_addr_t dirección IP, uint32_t netinet/pulg.h
in_port_t Número de puerto, uint16_t netinet/pulg.h

Análisis de miembros de la estructura sockaddr_in

miembro sin_familia

Cada familia de protocolos es aplicable a diferentes familias de protocolos. Por ejemplo, IPv4 usa una familia de direcciones de 4 bytes e IPv6 usa una familia de direcciones de 16 bytes.

familia de dirección

dirección familia significado
OF_INET Familia de direcciones utilizada en el protocolo de red IPv4
AF_INET6 Familia de direcciones utilizada en el protocolo de red IPv6
AF_LOCAL Familia de direcciones del protocolo UNIX utilizada en la comunicación local.

Se agregó AF_LOCAL para ilustrar que hay varias familias de direcciones

miembro sin_port

Este miembro almacena el número de puerto de 16 bits, lo importante es que se almacene en el orden de bytes de la red.

miembro sin_addr

Este miembro guarda información de direcciones IP de 32 bits, también en orden de bytes de red, y administra la estructura in_addr.

Miembro sin_zero

Sin significado especial. Es solo un miembro insertado para que el tamaño de la estructura sockaddr_in sea consistente con el almacenamiento de la estructura sockaddr. Debe completarse con 0.

Orden de bytes de red y conversión de direcciones

Orden de bytes y orden de bytes de red

La CPU tiene dos formas de guardar datos, lo que significa que la CPU tiene dos formas de analizar los datos.

  • Big endian: el byte de orden superior se almacena en la dirección de orden inferior
  • Little endian: el byte de orden superior se almacena en la dirección de orden superior

Acuerde un método unificado al transmitir datos a través de la red. Este acuerdo se denomina orden de bytes de la red y se unifica en orden big endian.

En resumen, reenvíe la matriz de datos al formato big-endian antes de transmitirla a través de la red. Al recibir los datos, todas las computadoras deben reconocer que los datos están en el formato de orden de bytes de la red. Al transmitir los datos en un sistema little-endian, debería convertirse en un arreglo big-endian.

Conversión de orden de bytes

Convierta los datos al orden de bytes de la red antes de completar la estructura sockadr_in e introduzca la función que ayuda a convertir el orden de bytes

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

El significado del nombre de la función.

  • La h en htons representa el orden de bytes del host
  • La n en htons representa el orden de bytes de la red.
  • La s en htons se refiere a corta
  • La l en htons se refiere a long (el tipo long en Linux ocupa 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;
}

A continuación se muestra el resultado de ejecutarlo en una CPU little-endian. Si se ejecuta en una CPU big-endian, los valores de las variables no cambiarán. La mayoría de los amigos obtendrán resultados de ejecución similares, porque las CPU de las series Intel y AMD adoptan el estándar little-endian.

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

Inicialización y asignación de direcciones de red.

Convertir información de cadena a tipo entero de orden de bytes de red

​ Para la representación de direcciones IP, estamos familiarizados con la notación decimal con puntos en lugar de la representación de datos enteros. Afortunadamente, existen funciones que nos ayudarán a convertir la dirección IP en forma de cadena en datos enteros de 32 bits y realizar la conversión del orden de bytes de la red mientras convierte el tipo.

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

El proceso de llamada de esta función.

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

Se puede ver en los resultados que la función inet_addr no solo puede convertir la dirección IP a un tipo entero de 32 bits, sino que también puede detectar direcciones IP no válidas.

La función inet_aton es funcionalmente idéntica a la función inet_addr. También convierte la dirección IP de la cadena en un entero de orden de bytes de red de 32 bits y lo devuelve. La diferencia es que esta función usa la estructura in_addr y se usa con más frecuencia.

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

El proceso de llamada de esta función.

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

Llame a la función inet_addr para devolver la información de la dirección IP convertida y guardarla en la variable de estructura in_addr declarada en la estructura sockaddr_in. La función inet_aton no requiere este proceso, porque la función almacenará automáticamente el resultado en la variable de estructura.

También hay una función, justo lo opuesto a inet_aton(), que puede convertir la dirección IP entera del orden de bytes de la red en la forma familiar de cadena.

#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);
	成功返回转换的字符串地址值,失败返回-1
  • Esta función convierte la dirección IP entera pasada a través del parámetro en formato de cadena y devuelve

  • Pero tenga cuidado, el valor de retorno es un puntero de carácter y devolver la dirección de la cadena significa que la cadena se ha guardado en el espacio de memoria. Sin embargo, esta función no solicita al programador que asigne memoria, sino que solicita memoria interna para guardar. la cuerda. Es decir, cuando se llama a esta función, la información se debe copiar a otros espacios de memoria inmediatamente. Porque, si se vuelve a llamar a la función inet_ntoa, es posible que se sobrescriba la información de la cadena guardada anteriormente.

  • En resumen, la dirección de cadena devuelta antes de volver a llamar a la función inet_ntoa es válida. Si se requiere almacenamiento a largo plazo, la cadena debe copiarse a otro espacio de memoria.

Dé un ejemplo de cómo llamar a esta función.

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

Inicialización de dirección de red

Métodos comunes de inicialización de información de direcciones de red durante la creación de sockets del lado del servidor:

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

Inicializar información de dirección

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

La mayor diferencia con el método anterior es que si usa INADDR_ANY para asignar la dirección IP del lado del servidor, puede obtener automáticamente la dirección IP de la computadora que ejecuta el servidor sin tener que ingresarla usted mismo. Además, si se han asignado varias direcciones IP a la misma computadora, se pueden recibir datos de diferentes direcciones IP siempre que los números de puerto sean consistentes.

Asignar una dirección de red a un socket

Hemos aprendido sobre el método de inicialización de la estructura sockaddr_in antes y luego asignamos la información de la dirección inicializada al socket. La función de vinculación es responsable de esta operación.

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

Si esta llamada a función tiene éxito, la información de dirección especificada por el segundo parámetro se asigna al socket correspondiente en el primer parámetro.

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

La estructura del código del lado del servidor es la anterior.

Resumir

Este es el tercer artículo de la columna "Programación de redes TCP/IP". ¡Los lectores pueden suscribirse!

Para obtener más información, haga clic en GitHub Bienvenidos lectores a Star

⭐Grupo de intercambio académico Q 754410389 se está actualizando~~~

Supongo que te gusta

Origin blog.csdn.net/m0_63743577/article/details/132651152
Recomendado
Clasificación