(Notas de programación de red): programación SOCKET

Tabla de contenido

Programación SOCKET

Orden de bytes de la red

Función de conversión de dirección IP

Una estructura importante utilizada en la programación de sockets: struct sockaddr

Introducción a las principales funciones API de la programación de sockets

Pasos de la función de la API de socket para escribir programas de servidor y cliente

Proceso de desarrollo del servidor

Proceso de desarrollo del cliente

Ejemplo de programa de servidor

Ejemplo de programa cliente

Programación SOCKET

  • La comunicación tradicional entre procesos se lleva a cabo con la ayuda del mecanismo IPC proporcionado por el kernel, pero solo se puede limitar a la comunicación de la máquina local.Si desea comunicarse entre máquinas, debe utilizar la comunicación de red. (Esencialmente, mediante el kernel-kernel proporciona un mecanismo de pseudoarchivo de socket para lograr la comunicación, en realidad utilizando descriptores de archivo ), esto requiere el uso de la biblioteca de funciones de API de socket proporcionada por el kernel al usuario
  • Dado que se menciona el pseudoarchivo de socket, puede usar la función leer y escribir relacionada con el descriptor de archivo
  • El uso de socket creará un par de sockets
  • Como se muestra en la figura siguiente, un descriptor de archivo opera dos búferes , que es diferente de una tubería, que es un búfer de kernel con dos descriptores de archivo.

Orden de bytes de la red

  • El concepto de big endian y little endian
    • Big endian: la dirección de orden inferior almacena datos de orden superior, la dirección de orden superior almacena datos de orden inferior
    • Little endian: las direcciones de orden inferior almacenan datos de orden inferior, las direcciones de orden superior almacenan datos de orden superior
  • Utilice ocasiones de big-endian y little-endian:
    • Big-endian y little-endian solo tienen dos o más tipos de datos de longitud, como int short, no hay límite para un solo byte y, a menudo, es necesario considerar la IP y el puerto big-endian y little-endian en la red.
  • Big-endian se utiliza para la transmisión de red. Si la máquina utiliza little-endian, debe convertirse entre big-endian y big-endian.
  • Las siguientes cuatro funciones son las funciones para realizar la conversión de endian grandes y pequeños:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • La h en el nombre de la función significa host, n significa red, s significa corto, l significa largo
  • Las diversas funciones anteriores no se convertirán si no es necesario convertirlas dentro de la función .

Función de conversión de dirección IP

  • p-> representa la forma de cadena de notación decimal con puntos
  • a-> a
  • n-> significa red
int inet_pton(int af, const char *src, void *dst);
  • Descripción de la función : Convierta la IP decimal con puntos en forma de cadena a la IP de red en modo big-endian (forma 4 bytes)
  • Descripción de parámetros:
    • por: AF_INET
    • src: dirección IP decimal con puntos en forma de cadena
    • dst: almacena la dirección de la variable convertida
  • Por ejemplo: inet_pton (AF_INET, "127.0.0.1", & serv.sin_addr.s_addr);    
    • También se puede calcular manualmente: como 192.168.232.145, primero convierta los 4 números positivos en números hexadecimales, 
    • 192 ---> 0xC0 168 ---> 0xA8 232 ---> 0xE8 145 ---> 0x91
    • Finalmente, se almacena en orden de bytes big-endian: 0x91E8A8C0, este es el valor entero de 4 bytes.
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • Descripción de la función: convierte la IP de la red en una IP decimal con puntos en forma de cadena
  • Descripción de parámetros:
    • por: AF_INET
    • src: la dirección IP con forma de la red
    • dst: dirección IP convertida, generalmente una matriz de cadenas
    • tamaño: longitud de dst
  • valor de retorno: 
    • Éxito: devolver un puntero a dst
    • Failure-return NULL y establezca errno
  • Por ejemplo: la dirección IP es 010aa8c0, convertida a formato decimal con puntos:
    • 01 ----> 1 0a ----> 10 a8 ----> 168 c0 ----> 192
    • Dado que la dirección IP en la red esclava está en modo de gama alta, debe convertirse a decimal con puntos: 192.168.10.1

Una estructura importante utilizada en la programación de sockets: struct sockaddr

  • Descripción de la estructura struct sockaddr:
