Fundamentos de la programación de redes UNIX

prefacio

Debido a que hay muchas estructuras involucradas en la programación de sockets, aquí se hacen algunos registros para facilitar la referencia posterior.


1. Estructura relacionada

1) Explicación detallada de sockaddr y sockaddr_in

sockaddrUtilizado en versiones muy tempranas, donde los miembros sa_datamezclan direcciones IP y puertos para la comunicación, la estructura y los archivos de encabezado son los siguientes:

#include <sys/socket.h>

struct sockaddr {
	sa_family_t 				sin_family;
	char 						sa_data[14];	
}

sockaddr_inSepare la ip y el puerto de la estructura inicial, la estructura es la siguiente:

#include <netinet/in.h>
#include <arpa/inet.h>

struct sockaddr_in
{
	sa_family_t 				sin_family;
	uint16_t 					sin_port;
	struct in_addr				sin_addr;
	char						sin_zero[8];
};
struct in_addr
{
	in_addr_t					s_addr;
};

Tanto sin_port como sin_addr deben estar en orden de bytes de red (big endian), y el linux que usamos normalmente es little endian (orden de bytes de host), por lo que se requiere conversión de interfaz.

2) Funciones para conversión de orden de bytes

~~ inet_addr~~ Se ha abolido ahora, consulte <<Programación de red UNIX Volumen 1>> Sección 3.6 P68, el reemplazo es inet_addr,更好的替代是inet_pton(支持IPv4和IPv6)

#include <arpa/inet.h>
/*返回:若字符串有效则为1,否则为0/*/
int inet_aton(const char *strptr, struct in_addr *addrptr);
/*返回:若字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE*/
in_addr_t inet_addr(const char *strptr);
/*返回:指向一个点分十进制数串的指针*/
char *inet_ntoa(struct in_addr inaddr); 

/*返回:若成功则为1,若输入不是有效的表达式则为0, 若出错则为-1*/
int  inet_pton(int family, const char *strptr, void *addrptr/*&foo.sin_addr*/);
/*如成功则为指向结果的指针,若出错则为NULL*/
len:如果是IPv4,填写16,在<netinet/in.h>中有定义宏 
#define INET_ADDRSTRLEN 16 
#define INET6_ADDRSTRLEN 46
char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
const char *inet_ntop(int family, const void *addrptr,  char *strptr, size_t len);

p.ej.:

struct sockaddr_in servaddr;
servaddr.sin_port = htons(13);
servaddr.sin_addr.s_addr = inet_addr("192.168.1.1");
//尽量使用如下,因为该接口不仅支持IPV4还支持IPV6

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int  inet_pton(int af, const char *src, void *dst); //将"点分十进制"->"二进制整数”
//struct  in_addr s;
char *ip = "192.168.1.1";
inet_pton(AF_INET, (void*) ip,  (void*)&s);

Nota: Si 端口是0() o INADDR_ANY(el valor suele ser 0), no es necesario convertir a orden de bytes de red (NBO), porque no es necesario usar htonl, pero todos INADDR_ están definidos en el archivo de encabezado <netinet/in .h> de acuerdo con la palabra principal Para la definición de secuencia, todavía tenemos que acostumbrarnos a usar htonl para <<Programación de red UNIX Volumen 1>> Sección 4.4 P83.
Se recomienda usar inet_pton, admite IPV4 e IPV6, solo haga pequeños cambios, pero solo un poco,
otros lugares deben modificarse, por lo que la mejor manera es escribir 协议无关el programa (11-11, usando la función getaddrinfo)

struct sockaddr_in ->struct sockaddr_in6
socket(AF_INET, SOCK_STREAM,0) 	-> socket(AF_INET6, SOCK_STREAM, 0);
servaddr.sin_family = AF_INET	->	servaddr.sin6_family = AF_INET6;
servaddr.sin_port 				->		servaddr_sin6_port
inet_pton(AF_INET,...,...)		->		inet_pton(AF_INET6,...,...);

#include	"unp.h"