struct sockaddr {
        sa_family_t sa_family;
        char     sa_data[14];
}
  • struct sockaddr_in estructura:
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
    uint32_t  s_addr;     /* address in network byte order */
};	 //网络字节序IP--大端模式
  • Puede ver las instrucciones relacionadas a través de man 7 ip

Introducción a las principales funciones API de la programación de sockets

int socket(int domain, int type, int protocol);
  • Descripción de la función : Crear enchufe
  • Descripción de parámetros:
    • dominio: versión del protocolo
      • AF_INET IPV4
      • AF_INET6 IPV6
      • AF_UNIX AF_LOCAL uso de socket local
    • tipo: tipo de protocolo
      • Transmisión SOCK_STREAM, el protocolo predeterminado utilizado es el protocolo TCP
      • Formato de informe SOCK_DGRAM, el protocolo UDP se utiliza de forma predeterminada
    • protocal: 
      • Por lo general, complete 0, lo que significa utilizar el protocolo predeterminado del tipo correspondiente.
  • valor de retorno: 
    • Éxito: devuelve un descriptor de archivo mayor que 0
    • Fallo: devuelve -1 y establece errno
  • Cuando se llama a la función de socket, se devuelve un descriptor de archivo y el kernel proporcionará los búferes de lectura y escritura correspondientes al descriptor de archivo. Al mismo tiempo, hay dos colas, a saber, la cola de conexión de solicitud y la cola conectada

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • Descripción de la función: vincular el descriptor de archivo de socket con IP y PORT
  • Descripción de parámetros:
    • socket: el descriptor de archivo devuelto al llamar a la función socket
    • addr : la dirección IP y el PUERTO del servidor local, 
struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
//serv.sin_addr.s_addr = htonl(INADDR_ANY);
//INADDR_ANY: 表示使用本机任意有效的可用IP

inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
  • addrlen: el tamaño de la memoria de la variable addr 
  • valor de retorno: 
    • Éxito: devuelve 0
    • Fallo: devuelve -1 y establece errno
int listen(int sockfd, int backlog);
  • Descripción de la función: cambie el zócalo de la dinámica principal a la dinámica
  • Descripción de parámetros:
    • sockfd: el descriptor de archivo devuelto al llamar a la función socket
    • Backlog: el número máximo de solicitudes simultáneas (conexiones aún no establecidas) 
  • valor de retorno:
    • Éxito: devuelve 0
    • Fallo: devuelve -1 y establece errno                
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);    
  • Descripción de la función: Obtenga una conexión, si no hay conexión actualmente, bloqueará la espera.
  • Parámetros de función:
    • sockfd: el descriptor de archivo devuelto al llamar a la función socket
    • addr : parámetros salientes, guarde la información de la dirección del cliente
    • addrlen : parámetros entrantes y salientes , el espacio de memoria ocupado por las variables addr
  • valor de retorno:
    • Éxito: devuelve un nuevo descriptor de archivo para comunicarse con el cliente
    • Error: devuelva -1 y establezca el valor errno.
  • La función de aceptación es una función de bloqueo, si no hay una nueva solicitud de conexión, siempre se bloqueará.
  • Obtenga una nueva conexión de la cola conectada y obtenga un nuevo descriptor de archivo, que se utiliza para comunicarse con el cliente (el kernel será responsable de obtener la conexión en la cola de solicitudes a la cola conectada)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • Descripción de la función: conectar servidor
  • Parámetros de función:
    • sockfd: el descriptor de archivo devuelto al llamar a la función socket
    • addr: información de la dirección del servidor
    • addrlen: el tamaño de la memoria de la variable addr
  • valor de retorno:
    • Éxito: devuelve 0
    • Error: devuelve -1 y establece el valor de errno
  • Luego, puede usar las funciones de escritura y lectura para leer y escribir operaciones.
  • Además de usar funciones de lectura / escritura, también puede usar funciones de envío y recepción
  • Leer datos y enviar datos:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);    
  • En correspondencia con los dos indicadores de función de recv y enviar, solo complete 0 directamente .
  • Nota : Si el búfer de escritura está lleno, la escritura también se bloqueará. Durante una operación de lectura, si no hay datos en el búfer de lectura, se producirá un bloqueo.

Pasos de la función de la API de socket para escribir programas de servidor y cliente

Proceso de desarrollo del servidor

  • Cree un socket y devuelva un descriptor de archivo lfd --- socket (): el descriptor de archivo se usa para monitorear las conexiones del cliente
  • Enlazar lfd y puerto IP ---- bind ()
  • Cambiar lfd de monitoreo activo a pasivo ---- listen ()
  • Acepte una nueva conexión, obtenga un descriptor de archivo cfd ---- accept () --- El descriptor de archivo se usa para comunicarse con el cliente
  • mientras (1)
    • {
    •     Recibir datos --- leer o recibir
    •      Enviar datos --- escribir o enviar
    •  }
  • Cierre el archivo descriptor-close (lfd) close (cfd);

Proceso de desarrollo del cliente

  • Crear un socket, devolver un descriptor de archivo cfd --- socket () - el descriptor de archivo se usa para comunicarse con el servidor
  • Conéctese al servidor --- connect () 
  • mientras (1)
    •  {
    •         // Enviar datos --- escribir o enviar
    •         // Recibir datos --- leer o recibir
    • }
  • cerrar (cfd)

Ejemplo de programa de servidor

//服务端程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>

int main()
{
	//创建socket
	//int socket(int domain, int type, int protocol);
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	if(lfd<0)
	{
		perror("socket error");
		return -1;
	}
	
	//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	//绑定
	struct sockaddr_in serv;
	bzero(&serv, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	serv.sin_addr.s_addr = htonl(INADDR_ANY); //表示使用本地任意可用IP
	int ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
	if(ret<0)
	{
		perror("bind error");	
		return -1;
	}

	//监听
	//int listen(int sockfd, int backlog);
	listen(lfd, 128);

	//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	struct sockaddr_in client;
	socklen_t len = sizeof(client);
	int cfd = accept(lfd, (struct sockaddr *)&client, &len);  //len是一个输入输出参数
	//const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
	
	//获取client端的IP和端口
	char sIP[16];
	memset(sIP, 0x00, sizeof(sIP));
	printf("client-->IP:[%s],PORT:[%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));
	printf("lfd==[%d], cfd==[%d]\n", lfd, cfd);

	int i = 0;
	int n = 0;
	char buf[1024];

	while(1)
	{
		//读数据
		memset(buf, 0x00, sizeof(buf));
		n = read(cfd, buf, sizeof(buf));
		if(n<=0)
		{
			printf("read error or client close, n==[%d]\n", n);
			break;
		}
		printf("n==[%d], buf==[%s]\n", n, buf);	

		for(i=0; i<n; i++)
		{
			buf[i] = toupper(buf[i]);
		}

		//发送数据
		write(cfd, buf, n);
	}

	//关闭监听文件描述符和通信文件描述符
	close(lfd);
	close(cfd);
	
	return 0;
}
  • Prueba (usando la herramienta de prueba nc) :
nc ip 端口号

Ejemplo de programa cliente

//客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
	//创建socket---用于和服务端进行通信
	int cfd = socket(AF_INET, SOCK_STREAM, 0);
	if(cfd<0)
	{
		perror("socket error");
		return -1;
	}

	//连接服务端
	//int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	struct sockaddr_in serv;
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
	printf("[%x]\n", serv.sin_addr.s_addr);
	int ret = connect(cfd, (struct sockaddr *)&serv, sizeof(serv));
	if(ret<0)
	{
		perror("connect error");
		return -1;
	}	

	int n = 0;
	char buf[256];
	while(1)
	{
		//读标准输入数据
		memset(buf, 0x00, sizeof(buf));
		n = read(STDIN_FILENO, buf, sizeof(buf));
		
		//发送数据
		write(cfd, buf, n);

		//读服务端发来的数据
		memset(buf, 0x00, sizeof(buf));
		n = read(cfd, buf, sizeof(buf));
		if(n<=0)
		{
			printf("read error or server closed, n==[%d]\n", n);
			break;
		}
		printf("n==[%d], buf==[%s]\n", n, buf);
	}

	//关闭套接字cfd
	close(cfd);

	return 0;
}
  • Prueba (usando netstat):

  • [Nota]: consulte el tutorial de dark horse linux C ++

Supongo que te gusta

Origin blog.csdn.net/baidu_41388533/article/details/108943887
Recomendado
Clasificación