int
main(int argc, char **argv)
{
	int					sockfd, n, counter = 0;
	char				recvline[MAXLINE + 1];
	struct sockaddr_in	servaddr;

	if (argc != 2)
		err_quit("usage: a.out <IPaddress>");

	if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		err_sys("socket error");

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;	/*协议族成员设置为AF_INET*/
	servaddr.sin_port   = htons(13);	/* daytime server */
	if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
		err_quit("inet_pton error for %s", argv[1]);

	if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
		err_sys("connect error");

	while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
		counter++;
		recvline[n] = 0;	/* null terminate */
		if (fputs(recvline, stdout) == EOF)
			err_sys("fputs error");
	}
	if (n < 0)
		err_sys("read error");

	printf("counter = %d\n", counter);
	exit(0);
}

3) Preguntas y conclusiones

3-1) Escribir código independiente del protocolo para admitir IPv4 e IPv6

3-2) Utilice el nombre de dominio en lugar de punto decimal


2. Relacionado con la API

1) función de conexión

#include <sys/socket.h>
/*返回: 若成功则为0,若出错则为-1*/
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

Si se trata de un socket TCP, la conexión desencadena el proceso de protocolo de enlace de tres vías TCP, solo si la conexión se realiza correctamente o si 出错返回se produce un error en las siguientes situaciones:

  • El cliente envía un segmento SYN, pero no recibe una respuesta del servidor, devuelve un error ETIMEDOUT y continuará enviando SYN a intervalos.
  • Si la respuesta SYN al cliente es RST(es decir, reinicio), significa que el servidor se está ejecutando sin proceso en el puerto especificado por el cliente.
    El cliente se está ejecutando, el servidor no se está ejecutando
root@ubuntu:/opt/socket/unpv13e/intro# ./daytimetcpcli 127.0.0.1
connect error: Connection refused

Capturar paquetes:
inserte la descripción de la imagen aquí

  • Si el cliente envía un segmento SYN y el enrutador reenvía un error ICMP, se considera un error suave (soft error), y el kernel del host del cliente guarda el mensaje y continúa enviándolo de acuerdo con el intervalo de tiempo.Si no se recibe respuesta después de un período de tiempo (75 s), es un error ICMP (EHOSTUNREACH o ENETUNREACH) devuelto al cliente.

2) función de enlace

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr * myaddr, socklen_t addrlen);//返回0成功,-1出错
  • El cliente generalmente no llama a bing, es decir, 使用通配地址(INADDR_ANY)se 端口0(内核自行分配端口)selecciona con la dirección IP y el puerto del kernel;
  • El servidor generalmente especifica el puerto y usa la dirección IPv4 通配地址(INADDR_ANY,其值一般为0). Si el servidor no vincula la dirección IP a su socket, el kernel usará la dirección IP de destino del SYN enviado por el cliente como la dirección IP de origen del servidor. Si es IPv6, consulte 4.4 Las secciones se procesan.

inserte la descripción de la imagen aquí

  • Si permite que el kernel elija un puerto efímero para el socket y desea obtener el puerto asignado, debe llamar a getsockname para devolver la dirección del protocolo. Por lo general, el puerto del servidor especificado debe estar en el rango de (5000, 49152), evitando el número de puerto temporal (generalmente mayor que 49152 o menor que 5000 P98)

  • Un error común devuelto por la función de vinculación es EADDRINUSE (la dirección ya está en uso). La solución es usar la opción de socket SO_REUSEADDR/SO_REUSEPORT, como se explica en la Sección 7.5.

Seguimiento para abrir fichas temáticas, aquí solo una guía de catálogo, enlace de seguimiento aquí.

3) función de escucha

#include <sys/socket.h>
int listen(int sockfd, int backlog);//成功返回0, 出错返回-1

4) aceptar función

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);//成功非负描述符,出错-1

3. Dificultades

Registros especiales de seguimiento, aquí solo hay enlaces de seguimiento.

Continuará...

Supongo que te gusta

Origin blog.csdn.net/nc_linux/article/details/125045976
Recomendado
Clasificación